File indexing completed on 2022-11-29 20:25:14

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