File indexing completed on 2022-09-27 16:33:52

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