File indexing completed on 2024-05-05 17:04:25

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2014 Dan Leinir Turthra Jensen <admin@leinir.dk>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Library General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #include "DocumentListModel.h"
0021 
0022 #include <QDebug>
0023 #include <QDirIterator>
0024 #include <QRunnable>
0025 #include <QThreadPool>
0026 #include <QTimer>
0027 #include <QLocale>
0028 
0029 #include <KSharedConfig>
0030 #include <KConfigGroup>
0031 
0032 /*#include <QSparqlConnection>
0033 #include <QSparqlResult>
0034 #include <QSparqlError>*/
0035 #include <kfileitem.h>
0036 
0037 
0038 QDebug operator<<(QDebug dbg, const DocumentListModel::DocumentInfo& d) { 
0039     dbg.nospace() << d.filePath << "," << d.fileName << "," << d.docType << "," << d.fileSize << "," << d.authorName << "," << d.accessedTime << "," << d.modifiedTime << "," << d.uuid;
0040     return dbg.space();
0041 };
0042 
0043 const QString SearchThread::textDocumentType = QString("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#PaginatedTextDocument");
0044 const QString SearchThread::presentationType = QString("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#PresentationDocument");
0045 const QString SearchThread::spreadsheetType = QString("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#SpreadsheetDocument");
0046 
0047 SearchThread::SearchThread(const QHash<QString, DocumentListModel::DocumentType> &docTypes, QObject *parent) 
0048     : QObject(parent), m_abort(false), m_docTypes(docTypes)
0049 {
0050 }
0051 
0052 SearchThread::~SearchThread()
0053 {
0054 }
0055 
0056 void SearchThread::run()
0057 {
0058 // Keeping this code around, in case Tracker later blows up horribly and we
0059 // have to rapidly reenable the filesystem only support
0060     // Get documents from the device storage's document directory...
0061     QString documentsDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
0062     QStringList nameFilters;
0063     for (QHash<QString, DocumentListModel::DocumentType>::const_iterator it = m_docTypes.constBegin(); it != m_docTypes.constEnd(); ++it)
0064         nameFilters.append("*." + it.key());
0065 
0066     QDirIterator it(documentsDir, nameFilters, QDir::Files, QDirIterator::Subdirectories);
0067     while (it.hasNext() && !m_abort) {
0068         it.next();
0069         DocumentListModel::DocumentInfo info;
0070         info.fileName = it.fileName();
0071         info.authorName = "-";
0072         info.filePath = it.filePath();
0073         info.modifiedTime = it.fileInfo().lastModified();
0074         info.accessedTime = it.fileInfo().lastRead();
0075         info.fileSize = QString("%1").arg(it.fileInfo().size());
0076         info.docType = m_docTypes.value(info.filePath.split('.').last());
0077         info.uuid = "not known...";
0078         emit documentFound(info);
0079     }
0080 
0081     emit finished();
0082 }
0083 
0084 DocumentListModel::DocumentListModel(QObject *parent)
0085     : QAbstractListModel(parent)
0086     , m_searchThread(0)
0087     , m_groupBy(GroupByName)
0088     , m_filter(UnknownType)
0089 {
0090     qRegisterMetaType<DocumentInfo>();
0091 
0092     QHash<int, QByteArray> roleNames = QAbstractListModel::roleNames();
0093     roleNames[FileNameRole] = "fileName";
0094     roleNames[FilePathRole] = "filePath";
0095     roleNames[DocTypeRole] = "docType";
0096     roleNames[SectionCategoryRole] = "sectionCategory";
0097     roleNames[FileSizeRole] = "fileSize";
0098     roleNames[AuthorNameRole] = "authorName";
0099     roleNames[AccessedTimeRole] = "accessedTime";
0100     roleNames[ModifiedTimeRole] = "modifiedTime";
0101     roleNames[UUIDRole] = "uuid";
0102     setRoleNames(roleNames);
0103 
0104     m_docTypes["odt"] = TextDocumentType;
0105     m_docTypes["doc"] = TextDocumentType;
0106     m_docTypes["docx"] = TextDocumentType;
0107     m_docTypes["odp"] = PresentationType;
0108     m_docTypes["ppt"] = PresentationType;
0109     m_docTypes["pptx"] = PresentationType;
0110     m_docTypes["ods"] = SpreadsheetType;
0111     m_docTypes["xls"] = SpreadsheetType;
0112     m_docTypes["xlsx"] = SpreadsheetType;
0113 }
0114 
0115 DocumentListModel::~DocumentListModel()
0116 {
0117     stopSearch();
0118 }
0119 
0120 void DocumentListModel::startSearch()
0121 {
0122     if (m_searchThread) {
0123         qDebug() << "Already searching or finished search";
0124         return;
0125     }
0126     m_searchThread = new SearchThread(m_docTypes);
0127     connect(m_searchThread, SIGNAL(documentFound(DocumentListModel::DocumentInfo)), this, SLOT(addDocument(DocumentListModel::DocumentInfo)));
0128     connect(m_searchThread, SIGNAL(finished()), this, SLOT(searchFinished()));
0129     m_searchThread->setAutoDelete(false);
0130     QThreadPool::globalInstance()->start(m_searchThread);
0131 }
0132 
0133 void DocumentListModel::stopSearch()
0134 {
0135     if (m_searchThread)
0136         m_searchThread->abort();
0137 }
0138 
0139 void DocumentListModel::searchFinished()
0140 {
0141     Q_ASSERT(m_searchThread);
0142     delete m_searchThread;
0143     m_searchThread = 0;
0144 }
0145 
0146 void DocumentListModel::addDocument(const DocumentInfo &info)
0147 {
0148     if(m_allDocumentInfos.contains(info))
0149     {
0150         qDebug() << "Attempted to add duplicate entry" << info;
0151         return;
0152     }
0153 
0154     m_allDocumentInfos.append(info);
0155 
0156     if(m_filter == UnknownType || info.docType == m_filter) {
0157         beginInsertRows(QModelIndex(), m_currentDocumentInfos.count(), m_currentDocumentInfos.count());
0158         m_currentDocumentInfos.append(info);
0159         endInsertRows();
0160     }
0161 }
0162 
0163 int DocumentListModel::rowCount(const QModelIndex &parent) const
0164 {
0165     if(parent.isValid())
0166         return 0;
0167     return m_currentDocumentInfos.count();
0168 }
0169 
0170 int DocumentListModel::columnCount(const QModelIndex &parent) const
0171 {
0172     if (parent.isValid())
0173         return 0;
0174     return 1;
0175 }
0176 
0177 QVariant DocumentListModel::data(const QModelIndex &index, int role) const
0178 {
0179     if (!index.isValid())
0180         return QVariant();
0181     const int row = index.row();
0182     const DocumentInfo &info = m_currentDocumentInfos[row];
0183 
0184     switch (role) {
0185     case FileNameRole: // intentional fall through
0186     case Qt::DisplayRole: return info.fileName;
0187     case FilePathRole: return info.filePath;
0188     case DocTypeRole: return info.docType;
0189     case FileSizeRole: return info.fileSize;
0190     case AuthorNameRole: return info.authorName;
0191     case AccessedTimeRole: return prettyTime(info.accessedTime);
0192     case ModifiedTimeRole: return prettyTime(info.modifiedTime);
0193     case UUIDRole: return info.uuid;
0194     case SectionCategoryRole: 
0195         return m_groupBy == GroupByName ? info.fileName[0].toUpper() : info.docType;
0196     default: return QVariant();
0197     }
0198 }
0199 
0200 QString DocumentListModel::prettyTime(QDateTime theTime)
0201 {
0202     // QT5TODO: used to be KLocale::FancyShortDate, but no such thing with QLocale (also static anyway)
0203     return QLocale().toString(theTime, QLocale::ShortFormat);
0204 }
0205 
0206 QVariant DocumentListModel::headerData(int section, Qt::Orientation orientation, int role) const
0207 {
0208     if (orientation == Qt::Vertical || role != Qt::DisplayRole)
0209         return QVariant();
0210     switch (section) {
0211     case 0: return tr("Filename");
0212     case 1: return tr("Path");
0213     case 2: return tr("Type");
0214     case 3: return tr("Size");
0215     case 4: return tr("Author");
0216     case 5: return tr("Last Accessed");
0217     case 6: return tr("Last Modified");
0218     default: return QVariant();
0219     }
0220 }
0221 
0222 void DocumentListModel::groupBy(GroupBy role)
0223 {
0224     if (m_groupBy == role)
0225         return;
0226     m_groupBy = role;
0227     relayout();
0228 }
0229 
0230 void DocumentListModel::relayout()
0231 {
0232     beginResetModel();
0233     emit layoutAboutToBeChanged();
0234 
0235     QList<DocumentInfo> newList;
0236     foreach(const DocumentInfo &docInfo, m_allDocumentInfos) {
0237         if(m_filter == UnknownType || docInfo.docType == m_filter) {
0238             qDebug() << docInfo.filePath;
0239             newList.append(docInfo);
0240         }
0241     }
0242     
0243     m_currentDocumentInfos = newList;
0244     emit layoutChanged();
0245     endResetModel();
0246 }
0247 
0248 void DocumentListModel::classBegin()
0249 {
0250 }
0251 
0252 DocumentListModel::DocumentType DocumentListModel::filter()
0253 {
0254     return m_filter;
0255 }
0256 
0257 QString DocumentListModel::documentsFolder() const
0258 {
0259     return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
0260 }
0261 
0262 void DocumentListModel::setFilter(DocumentListModel::DocumentType newFilter)
0263 {
0264     m_filter = newFilter;
0265     relayout();
0266     emit filterChanged();
0267 }
0268 
0269 void DocumentListModel::componentComplete()
0270 {
0271     reset();
0272     startSearch();
0273 }
0274