diff --git a/src/contentlist/BalooContentLister.cpp b/src/contentlist/BalooContentLister.cpp index 98345b4..b69b0a3 100644 --- a/src/contentlist/BalooContentLister.cpp +++ b/src/contentlist/BalooContentLister.cpp @@ -1,163 +1,174 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "BalooContentLister.h" #include #include #include #include #include #include #include #include #include #include class BalooContentLister::Private { public: Private() {} - QList locations; + QStringList knownFiles; + QStringList locations; QString searchString; QList queries; }; BalooContentLister::BalooContentLister(QObject* parent) : ContentListerBase(parent) , d(new Private) { } BalooContentLister::~BalooContentLister() { delete d; } bool BalooContentLister::balooEnabled() const { Baloo::IndexerConfig config; bool result = config.fileIndexingEnabled(); if(result) { // It would be terribly nice with a bit of baloo engine exporting, so // we can ask the database about whether or not it is accessible... // But, this is a catch-all check anyway, so we get a complete "everything's broken" // result if anything is broken... guess it will do :) QProcess statuscheck; statuscheck.start("balooctl", QStringList() << "status"); statuscheck.waitForFinished(); QString output = statuscheck.readAll(); qDebug() << "Baloo status check says:" << output; if(statuscheck.exitStatus() == QProcess::CrashExit || statuscheck.exitCode() != 0) { result = false; } } return result; } void BalooContentLister::addLocation(QString path) { d->locations.append(path); } void BalooContentLister::addMimetype(QString mimetype) { Q_UNUSED(mimetype) // yes, unsatisfactory... we're using this to find comic books for now, and their mimetypes are terrible } void BalooContentLister::setSearchString(const QString& searchString) { d->searchString = searchString; } +void BalooContentLister::setKnownFiles(QStringList knownFiles) +{ + d->knownFiles = knownFiles; +} + void BalooContentLister::startSearch() { Q_FOREACH(const QString& location, d->locations) { Baloo::Query query; query.setSearchString(d->searchString); query.setIncludeFolder(location); Baloo::QueryRunnable *runnable = new Baloo::QueryRunnable(query); connect(runnable, SIGNAL(queryResult(Baloo::QueryRunnable*, QString)), this, SLOT(queryResult(Baloo::QueryRunnable*, QString)), Qt::QueuedConnection); connect(runnable, SIGNAL(finished(Baloo::QueryRunnable*)), this, SLOT(queryCompleted(Baloo::QueryRunnable*))); d->queries.append(runnable); } // This ensures that, should we decide to search more stuff later, we can do so granularly d->locations.clear(); if(!d->queries.empty()) { QThreadPool::globalInstance()->start(d->queries.first()); } } void BalooContentLister::queryCompleted(Baloo::QueryRunnable* query) { d->queries.removeAll(query); if(d->queries.empty()) { emit searchCompleted(); } else { QThreadPool::globalInstance()->start(d->queries.first()); } } void BalooContentLister::queryResult(Baloo::QueryRunnable* query, QString file) { Q_UNUSED(query) + + if(d->knownFiles.contains(file)) { + return; + } + QVariantHash metadata; Baloo::File balooFile(file); balooFile.load(); KFileMetaData::PropertyMap properties = balooFile.properties(); KFileMetaData::PropertyMap::const_iterator it = properties.constBegin(); for (; it != properties.constEnd(); it++) { KFileMetaData::PropertyInfo propInfo(it.key()); metadata[propInfo.name()] = it.value(); // qDebug() << KFileMetaData::PropertyInfo(it.key()).name() << " --> " // << it.value().toString() << " (" << it.value().typeName() << ")\n"; } QFileInfo info(file); metadata["lastModified"] = info.lastModified(); metadata["created"] = info.created(); metadata["lastRead"] = info.lastRead(); KFileMetaData::UserMetaData data(file); int currentPage = data.attribute("peruse.currentPage").toInt(); metadata["currentPage"] = QVariant::fromValue(currentPage); int totalPages = data.attribute("peruse.totalPages").toInt(); metadata["totalPages"] = QVariant::fromValue(totalPages); emit fileFound(file, metadata); } diff --git a/src/contentlist/BalooContentLister.h b/src/contentlist/BalooContentLister.h index 792ce00..3d35026 100644 --- a/src/contentlist/BalooContentLister.h +++ b/src/contentlist/BalooContentLister.h @@ -1,52 +1,53 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef BALOOCONTENTLISTER_H #define BALOOCONTENTLISTER_H #include "ContentListerBase.h" #include #include class BalooContentLister : public ContentListerBase { Q_OBJECT public: explicit BalooContentLister(QObject* parent = 0); virtual ~BalooContentLister(); bool balooEnabled() const; virtual void addLocation(QString path); virtual void addMimetype(QString mimetype); virtual void setSearchString(const QString& searchString); + virtual void setKnownFiles(QStringList knownFiles); virtual void startSearch(); private: class Private; Private* d; Q_SLOT void queryCompleted(Baloo::QueryRunnable* query); Q_SLOT void queryResult(Baloo::QueryRunnable* query, QString file); }; #endif//BALOOCONTENTLISTER_H diff --git a/src/contentlist/ContentList.cpp b/src/contentlist/ContentList.cpp index bb510eb..c73793d 100644 --- a/src/contentlist/ContentList.cpp +++ b/src/contentlist/ContentList.cpp @@ -1,150 +1,155 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "ContentList.h" #include "FilesystemContentLister.h" #ifdef BALOO_FOUND #include "BalooContentLister.h" #endif #include #include #include struct ContentEntry { QString filename; QVariantHash metadata; }; class ContentList::Private { public: Private() : actualContentList(0) {} QList entries; ContentListerBase* actualContentList; }; ContentList::ContentList(QObject* parent) : QAbstractListModel(parent) , d(new Private) { #ifdef BALOO_FOUND BalooContentLister* baloo = new BalooContentLister(this); if(baloo->balooEnabled()) { d->actualContentList = baloo; qDebug() << "Baloo support enabled"; } else { baloo->deleteLater(); d->actualContentList = new FilesystemContentLister(this); qDebug() << "Baloo is disabled for the system, use the filesystem scraper"; } #else d->actualContentList = new FilesystemContentLister(this); #endif connect(d->actualContentList, SIGNAL(fileFound(QString,QVariantHash)), this, SLOT(fileFound(QString,QVariantHash))); connect(d->actualContentList, SIGNAL(searchCompleted()), this, SIGNAL(searchCompleted())); } ContentList::~ContentList() { delete d; } QString ContentList::getMimetype(QString filePath) { QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(filePath); return mime.name(); } void ContentList::addLocation(QString path) { d->actualContentList->addLocation(path); } void ContentList::addMimetype(QString mimetype) { d->actualContentList->addMimetype(mimetype); } void ContentList::setSearchString(const QString& searchString) { d->actualContentList->setSearchString(searchString); } void ContentList::startSearch() { QTimer::singleShot(1, d->actualContentList, SLOT(startSearch())); } +void ContentList::setKnownFiles(QStringList knownFiles) +{ + d->actualContentList->setKnownFiles(knownFiles); +} + void ContentList::fileFound(const QString& filePath, const QVariantHash& metadata) { ContentEntry* entry = new ContentEntry(); entry->filename = filePath; entry->metadata = metadata; int newRow = d->entries.count(); beginInsertRows(QModelIndex(), newRow, newRow); d->entries.append(entry); endInsertRows(); } QHash ContentList::roleNames() const { QHash roles; roles[FilenameRole] = "filename"; roles[MetadataRole] = "metadata"; return roles; } QVariant ContentList::data(const QModelIndex& index, int role) const { QVariant result; if(index.isValid() && index.row() > -1 && index.row() < d->entries.count()) { const ContentEntry* entry = d->entries[index.row()]; switch(role) { case FilenameRole: result.setValue(entry->filename); break; case MetadataRole: result.setValue(entry->metadata); break; default: result.setValue(QString("Unknown role")); break; } } return result; } int ContentList::rowCount(const QModelIndex& parent) const { if(parent.isValid()) return 0; return d->entries.count(); } diff --git a/src/contentlist/ContentList.h b/src/contentlist/ContentList.h index f64d70d..dfe0a24 100644 --- a/src/contentlist/ContentList.h +++ b/src/contentlist/ContentList.h @@ -1,57 +1,58 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef CONTENTLISTBASE_H #define CONTENTLISTBASE_H #include class ContentList : public QAbstractListModel { Q_OBJECT public: explicit ContentList(QObject* parent = 0); virtual ~ContentList(); Q_INVOKABLE static QString getMimetype(QString filePath); Q_SLOT void addLocation(QString path); Q_SLOT void addMimetype(QString mimetype); Q_SLOT void setSearchString(const QString& searchString); + Q_SLOT void setKnownFiles(QStringList knownFiles); Q_SLOT void startSearch(); enum Roles { FilenameRole = Qt::UserRole + 1, MetadataRole }; virtual QHash roleNames() const; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; Q_SLOT void fileFound(const QString& filePath, const QVariantHash& metaData); Q_SIGNAL void searchCompleted(); private: class Private; Private* d; }; #endif//CONTENTLISTBASE_H diff --git a/src/contentlist/ContentListerBase.cpp b/src/contentlist/ContentListerBase.cpp index 11637a2..18b7d28 100644 --- a/src/contentlist/ContentListerBase.cpp +++ b/src/contentlist/ContentListerBase.cpp @@ -1,51 +1,56 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "ContentListerBase.h" ContentListerBase::ContentListerBase(QObject* parent) : QObject(parent) { } ContentListerBase::~ContentListerBase() { } void ContentListerBase::addLocation(QString path) { Q_UNUSED(path) } void ContentListerBase::addMimetype(QString mimetype) { Q_UNUSED(mimetype) } void ContentListerBase::setSearchString(const QString& searchString) { Q_UNUSED(searchString) } +void ContentListerBase::setKnownFiles(QStringList knownFiles) +{ + Q_UNUSED(knownFiles); +} + void ContentListerBase::startSearch() { } diff --git a/src/contentlist/ContentListerBase.h b/src/contentlist/ContentListerBase.h index 7977c7d..ffacbdb 100644 --- a/src/contentlist/ContentListerBase.h +++ b/src/contentlist/ContentListerBase.h @@ -1,43 +1,44 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef CONTENTLISTERBASE_H #define CONTENTLISTERBASE_H #include class ContentListerBase : public QObject { Q_OBJECT public: explicit ContentListerBase(QObject* parent = 0); virtual ~ContentListerBase(); virtual void addLocation(QString path); virtual void addMimetype(QString mimetype); virtual void setSearchString(const QString& searchString); + virtual void setKnownFiles(QStringList knownFiles); Q_SLOT virtual void startSearch(); Q_SIGNAL void fileFound(const QString& filePath, const QVariantHash& metadata); Q_SIGNAL void searchCompleted(); }; #endif//CONTENTLISTERBASE_H diff --git a/src/contentlist/FilesystemContentLister.cpp b/src/contentlist/FilesystemContentLister.cpp index 990b95f..f6a3f4e 100644 --- a/src/contentlist/FilesystemContentLister.cpp +++ b/src/contentlist/FilesystemContentLister.cpp @@ -1,127 +1,137 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "FilesystemContentLister.h" #include #include #include #include #include #include #include #include class FilesystemContentLister::Private { public: Private() {} QString searchString; + QStringList knownFiles; QStringList locations; QStringList mimetypes; }; FilesystemContentLister::FilesystemContentLister(QObject* parent) : ContentListerBase(parent) , d(new Private) { } FilesystemContentLister::~FilesystemContentLister() { delete d; } void FilesystemContentLister::addLocation(QString path) { d->locations.append(path); } void FilesystemContentLister::addMimetype(QString mimetype) { d->mimetypes.append(mimetype); } void FilesystemContentLister::setSearchString(const QString& searchString) { d->searchString = searchString; } +void FilesystemContentLister::setKnownFiles(QStringList knownFiles) +{ + d->knownFiles = knownFiles; +} + void FilesystemContentLister::startSearch() { QMimeDatabase mimeDb; bool useThis(false); qDebug() << "Searching in" << d->locations; Q_FOREACH(const QString& folder, d->locations) { QDirIterator it(folder, QDirIterator::Subdirectories); while (it.hasNext()) { QString filePath = it.next(); + if(d->knownFiles.contains(filePath)) { + continue; + } + QFileInfo info(filePath); if(info.isDir()) { qApp->processEvents(); continue; } useThis = false; QString mimetype = mimeDb.mimeTypeForFile(filePath, QMimeDatabase::MatchExtension).name(); // qDebug() << useThis << mimetype << filePath; Q_FOREACH(const QString& type, d->mimetypes) { if(type == mimetype) { useThis = true; break; } } if(useThis) { QVariantHash metadata; metadata["created"] = info.created(); KFileMetaData::UserMetaData data(filePath); if (data.hasAttribute("peruse.currentPage")) { int currentPage = data.attribute("peruse.currentPage").toInt(); metadata["currentPage"] = QVariant::fromValue(currentPage); } if (data.hasAttribute("peruse.totalPages")) { int totalPages = data.attribute("peruse.totalPages").toInt(); metadata["totalPages"] = QVariant::fromValue(totalPages); } emit fileFound(filePath, metadata); } qApp->processEvents(); } } // This ensures that, should we decide to search more stuff later, we can do so granularly d->locations.clear(); // Not entirely happy about this, but it makes things not break... // Previously, the welcome page in Peruse would end up unpopulated because a signal // was unreceived from the main window upon search completion (and consequently // application readiness) QTimer::singleShot(0, this, SIGNAL(searchCompleted())); } diff --git a/src/contentlist/FilesystemContentLister.h b/src/contentlist/FilesystemContentLister.h index 00a3f9e..d35cff0 100644 --- a/src/contentlist/FilesystemContentLister.h +++ b/src/contentlist/FilesystemContentLister.h @@ -1,43 +1,44 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef FILESYSTEMCONTENTLISTER_H #define FILESYSTEMCONTENTLISTER_H #include "ContentListerBase.h" class FilesystemContentLister : public ContentListerBase { Q_OBJECT public: explicit FilesystemContentLister(QObject* parent = 0); virtual ~FilesystemContentLister(); virtual void addLocation(QString path); virtual void addMimetype(QString mimetype); virtual void setSearchString(const QString& searchString); + virtual void setKnownFiles(QStringList knownFiles); virtual void startSearch(); private: class Private; Private* d; }; #endif//FILESYSTEMCONTENTLISTER_H diff --git a/src/qtquick/BookListModel.cpp b/src/qtquick/BookListModel.cpp index a853f75..1c8f4cb 100644 --- a/src/qtquick/BookListModel.cpp +++ b/src/qtquick/BookListModel.cpp @@ -1,266 +1,297 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "BookListModel.h" +#include "BookDatabase.h" #include "CategoryEntriesModel.h" #include #include #include #include #include #include class BookListModel::Private { public: Private() : contentModel(0) , titleCategoryModel(0) , newlyAddedCategoryModel(0) , authorCategoryModel(0) , seriesCategoryModel(0) , folderCategoryModel(0) - {}; + { + db = new BookDatabase(); + }; ~Private() { qDeleteAll(entries); + db->deleteLater(); } QList entries; QAbstractListModel* contentModel; CategoryEntriesModel* titleCategoryModel; CategoryEntriesModel* newlyAddedCategoryModel; CategoryEntriesModel* authorCategoryModel; CategoryEntriesModel* seriesCategoryModel; CategoryEntriesModel* folderCategoryModel; + + BookDatabase* db; + + void initializeSubModels(BookListModel* q) { + if(!titleCategoryModel) + { + titleCategoryModel = new CategoryEntriesModel(q); + connect(q, SIGNAL(entryDataUpdated(BookEntry*)), titleCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); + connect(q, SIGNAL(entryRemoved(BookEntry*)), titleCategoryModel, SIGNAL(entryRemoved(BookEntry*))); + emit q->titleCategoryModelChanged(); + } + if(!newlyAddedCategoryModel) + { + newlyAddedCategoryModel = new CategoryEntriesModel(q); + connect(q, SIGNAL(entryDataUpdated(BookEntry*)), newlyAddedCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); + connect(q, SIGNAL(entryRemoved(BookEntry*)), newlyAddedCategoryModel, SIGNAL(entryRemoved(BookEntry*))); + emit q->newlyAddedCategoryModelChanged(); + } + if(!authorCategoryModel) + { + authorCategoryModel = new CategoryEntriesModel(q); + connect(q, SIGNAL(entryDataUpdated(BookEntry*)), authorCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); + connect(q, SIGNAL(entryRemoved(BookEntry*)), authorCategoryModel, SIGNAL(entryRemoved(BookEntry*))); + emit q->authorCategoryModelChanged(); + } + if(!seriesCategoryModel) + { + seriesCategoryModel = new CategoryEntriesModel(q); + connect(q, SIGNAL(entryDataUpdated(BookEntry*)), seriesCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); + connect(q, SIGNAL(entryRemoved(BookEntry*)), seriesCategoryModel, SIGNAL(entryRemoved(BookEntry*))); + emit q->seriesCategoryModelChanged(); + } + if(!folderCategoryModel) + { + folderCategoryModel = new CategoryEntriesModel(q); + connect(q, SIGNAL(entryDataUpdated(BookEntry*)), folderCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); + connect(q, SIGNAL(entryRemoved(BookEntry*)), folderCategoryModel, SIGNAL(entryRemoved(BookEntry*))); + emit q->folderCategoryModel(); + } + } + + void addEntry(BookListModel* q, BookEntry* entry) { + entries.append(entry); + q->append(entry); + titleCategoryModel->addCategoryEntry(entry->title.left(1).toUpper(), entry); + authorCategoryModel->addCategoryEntry(entry->author, entry); + seriesCategoryModel->addCategoryEntry(entry->series, entry); + newlyAddedCategoryModel->append(entry, CreatedRole); + QUrl url(entry->filename.left(entry->filename.lastIndexOf("/"))); + folderCategoryModel->addCategoryEntry(url.path().mid(1), entry); + folderCategoryModel->append(entry); + } }; BookListModel::BookListModel(QObject* parent) : CategoryEntriesModel(parent) , d(new Private) { + QList entries = d->db->loadEntries(); + if(entries.count() > 0) + { + d->initializeSubModels(this); + } + foreach(BookEntry* entry, entries) + { + d->addEntry(this, entry); + } } BookListModel::~BookListModel() { delete d; } void BookListModel::setContentModel(QObject* newModel) { if(d->contentModel) { d->contentModel->disconnect(this); } d->contentModel = qobject_cast(newModel); if(d->contentModel) { connect(d->contentModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(contentModelItemsInserted(QModelIndex,int, int))); } emit contentModelChanged(); } QObject * BookListModel::contentModel() const { return d->contentModel; } void BookListModel::contentModelItemsInserted(QModelIndex index, int first, int last) { - if(!d->titleCategoryModel) - { - d->titleCategoryModel = new CategoryEntriesModel(this); - connect(this, SIGNAL(entryDataUpdated(BookEntry*)), d->titleCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); - connect(this, SIGNAL(entryRemoved(BookEntry*)), d->titleCategoryModel, SIGNAL(entryRemoved(BookEntry*))); - emit titleCategoryModelChanged(); - } - if(!d->newlyAddedCategoryModel) - { - d->newlyAddedCategoryModel = new CategoryEntriesModel(this); - connect(this, SIGNAL(entryDataUpdated(BookEntry*)), d->newlyAddedCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); - connect(this, SIGNAL(entryRemoved(BookEntry*)), d->newlyAddedCategoryModel, SIGNAL(entryRemoved(BookEntry*))); - emit newlyAddedCategoryModelChanged(); - } - if(!d->authorCategoryModel) - { - d->authorCategoryModel = new CategoryEntriesModel(this); - connect(this, SIGNAL(entryDataUpdated(BookEntry*)), d->authorCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); - connect(this, SIGNAL(entryRemoved(BookEntry*)), d->authorCategoryModel, SIGNAL(entryRemoved(BookEntry*))); - emit authorCategoryModelChanged(); - } - if(!d->seriesCategoryModel) - { - d->seriesCategoryModel = new CategoryEntriesModel(this); - connect(this, SIGNAL(entryDataUpdated(BookEntry*)), d->seriesCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); - connect(this, SIGNAL(entryRemoved(BookEntry*)), d->seriesCategoryModel, SIGNAL(entryRemoved(BookEntry*))); - emit seriesCategoryModelChanged(); - } - if(!d->folderCategoryModel) - { - d->folderCategoryModel = new CategoryEntriesModel(this); - connect(this, SIGNAL(entryDataUpdated(BookEntry*)), d->folderCategoryModel, SIGNAL(entryDataUpdated(BookEntry*))); - connect(this, SIGNAL(entryRemoved(BookEntry*)), d->folderCategoryModel, SIGNAL(entryRemoved(BookEntry*))); - emit folderCategoryModel(); - } - + d->initializeSubModels(this); int newRow = d->entries.count(); beginInsertRows(QModelIndex(), newRow, newRow + (last - first)); for(int i = first; i < last + 1; ++i) { QVariant filename = d->contentModel->data(d->contentModel->index(first, 0, index), Qt::UserRole + 1); BookEntry* entry = new BookEntry(); entry->filename = filename.toString(); QStringList splitName = entry->filename.split("/"); if (!splitName.isEmpty()) entry->filetitle = splitName.takeLast(); if(!splitName.isEmpty()) entry->series = splitName.takeLast(); // hahahaheuristics (dumb assumptions about filesystems, go!) // just in case we end up without a title... using complete basename here, // as we would rather have "book one. part two" and the odd "book one - part two.tar" QFileInfo fileinfo(entry->filename); entry->title = fileinfo.completeBaseName(); if(entry->filename.toLower().endsWith("cbr")) { entry->thumbnail = QString("image://comiccover/").append(entry->filename); } #ifdef USE_PERUSE_PDFTHUMBNAILER else if(entry->filename.toLower().endsWith("pdf")) { entry->thumbnail = QString("image://pdfcover/").append(entry->filename); } #endif else { entry->thumbnail = QString("image://preview/").append(entry->filename); } QVariantHash metadata = d->contentModel->data(d->contentModel->index(first, 0, index), Qt::UserRole + 2).toHash(); QVariantHash::const_iterator it = metadata.constBegin(); for (; it != metadata.constEnd(); it++) { if(it.key() == QLatin1String("author")) { entry->author = it.value().toString().trimmed(); } else if(it.key() == QLatin1String("title")) { entry->title = it.value().toString().trimmed(); } else if(it.key() == QLatin1String("publisher")) { entry->publisher = it.value().toString().trimmed(); } else if(it.key() == QLatin1String("created")) { entry->created = it.value().toDateTime(); } else if(it.key() == QLatin1String("currentPage")) { entry->currentPage = it.value().toInt(); } else if(it.key() == QLatin1String("totalPages")) { entry->totalPages = it.value().toInt(); } } - d->entries.append(entry); - - append(entry); - d->titleCategoryModel->addCategoryEntry(entry->title.left(1).toUpper(), entry); - d->authorCategoryModel->addCategoryEntry(entry->author, entry); - d->seriesCategoryModel->addCategoryEntry(entry->series, entry); - d->newlyAddedCategoryModel->append(entry, CreatedRole); - QUrl url(entry->filename.left(entry->filename.lastIndexOf("/"))); - d->folderCategoryModel->addCategoryEntry(url.path().mid(1), entry); - d->folderCategoryModel->append(entry); + d->addEntry(this, entry); + d->db->addEntry(entry); } endInsertRows(); emit countChanged(); qApp->processEvents(); } QObject * BookListModel::titleCategoryModel() const { return d->titleCategoryModel; } QObject * BookListModel::newlyAddedCategoryModel() const { return d->newlyAddedCategoryModel; } QObject * BookListModel::authorCategoryModel() const { return d->authorCategoryModel; } QObject * BookListModel::seriesCategoryModel() const { return d->seriesCategoryModel; } QObject * BookListModel::seriesModelForEntry(QString fileName) { Q_FOREACH(BookEntry* entry, d->entries) { if(entry->filename == fileName) { return d->seriesCategoryModel->leafModelForEntry(entry); } } return 0; } QObject * BookListModel::folderCategoryModel() const { return d->folderCategoryModel; } int BookListModel::count() const { return d->entries.count(); } void BookListModel::setBookData(QString fileName, QString property, QString value) { Q_FOREACH(BookEntry* entry, d->entries) { if(entry->filename == fileName) { if(property == "totalPages") { entry->totalPages = value.toInt(); } else if(property == "currentPage") { entry->currentPage = value.toInt(); } emit entryDataUpdated(entry); break; } } } void BookListModel::removeBook(QString fileName, bool deleteFile) { if(deleteFile) { KIO::DeleteJob* job = KIO::del(QUrl::fromLocalFile(fileName), KIO::HideProgressInfo); job->start(); } Q_FOREACH(BookEntry* entry, d->entries) { if(entry->filename == fileName) { emit entryRemoved(entry); delete entry; break; } } } + +QStringList BookListModel::knownBookFiles() const +{ + QStringList files; + foreach(BookEntry* entry, d->entries) { + files.append(entry->filename); + } + return files; +} diff --git a/src/qtquick/BookListModel.h b/src/qtquick/BookListModel.h index e97f4fc..0c7f236 100644 --- a/src/qtquick/BookListModel.h +++ b/src/qtquick/BookListModel.h @@ -1,89 +1,92 @@ /* * Copyright (C) 2015 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 6 of version 3 of the license. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef BOOKLISTMODEL_H #define BOOKLISTMODEL_H #include "CategoryEntriesModel.h" class BookListModel : public CategoryEntriesModel { Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(QObject* contentModel READ contentModel WRITE setContentModel NOTIFY contentModelChanged) Q_PROPERTY(QObject* newlyAddedCategoryModel READ newlyAddedCategoryModel NOTIFY newlyAddedCategoryModelChanged) Q_PROPERTY(QObject* titleCategoryModel READ titleCategoryModel NOTIFY titleCategoryModelChanged) Q_PROPERTY(QObject* authorCategoryModel READ authorCategoryModel NOTIFY authorCategoryModelChanged) Q_PROPERTY(QObject* seriesCategoryModel READ seriesCategoryModel NOTIFY seriesCategoryModelChanged) Q_PROPERTY(QObject* folderCategoryModel READ folderCategoryModel NOTIFY folderCategoryModelChanged) Q_ENUMS(Grouping) public: explicit BookListModel(QObject* parent = 0); virtual ~BookListModel(); enum Grouping { GroupByNone = 0, GroupByRecentlyAdded, GroupByRecentlyRead, GroupByTitle, GroupByAuthor, GroupByPublisher }; QObject* contentModel() const; void setContentModel(QObject* newModel); Q_SIGNAL void contentModelChanged(); int count() const; Q_SIGNAL void countChanged(); QObject* titleCategoryModel() const; Q_SIGNAL void titleCategoryModelChanged(); QObject* newlyAddedCategoryModel() const; Q_SIGNAL void newlyAddedCategoryModelChanged(); QObject* authorCategoryModel() const; Q_SIGNAL void authorCategoryModelChanged(); QObject* seriesCategoryModel() const; Q_SIGNAL void seriesCategoryModelChanged(); // Returns the leaf model representing the series the entry with the passed URL is a part of // Base assumption: A book is only part of one series. This is not always true, but not sure how // to sensibly represent that Q_INVOKABLE QObject* seriesModelForEntry(QString fileName); QObject* folderCategoryModel() const; Q_SIGNAL void folderCategoryModelChanged(); // Update the data of a book at runtime - in particular, we need to update totalPages and currentPage Q_INVOKABLE void setBookData(QString fileName, QString property, QString value); // Delete a book from the model, and optionally delete the entry from file storage Q_INVOKABLE void removeBook(QString fileName, bool deleteFile = false); + + // A list of the files currently known by the applications + Q_INVOKABLE QStringList knownBookFiles() const; private: class Private; Private* d; Q_SLOT void contentModelItemsInserted(QModelIndex index,int first, int last); }; #endif//BOOKLISTMODEL_H diff --git a/src/qtquick/CMakeLists.txt b/src/qtquick/CMakeLists.txt index 2820a27..d1c7b69 100644 --- a/src/qtquick/CMakeLists.txt +++ b/src/qtquick/CMakeLists.txt @@ -1,58 +1,60 @@ add_subdirectory(karchive-rar) set(USE_PERUSE_PDFTHUMBNAILER false) if(WIN32) set(USE_PERUSE_PDFTHUMBNAILER true) endif() set(qmlplugin_SRCS qmlplugin.cpp ArchiveBookModel.cpp ArchiveImageProvider.cpp + BookDatabase.cpp BookModel.cpp BookListModel.cpp CategoryEntriesModel.cpp ComicCoverImageProvider.cpp FilterProxy.cpp FolderBookModel.cpp PeruseConfig.cpp PreviewImageProvider.cpp PropertyContainer.cpp ) if(USE_PERUSE_PDFTHUMBNAILER) set(qmlplugin_SRCS ${qmlplugin_SRCS} PDFCoverImageProvider.cpp ) endif() add_library (peruseqmlplugin SHARED ${qmlplugin_SRCS}) if(USE_PERUSE_PDFTHUMBNAILER) target_compile_definitions(peruseqmlplugin PRIVATE -DUSE_PERUSE_PDFTHUMBNAILER ) endif() target_include_directories(peruseqmlplugin PRIVATE karchive-rar acbf ) target_link_libraries (peruseqmlplugin acbf karchive-rar Qt5::Core Qt5::Qml Qt5::Quick + Qt5::Sql KF5::Archive KF5::IconThemes KF5::ConfigCore KF5::KIOCore KF5::KIOWidgets KF5::FileMetaData ) install (TARGETS peruseqmlplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/peruse) install (FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/peruse) install (FILES peruse.knsrc DESTINATION ${CONFIG_INSTALL_DIR})