File indexing completed on 2024-04-14 05:44:31
0001 // SPDX-License-Identifier: GPL-2.0-or-later 0002 // SPDX-FileCopyrightText: 2007 Dominik Seichter <domseichter@web.de> 0003 0004 #include "krenamemodel.h" 0005 #include "threadedlister.h" 0006 0007 #include <random> 0008 0009 #include <QMimeData> 0010 #include <QPixmap> 0011 #include <QApplication> 0012 0013 #include <KLocalizedString> 0014 #include <krun.h> 0015 #include <kio/previewjob.h> 0016 0017 KRenameModel::KRenameModel(KRenameFile::List *vector) 0018 : QAbstractListModel(), 0019 m_vector(vector), 0020 m_preview(false), 0021 m_text(false), 0022 m_maxDots(0), 0023 m_mimeType("text/uri-list"), 0024 m_eSortMode(eSortMode_Unsorted), 0025 m_customSortToken("creationdate;yyyyMMddHHmm"), 0026 m_eCustomSortMode(KRenameTokenSorter::eSimpleSortMode_Ascending) 0027 { 0028 0029 } 0030 0031 KRenameModel::~KRenameModel() 0032 { 0033 0034 } 0035 0036 int KRenameModel::rowCount(const QModelIndex &index) const 0037 { 0038 if (!index.isValid()) { 0039 return m_vector->size(); 0040 } 0041 0042 return 0; 0043 } 0044 0045 QVariant KRenameModel::data(const QModelIndex &index, int role) const 0046 { 0047 if (!index.isValid()) { 0048 return QVariant(); 0049 } 0050 0051 if (index.row() >= m_vector->size()) { 0052 return QVariant(); 0053 } 0054 0055 if (role == Qt::DisplayRole) { 0056 if (!m_preview) { 0057 // Only return path 0058 return m_vector->at(index.row()).toString(); 0059 } else if (m_preview && m_text) { 0060 // Short filename as first line in bold 0061 // Path as second line 0062 const KRenameFile &file = m_vector->at(index.row()); 0063 QString filename = file.srcFilename(); 0064 if (!file.srcExtension().isEmpty()) { 0065 filename = filename + '.' + file.srcExtension(); 0066 } 0067 0068 const QString &prettyUrl = file.toString(); 0069 return "<qt><b>" + filename + "</b><br/>" + 0070 prettyUrl + "</qt>"; 0071 } 0072 } else if (role == Qt::DecorationRole && m_preview) { 0073 return m_vector->at(index.row()).icon(); 0074 } else if (role == Qt::UserRole) { 0075 return m_vector->at(index.row()).toString(); 0076 } 0077 0078 return QVariant(); 0079 } 0080 0081 Qt::ItemFlags KRenameModel::flags(const QModelIndex &index) const 0082 { 0083 if (!index.isValid()) { 0084 return Qt::ItemIsDropEnabled; 0085 } 0086 0087 return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDropEnabled; 0088 } 0089 0090 Qt::DropActions KRenameModel::supportedDropActions() const 0091 { 0092 return Qt::CopyAction | Qt::MoveAction; 0093 } 0094 0095 QStringList KRenameModel::mimeTypes() const 0096 { 0097 QStringList types; 0098 types << m_mimeType; 0099 return types; 0100 } 0101 0102 bool KRenameModel::dropMimeData(const QMimeData *data, 0103 Qt::DropAction action, 0104 int, int, 0105 const QModelIndex &) 0106 { 0107 if (action == Qt::IgnoreAction) { 0108 return true; 0109 } 0110 0111 if (!data->hasFormat(m_mimeType)) { 0112 return false; 0113 } 0114 0115 QList<QUrl> dirs; 0116 KRenameFile::List files; 0117 QList<QUrl> urls = data->urls(); 0118 QList<QUrl>::const_iterator it = urls.constBegin(); 0119 0120 QApplication::setOverrideCursor(Qt::BusyCursor); 0121 0122 while (it != urls.constEnd()) { 0123 if ((*it).isValid()) { 0124 KRenameFile file(*it, m_eSplitMode, m_dot); 0125 0126 if (file.isValid() && !file.isDirectory()) { 0127 files.append(file); 0128 } else if (file.isValid() && file.isDirectory()) 0129 // Add directories recursively 0130 { 0131 dirs.append(*it); 0132 } 0133 } 0134 0135 ++it; 0136 } 0137 0138 this->addFiles(files); 0139 if (dirs.count()) { 0140 QList<QUrl>::const_iterator it = dirs.constBegin(); 0141 0142 while (it != dirs.constEnd()) { 0143 ThreadedLister *thl = new ThreadedLister(*it, nullptr, this); 0144 connect(thl, &ThreadedLister::listerDone, 0145 this, &KRenameModel::slotListerDone); 0146 0147 thl->setListDirnamesOnly(false); 0148 thl->setListHidden(false); 0149 thl->setListRecursively(true); 0150 thl->setListDirnames(false); 0151 0152 thl->start(); 0153 0154 ++it; 0155 } 0156 } else { 0157 QApplication::restoreOverrideCursor(); 0158 Q_EMIT filesDropped(); 0159 } 0160 0161 return true; 0162 } 0163 0164 void KRenameModel::slotListerDone(ThreadedLister *lister) 0165 { 0166 // Delete the listener 0167 delete lister; 0168 0169 // restore cursor 0170 QApplication::restoreOverrideCursor(); 0171 0172 Q_EMIT filesDropped(); 0173 } 0174 0175 bool KRenameModel::setData(const QModelIndex &index, 0176 const QVariant &, int role) 0177 { 0178 if (index.isValid() && role == Qt::EditRole) { 0179 0180 //stringList.replace(index.row(), value.toString()); 0181 Q_EMIT dataChanged(index, index); 0182 return true; 0183 } 0184 0185 return false; 0186 } 0187 0188 void KRenameModel::addFiles(const KRenameFile::List &files) 0189 { 0190 if (files.count()) { 0191 int oldMaxDots = m_maxDots; 0192 m_vector->reserve(m_vector->count() + files.count()); 0193 0194 this->beginInsertRows(QModelIndex(), m_vector->size(), m_vector->size() + files.count() - 1); 0195 0196 KRenameFile::List::const_iterator it = files.begin(); 0197 while (it != files.end()) { 0198 m_vector->push_back(*it); 0199 0200 int dots = (*it).dots(); 0201 if (dots > m_maxDots) { 0202 m_maxDots = dots; 0203 } 0204 0205 ++it; 0206 } 0207 this->endInsertRows(); 0208 0209 if (m_maxDots > oldMaxDots) { 0210 Q_EMIT maxDotsChanged(m_maxDots); 0211 } 0212 0213 // Update sorting 0214 this->sortFiles(m_eSortMode, m_customSortToken, m_eCustomSortMode); 0215 0216 // Generate previews if necessary 0217 if (m_preview) { 0218 // Construct a list of KFileItems 0219 // Only do this is necessary, 0220 // as this might create new KFileItems which is slow. 0221 KFileItemList fileItems; 0222 it = files.begin(); 0223 while (it != files.end()) { 0224 fileItems << (*it).fileItem(); 0225 0226 ++it; 0227 } 0228 0229 // TODO: Enable this job, it currently crashes for me 0230 0231 // Start a job to create the real file previews 0232 KIO::PreviewJob *job = KIO::filePreview(fileItems, QSize(KRenameFile::iconSize(), KRenameFile::iconSize())); 0233 0234 connect(job, &KIO::PreviewJob::gotPreview, 0235 this, &KRenameModel::gotPreview); 0236 job->start(); 0237 } 0238 } 0239 } 0240 0241 void KRenameModel::gotPreview(const KFileItem &item, const QPixmap &preview) 0242 { 0243 /* 0244 const KRenameFile* file = 0245 static_cast<const KRenameFile*>(item.extraData(KRenameFile::EXTRA_DATA_KEY)); 0246 */ 0247 0248 KRenameFile *file = nullptr; 0249 // TODO: Find a more optimal "search algorithm" .... 0250 KRenameFile::List::iterator it = m_vector->begin(); 0251 while (it != m_vector->end()) { 0252 if ((*it).srcUrl() == item.url()) { 0253 file = &(*it); 0254 break; 0255 } 0256 0257 ++it; 0258 } 0259 0260 //it = find( m_vector->begin(), m_vector->end(), item ); 0261 if (file != nullptr) { // && file->fileItem() == item ) 0262 file->setIcon(preview); 0263 } 0264 } 0265 0266 void KRenameModel::removeFiles(const QList<int> &remove) 0267 { 0268 int offset = 0; 0269 0270 QList<int> copy(remove); 0271 std::sort(copy.begin(), copy.end()); 0272 0273 QList<int>::const_iterator it = copy.constBegin(); 0274 this->beginRemoveRows(QModelIndex(), *it, copy.back()); 0275 while (it != copy.constEnd()) { 0276 m_vector->erase(m_vector->begin() + *it - offset); 0277 0278 ++offset; 0279 ++it; 0280 } 0281 0282 this->endRemoveRows(); 0283 } 0284 0285 void KRenameModel::sortFiles(ESortMode mode, const QString &customSortToken, KRenameTokenSorter::ESimpleSortMode customSortMode) 0286 { 0287 beginResetModel(); 0288 const QString dateSortToken = "creationdate;yyyyMMddHHmm"; 0289 0290 m_eSortMode = mode; 0291 m_customSortToken = customSortToken; 0292 m_eCustomSortMode = customSortMode; 0293 0294 if (mode == eSortMode_Ascending) { 0295 std::sort(m_vector->begin(), m_vector->end(), ascendingKRenameFileLessThan); 0296 } else if (mode == eSortMode_Descending) { 0297 std::sort(m_vector->begin(), m_vector->end(), descendingKRenameFileLessThan); 0298 } else if (mode == eSortMode_Numeric) { 0299 std::sort(m_vector->begin(), m_vector->end(), numericKRenameFileLessThan); 0300 } else if (mode == eSortMode_Random) { 0301 std::random_device rd; 0302 std::shuffle(m_vector->begin(), m_vector->end(), std::mt19937(rd())); 0303 } else if (mode == eSortMode_AscendingDate) { 0304 KRenameTokenSorter sorter(m_renamer, dateSortToken, *m_vector, 0305 KRenameTokenSorter::eSimpleSortMode_Ascending); 0306 std::sort(m_vector->begin(), m_vector->end(), sorter); 0307 } else if (mode == eSortMode_DescendingDate) { 0308 KRenameTokenSorter sorter(m_renamer, dateSortToken, *m_vector, 0309 KRenameTokenSorter::eSimpleSortMode_Descending); 0310 std::sort(m_vector->begin(), m_vector->end(), sorter); 0311 } else if (mode == eSortMode_Token) { 0312 KRenameTokenSorter sorter(m_renamer, customSortToken, *m_vector, 0313 customSortMode); 0314 std::sort(m_vector->begin(), m_vector->end(), sorter); 0315 } 0316 0317 endResetModel(); 0318 } 0319 0320 void KRenameModel::run(const QModelIndex &index, QWidget *window) const 0321 { 0322 KRenameFile file = m_vector->at(index.row()); 0323 new KRun(file.srcUrl(), window); 0324 } 0325 0326 const QModelIndex KRenameModel::createIndex(int row) const 0327 { 0328 return QAbstractItemModel::createIndex(row, 0); 0329 } 0330 0331 void KRenameModel::moveFilesUp(const QList<int> &files) 0332 { 0333 int index; 0334 KRenameFile tmp; 0335 0336 QList<int> copy(files); 0337 std::sort(copy.begin(), copy.end()); 0338 0339 beginResetModel(); 0340 QList<int>::const_iterator it = copy.constBegin(); 0341 while (it != copy.constEnd()) { 0342 index = *it; 0343 if (index <= 0) { // cannot swap top item 0344 ++it; 0345 continue; 0346 } 0347 0348 // swap items 0349 tmp = m_vector->at(index); 0350 (*m_vector)[index] = KRenameFile(m_vector->at(index - 1)); 0351 (*m_vector)[index - 1] = tmp; 0352 0353 ++it; 0354 } 0355 0356 endResetModel(); 0357 } 0358 0359 void KRenameModel::moveFilesDown(const QList<int> &files) 0360 { 0361 int index; 0362 KRenameFile tmp; 0363 0364 QList<int> copy(files); 0365 // sort the list in reverse order 0366 std::sort(copy.begin(), copy.end(), std::greater<int>()); 0367 0368 beginResetModel(); 0369 QList<int>::const_iterator it = copy.constBegin(); 0370 while (it != copy.constEnd()) { 0371 index = *it; 0372 if (index + 1 >= m_vector->size()) { // cannot swap bottom item 0373 ++it; 0374 continue; 0375 } 0376 0377 // swap items 0378 tmp = m_vector->at(index); 0379 (*m_vector)[index] = KRenameFile(m_vector->at(index + 1)); 0380 (*m_vector)[index + 1] = tmp; 0381 0382 ++it; 0383 } 0384 0385 endResetModel(); 0386 } 0387 0388 ////////////////////////////////////////////////////////////// 0389 // Preview model starts below 0390 ////////////////////////////////////////////////////////////// 0391 KRenamePreviewModel::KRenamePreviewModel(KRenameFile::List *vector) 0392 : m_vector(vector) 0393 { 0394 0395 } 0396 0397 KRenamePreviewModel::~KRenamePreviewModel() 0398 { 0399 0400 } 0401 0402 int KRenamePreviewModel::rowCount(const QModelIndex &parent) const 0403 { 0404 if (!parent.isValid()) { 0405 return m_vector->size(); 0406 } 0407 0408 return 0; 0409 } 0410 0411 int KRenamePreviewModel::columnCount(const QModelIndex &) const 0412 { 0413 return 2; 0414 } 0415 0416 QVariant KRenamePreviewModel::headerData(int section, Qt::Orientation orientation, int role) const 0417 { 0418 if (orientation != Qt::Horizontal || section > 1 || role != Qt::DisplayRole) { 0419 return QVariant(); 0420 } 0421 0422 return (section == 0) ? i18n("Origin") : i18n("Renamed"); 0423 } 0424 0425 QVariant KRenamePreviewModel::data(const QModelIndex &index, int role) const 0426 { 0427 if (!index.isValid()) { 0428 return QVariant(); 0429 } 0430 0431 if (index.row() >= m_vector->size()) { 0432 return QVariant(); 0433 } 0434 0435 if (index.column() >= 2) { 0436 return QVariant(); 0437 } 0438 0439 if (role == Qt::DisplayRole) { 0440 const KRenameFile &file = m_vector->at(index.row()); 0441 QString filename; 0442 QString extension; 0443 QString manual; 0444 0445 if (index.column()) { 0446 manual = file.manualChanges(); 0447 if (manual.isNull()) { 0448 filename = file.dstFilename(); 0449 extension = file.dstExtension(); 0450 } else { 0451 filename = manual; 0452 } 0453 } else { 0454 filename = file.srcFilename(); 0455 extension = file.srcExtension(); 0456 } 0457 0458 if (!extension.isEmpty()) { 0459 filename += '.'; 0460 filename += extension; 0461 } 0462 0463 if (file.isDirectory()) { 0464 filename = (index.column() ? file.dstDirectory() : file.srcDirectory()) + '/' + filename; 0465 } 0466 0467 return filename; 0468 } else if (role == Qt::ForegroundRole) { 0469 const KRenameFile &file = m_vector->at(index.row()); 0470 if (!file.manualChanges().isNull()) { 0471 return QVariant(QColor(Qt::blue)); 0472 } 0473 } 0474 /* 0475 Icons are to large, so this is disabled 0476 else if( role == Qt::DecorationRole && index.column() == 0 ) 0477 { 0478 return m_vector->at(index.row()).icon(); 0479 } 0480 */ 0481 0482 return QVariant(); 0483 0484 } 0485 0486 QModelIndex KRenamePreviewModel::parent(const QModelIndex &) const 0487 { 0488 return QModelIndex(); 0489 } 0490 0491 QModelIndex KRenamePreviewModel::sibling(int, int, const QModelIndex &) const 0492 { 0493 return QModelIndex(); 0494 } 0495 0496 void KRenamePreviewModel::refresh() 0497 { 0498 beginResetModel(); 0499 endResetModel(); 0500 } 0501 0502 #include "moc_krenamemodel.cpp"