File indexing completed on 2024-05-26 05:54:55

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"