File indexing completed on 2024-12-01 07:38:39
0001 /*************************************************************************** 0002 * Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify * 0005 * it under the terms of the GNU General Public License as published by * 0006 * the Free Software Foundation; either version 2 of the License, or * 0007 * (at your option) any later version. * 0008 * * 0009 * This program 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 * 0012 * GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License * 0015 * along with this program; if not, write to the * 0016 * Free Software Foundation, Inc., * 0017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * 0018 ***************************************************************************/ 0019 0020 #include "filemodel.h" 0021 0022 #include "signature.h" 0023 #include "verifier.h" 0024 0025 #include <KIO/Global> 0026 #include <KLocalizedString> 0027 0028 FileItem::FileItem(const QString &name, FileItem *parent) 0029 : m_name(name) 0030 , m_state(Qt::Checked) 0031 , m_status(Job::Stopped) 0032 , m_totalSize(0) 0033 , m_checkusmVerified(0) 0034 , m_signatureVerified(0) 0035 , m_parent(parent) 0036 { 0037 } 0038 0039 FileItem::~FileItem() 0040 { 0041 qDeleteAll(m_childItems); 0042 } 0043 0044 void FileItem::appendChild(FileItem *child) 0045 { 0046 m_childItems.append(child); 0047 } 0048 0049 FileItem *FileItem::child(int row) 0050 { 0051 return m_childItems.value(row); 0052 } 0053 0054 int FileItem::childCount() const 0055 { 0056 return m_childItems.count(); 0057 } 0058 0059 bool FileItem::isFile() const 0060 { 0061 return m_childItems.isEmpty(); 0062 } 0063 0064 int FileItem::columnCount() const 0065 { 0066 return 5; 0067 } 0068 0069 QVariant FileItem::data(int column, int role) const 0070 { 0071 if (column == FileItem::File) { 0072 if (role == Qt::CheckStateRole) { 0073 return m_state; 0074 } else if (role == Qt::DisplayRole) { 0075 return m_name; 0076 } else if (role == Qt::DecorationRole) { 0077 if (m_mimeType.isNull()) { 0078 if (isFile()) { 0079 m_mimeType = QIcon::fromTheme(KIO::iconNameForUrl(QUrl(m_name))); 0080 } else { 0081 m_mimeType = QIcon::fromTheme("folder"); 0082 } 0083 } 0084 0085 return m_mimeType; 0086 } 0087 } else if (column == FileItem::Status) { 0088 if ((role == Qt::DisplayRole) || (role == Qt::DecorationRole)) { 0089 if (isFile()) { 0090 return m_status; 0091 } 0092 } 0093 } else if (column == FileItem::Size) { 0094 if (role == Qt::DisplayRole) { 0095 return KIO::convertSize(m_totalSize); 0096 } 0097 } else if (column == FileItem::ChecksumVerified) { 0098 if (role == Qt::DecorationRole) { 0099 switch (m_checkusmVerified) { 0100 case Verifier::Verified: 0101 return QIcon::fromTheme("dialog-ok"); 0102 case Verifier::NotVerified: 0103 return QIcon::fromTheme("dialog-error"); 0104 case Verifier::NoResult: 0105 default: 0106 return QIcon::fromTheme(QString()); 0107 } 0108 } 0109 } else if (column == FileItem::SignatureVerified) { // TODO implement all cases 0110 if (role == Qt::DecorationRole) { 0111 switch (m_signatureVerified) { 0112 case Signature::Verified: 0113 return QIcon::fromTheme("dialog-ok"); 0114 case Signature::VerifiedInformation: 0115 return QIcon::fromTheme("dialog-information"); 0116 case Signature::VerifiedWarning: 0117 return QIcon::fromTheme("dialog-warning"); 0118 case Signature::NotVerified: 0119 return QIcon::fromTheme("dialog-error"); 0120 case Signature::NoResult: 0121 default: 0122 return QIcon::fromTheme(QString()); 0123 } 0124 } 0125 } 0126 0127 return QVariant(); 0128 } 0129 0130 bool FileItem::setData(int column, const QVariant &value, FileModel *model, int role) 0131 { 0132 if (value.isNull()) { 0133 return false; 0134 } 0135 0136 if (column == FileItem::File) { 0137 if (role == Qt::CheckStateRole) { 0138 m_state = static_cast<Qt::CheckState>(value.toInt()); 0139 model->changeData(this->row(), column, this); 0140 checkParents(m_state, model); 0141 checkChildren(m_state, model); 0142 return true; 0143 } else if (role == Qt::EditRole) { 0144 m_name = value.toString(); 0145 model->changeData(this->row(), column, this); 0146 return true; 0147 } 0148 } else if (column == FileItem::Status) { 0149 if (role == Qt::EditRole) { 0150 if (isFile()) { 0151 m_status = static_cast<Job::Status>(value.toInt()); 0152 bool finished = (m_status == Job::Finished); 0153 model->changeData(this->row(), column, this, finished); 0154 0155 return true; 0156 } 0157 } 0158 } else if (column == FileItem::Size) { 0159 if (role == Qt::EditRole) { 0160 KIO::fileoffset_t newSize = value.toLongLong(); 0161 if (m_parent) { 0162 m_parent->addSize(newSize - m_totalSize, model); 0163 } 0164 m_totalSize = newSize; 0165 model->changeData(this->row(), column, this); 0166 return true; 0167 } 0168 } else if (column == FileItem::ChecksumVerified) { 0169 m_checkusmVerified = value.toInt(); 0170 model->changeData(this->row(), column, this); 0171 return true; 0172 } else if (column == FileItem::SignatureVerified) { 0173 m_signatureVerified = value.toInt(); 0174 model->changeData(this->row(), column, this); 0175 return true; 0176 } 0177 0178 return false; 0179 } 0180 0181 void FileItem::checkParents(Qt::CheckState state, FileModel *model) 0182 { 0183 if (!model) { 0184 return; 0185 } 0186 0187 if (!m_parent) { 0188 return; 0189 } 0190 0191 foreach (FileItem *child, m_parent->m_childItems) { 0192 if (child->m_state != state) { 0193 state = Qt::Unchecked; 0194 break; 0195 } 0196 } 0197 0198 m_parent->m_state = state; 0199 model->changeData(m_parent->row(), FileItem::File, m_parent); 0200 m_parent->checkParents(state, model); 0201 } 0202 0203 void FileItem::checkChildren(Qt::CheckState state, FileModel *model) 0204 { 0205 if (!model) { 0206 return; 0207 } 0208 0209 m_state = state; 0210 model->changeData(row(), FileItem::File, this); 0211 0212 foreach (FileItem *child, m_childItems) { 0213 child->checkChildren(state, model); 0214 } 0215 } 0216 0217 FileItem *FileItem::parent() 0218 { 0219 return m_parent; 0220 } 0221 0222 int FileItem::row() const 0223 { 0224 if (m_parent) { 0225 return m_parent->m_childItems.indexOf(const_cast<FileItem *>(this)); 0226 } 0227 0228 return 0; 0229 } 0230 0231 void FileItem::addSize(KIO::fileoffset_t size, FileModel *model) 0232 { 0233 if (!isFile()) { 0234 m_totalSize += size; 0235 model->changeData(this->row(), FileItem::Size, this); 0236 if (m_parent) { 0237 m_parent->addSize(size, model); 0238 } 0239 } 0240 } 0241 0242 FileModel::FileModel(const QList<QUrl> &files, const QUrl &destDirectory, QObject *parent) 0243 : QAbstractItemModel(parent) 0244 , m_destDirectory(destDirectory) 0245 , m_checkStateChanged(false) 0246 { 0247 m_rootItem = new FileItem("root"); 0248 m_header << i18nc("file in a filesystem", "File") << i18nc("status of the download", "Status") << i18nc("size of the download", "Size") 0249 << i18nc("checksum of a file", "Checksum") << i18nc("signature of a file", "Signature"); 0250 0251 setupModelData(files); 0252 } 0253 0254 FileModel::~FileModel() 0255 { 0256 delete m_rootItem; 0257 } 0258 0259 void FileModel::setupModelData(const QList<QUrl> &files) 0260 { 0261 QString destDirectory = m_destDirectory.toLocalFile(); 0262 0263 foreach (const QUrl &file, files) { 0264 FileItem *parent = m_rootItem; 0265 QStringList directories = file.toLocalFile().remove(destDirectory).split('/', Qt::SkipEmptyParts); 0266 FileItem *child = nullptr; 0267 while (directories.count()) { 0268 QString part = directories.takeFirst(); 0269 for (int i = 0; i < parent->childCount(); ++i) { 0270 // folder already exists 0271 if (parent->child(i)->data(0, Qt::DisplayRole).toString() == part) { 0272 parent = parent->child(i); 0273 // file already exists 0274 if (!directories.count()) { 0275 break; 0276 } 0277 part = directories.takeFirst(); 0278 i = -1; 0279 continue; 0280 } 0281 } 0282 child = new FileItem(part, parent); 0283 parent->appendChild(child); 0284 parent = parent->child(parent->childCount() - 1); 0285 } 0286 if (child) { 0287 m_files.append(child); 0288 } 0289 } 0290 } 0291 0292 int FileModel::columnCount(const QModelIndex &parent) const 0293 { 0294 if (parent.isValid()) { 0295 return static_cast<FileItem *>(parent.internalPointer())->columnCount(); 0296 } else { 0297 return m_rootItem->columnCount(); 0298 } 0299 } 0300 0301 QVariant FileModel::data(const QModelIndex &index, int role) const 0302 { 0303 if (!index.isValid()) { 0304 return QVariant(); 0305 } 0306 0307 auto *item = static_cast<FileItem *>(index.internalPointer()); 0308 const QVariant data = item->data(index.column(), role); 0309 0310 // get the status icon as well as status text 0311 if (index.column() == FileItem::Status) { 0312 const auto status = static_cast<Job::Status>(data.toInt()); 0313 if (item->isFile()) { 0314 if (role == Qt::DisplayRole) { 0315 if (m_customStatusTexts.contains(status)) { 0316 return m_customStatusTexts[status]; 0317 } else { 0318 return Transfer::statusText(status); 0319 } 0320 } else if (role == Qt::DecorationRole) { 0321 if (m_customStatusIcons.contains(status)) { 0322 return m_customStatusIcons[status]; 0323 } else { 0324 return QIcon::fromTheme(Transfer::statusIconName(status)); 0325 } 0326 } 0327 } else { 0328 return QVariant(); 0329 } 0330 } 0331 0332 return data; 0333 } 0334 0335 bool FileModel::setData(const QModelIndex &index, const QVariant &value, int role) 0336 { 0337 if (!index.isValid()) { 0338 return false; 0339 } 0340 0341 auto *item = static_cast<FileItem *>(index.internalPointer()); 0342 0343 if ((index.column() == FileItem::File) && (role == Qt::CheckStateRole)) { 0344 const bool worked = item->setData(index.column(), value, this, role); 0345 if (worked) { 0346 m_checkStateChanged = true; 0347 } 0348 0349 return worked; 0350 } 0351 0352 return item->setData(index.column(), value, this, role); 0353 } 0354 0355 Qt::ItemFlags FileModel::flags(const QModelIndex &index) const 0356 { 0357 if (!index.isValid()) { 0358 return Qt::NoItemFlags; 0359 } 0360 0361 if (index.column() == FileItem::File) { 0362 return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable; 0363 } 0364 0365 return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 0366 } 0367 0368 QVariant FileModel::headerData(int section, Qt::Orientation orientation, int role) const 0369 { 0370 if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole)) { 0371 return m_header.value(section); 0372 } 0373 0374 return QVariant(); 0375 } 0376 0377 QModelIndex FileModel::index(int row, int column, const QModelIndex &parent) const 0378 { 0379 if (!hasIndex(row, column, parent)) { 0380 return QModelIndex(); 0381 } 0382 0383 FileItem *parentItem; 0384 if (parent.isValid()) { 0385 parentItem = static_cast<FileItem *>(parent.internalPointer()); 0386 } else { 0387 parentItem = m_rootItem; 0388 } 0389 0390 FileItem *childItem = parentItem->child(row); 0391 if (childItem) { 0392 return createIndex(row, column, childItem); 0393 } else { 0394 return QModelIndex(); 0395 } 0396 } 0397 0398 QModelIndex FileModel::index(const QUrl &file, int column) 0399 { 0400 FileItem *item = getItem(file); 0401 if (!item) { 0402 return QModelIndex(); 0403 } 0404 0405 return createIndex(item->row(), column, item); 0406 } 0407 0408 QModelIndexList FileModel::fileIndexes(int column) const 0409 { 0410 QModelIndexList indexList; 0411 foreach (FileItem *item, m_files) { 0412 int row = item->row(); 0413 indexList.append(createIndex(row, column, item)); 0414 } 0415 0416 return indexList; 0417 } 0418 0419 QModelIndex FileModel::parent(const QModelIndex &index) const 0420 { 0421 if (!index.isValid()) { 0422 return QModelIndex(); 0423 } 0424 0425 auto *childItem = static_cast<FileItem *>(index.internalPointer()); 0426 FileItem *parentItem = childItem->parent(); 0427 if ((parentItem == m_rootItem) || (!parentItem)) { 0428 return QModelIndex(); 0429 } else { 0430 return createIndex(parentItem->row(), 0, parentItem); 0431 } 0432 } 0433 0434 int FileModel::rowCount(const QModelIndex &parent) const 0435 { 0436 if (parent.column() > 0) { 0437 return 0; 0438 } 0439 0440 FileItem *parentItem; 0441 if (parent.isValid()) { 0442 parentItem = static_cast<FileItem *>(parent.internalPointer()); 0443 } else { 0444 parentItem = m_rootItem; 0445 } 0446 0447 return parentItem->childCount(); 0448 } 0449 0450 void FileModel::changeData(int row, int column, FileItem *item, bool finished) 0451 { 0452 QModelIndex index = createIndex(row, column, item); 0453 Q_EMIT dataChanged(index, index); 0454 0455 if (finished) { 0456 const QUrl file = getUrl(index); 0457 Q_EMIT fileFinished(file); 0458 } 0459 } 0460 0461 void FileModel::setDirectory(const QUrl &newDirectory) 0462 { 0463 m_destDirectory = newDirectory; 0464 m_itemCache.clear(); 0465 } 0466 0467 QUrl FileModel::getUrl(const QModelIndex &index) 0468 { 0469 if (!index.isValid()) { 0470 return QUrl(); 0471 } 0472 0473 const QModelIndex file = index.sibling(index.row(), FileItem::File); 0474 0475 return getUrl(static_cast<FileItem *>(file.internalPointer())); 0476 } 0477 0478 QUrl FileModel::getUrl(FileItem *item) 0479 { 0480 const QString path = getPath(item); 0481 const QString name = item->data(FileItem::File, Qt::DisplayRole).toString(); 0482 QUrl url = m_destDirectory; 0483 url.setPath(m_destDirectory.path() + path + name); 0484 0485 return url; 0486 } 0487 0488 QString FileModel::getPath(FileItem *item) 0489 { 0490 FileItem *parent = item->parent(); 0491 QString path; 0492 while (parent && parent->parent()) { 0493 path = parent->data(FileItem::File, Qt::DisplayRole).toString() + '/' + path; 0494 parent = parent->parent(); 0495 } 0496 0497 return path; 0498 } 0499 0500 FileItem *FileModel::getItem(const QUrl &file) 0501 { 0502 if (m_itemCache.contains(file)) { 0503 return m_itemCache[file]; 0504 } 0505 0506 QString destDirectory = m_destDirectory.toLocalFile(); 0507 0508 FileItem *item = m_rootItem; 0509 QStringList directories = file.toLocalFile().remove(destDirectory).split('/', Qt::SkipEmptyParts); 0510 while (directories.count()) { 0511 QString part = directories.takeFirst(); 0512 for (int i = 0; i < item->childCount(); ++i) { 0513 // folder already exists 0514 if (item->child(i)->data(FileItem::File, Qt::DisplayRole).toString() == part) { 0515 item = item->child(i); 0516 // file already exists 0517 if (!directories.count()) { 0518 break; 0519 } 0520 part = directories.takeFirst(); 0521 i = -1; 0522 continue; 0523 } 0524 } 0525 } 0526 0527 if (item == m_rootItem) { 0528 item = nullptr; 0529 } else { 0530 m_itemCache[file] = item; 0531 } 0532 0533 return item; 0534 } 0535 0536 bool FileModel::downloadFinished(const QUrl &file) 0537 { 0538 FileItem *item = getItem(file); 0539 if (item) { 0540 const Job::Status status = static_cast<Job::Status>(item->data(FileItem::Status, Qt::DisplayRole).toInt()); 0541 if (status == Job::Finished) { 0542 return true; 0543 } 0544 } 0545 0546 return false; 0547 } 0548 0549 bool FileModel::isFile(const QModelIndex &index) const 0550 { 0551 if (!index.isValid()) { 0552 return false; 0553 } 0554 0555 auto *item = static_cast<FileItem *>(index.internalPointer()); 0556 0557 // only files can be renamed, no folders 0558 return item->isFile(); 0559 } 0560 0561 void FileModel::rename(const QModelIndex &file, const QString &newName) 0562 { 0563 if (!file.isValid() || (file.column() != FileItem::File)) { 0564 return; 0565 } 0566 0567 auto *item = static_cast<FileItem *>(file.internalPointer()); 0568 // only files can be renamed, no folders 0569 if (!item->isFile()) { 0570 return; 0571 } 0572 0573 // Find out the old and the new QUrl 0574 QString oldName = file.data(Qt::DisplayRole).toString(); 0575 QString path = getPath(item); 0576 0577 QUrl oldUrl = m_destDirectory; 0578 oldUrl.setPath(m_destDirectory.path() + path + oldName); 0579 QUrl newUrl = m_destDirectory; 0580 newUrl.setPath(m_destDirectory.path() + path + newName); 0581 0582 m_itemCache.remove(oldUrl); 0583 0584 setData(file, newName); 0585 0586 Q_EMIT rename(oldUrl, newUrl); 0587 } 0588 0589 void FileModel::renameFailed(const QUrl &beforeRename, const QUrl &afterRename) 0590 { 0591 Q_UNUSED(beforeRename) 0592 Q_UNUSED(afterRename) 0593 } 0594 0595 void FileModel::watchCheckState() 0596 { 0597 m_checkStateChanged = false; 0598 } 0599 0600 void FileModel::stopWatchCheckState() 0601 { 0602 if (m_checkStateChanged) { 0603 Q_EMIT checkStateChanged(); 0604 } 0605 0606 m_checkStateChanged = false; 0607 } 0608 0609 #include "moc_filemodel.cpp"