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