File indexing completed on 2025-03-09 03:58:52

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2008-11-21
0007  * Description : Batch Queue Manager items list.
0008  *
0009  * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText:      2014 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "queuelist.h"
0017 
0018 // Qt includes
0019 
0020 #include <QDragEnterEvent>
0021 #include <QFileInfo>
0022 #include <QHeaderView>
0023 #include <QPainter>
0024 #include <QTimer>
0025 #include <QUrl>
0026 #include <QDrag>
0027 #include <QMenu>
0028 #include <QAction>
0029 
0030 // KDE includes
0031 
0032 #if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU)
0033 #   pragma GCC diagnostic push
0034 #   pragma GCC diagnostic ignored "-Wdeprecated-declarations"
0035 #endif
0036 
0037 #if defined(Q_CC_CLANG)
0038 #   pragma clang diagnostic push
0039 #   pragma clang diagnostic ignored "-Wdeprecated-declarations"
0040 #endif
0041 
0042 #include <kactioncollection.h>
0043 #include <klocalizedstring.h>
0044 
0045 // Restore warnings
0046 #if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU)
0047 #   pragma GCC diagnostic pop
0048 #endif
0049 
0050 #if defined(Q_CC_CLANG)
0051 #   pragma clang diagnostic pop
0052 #endif
0053 
0054 // Local includes
0055 
0056 #include "digikam_debug.h"
0057 #include "coredb.h"
0058 #include "coredbchangesets.h"
0059 #include "coredbwatch.h"
0060 #include "ddragobjects.h"
0061 #include "defaultrenameparser.h"
0062 #include "queuemgrwindow.h"
0063 #include "queuesettings.h"
0064 #include "queuetooltip.h"
0065 #include "thumbnailloadthread.h"
0066 #include "thumbnailsize.h"
0067 #include "workflowmanager.h"
0068 #include "dlayoutbox.h"
0069 #include "dworkingpixmap.h"
0070 
0071 namespace Digikam
0072 {
0073 
0074 class Q_DECL_HIDDEN QueueListViewItem::Private
0075 {
0076 
0077 public:
0078 
0079     explicit Private()
0080       : isBusy       (false),
0081         done         (false),
0082         hasThumb     (false),
0083         progressIndex(0),
0084         view         (nullptr)
0085     {
0086     }
0087 
0088     bool           isBusy;
0089     bool           done;
0090     bool           hasThumb;
0091 
0092     int            progressIndex;
0093 
0094     QString        destFileName;
0095 
0096     QPixmap        preview;
0097 
0098     QueueListView* view;
0099 
0100     ItemInfo       info;
0101 };
0102 
0103 QueueListViewItem::QueueListViewItem(QueueListView* const view, const ItemInfo& info)
0104     : QTreeWidgetItem(view),
0105       d              (new Private)
0106 {
0107     d->view = view;
0108     setThumb(QIcon::fromTheme(QLatin1String("view-preview")).pixmap(48, QIcon::Disabled), false);
0109     setInfo(info);
0110 }
0111 
0112 QueueListViewItem::~QueueListViewItem()
0113 {
0114     delete d;
0115 }
0116 
0117 bool QueueListViewItem::hasValidThumbnail() const
0118 {
0119     return d->hasThumb;
0120 }
0121 
0122 void QueueListViewItem::setInfo(const ItemInfo& info)
0123 {
0124     d->info = info;
0125     setText(1, d->info.name());
0126 }
0127 
0128 ItemInfo QueueListViewItem::info() const
0129 {
0130     return d->info;
0131 }
0132 
0133 void QueueListViewItem::setPixmap(const QPixmap& pix)
0134 {
0135     QIcon icon = QIcon(pix);
0136 
0137     //  We make sure the preview icon stays the same regardless of the role
0138 
0139     icon.addPixmap(pix, QIcon::Selected, QIcon::On);
0140     icon.addPixmap(pix, QIcon::Selected, QIcon::Off);
0141     icon.addPixmap(pix, QIcon::Active,   QIcon::On);
0142     icon.addPixmap(pix, QIcon::Active,   QIcon::Off);
0143     icon.addPixmap(pix, QIcon::Normal,   QIcon::On);
0144     icon.addPixmap(pix, QIcon::Normal,   QIcon::Off);
0145     setIcon(0, icon);
0146 }
0147 
0148 void QueueListViewItem::setThumb(const QPixmap& pix, bool hasThumb)
0149 {
0150     QSize iSize = treeWidget()->iconSize();
0151     QPixmap pixmap(iSize.width() + 2, iSize.height() + 2);
0152     pixmap.fill(Qt::transparent);
0153     QPainter p(&pixmap);
0154     p.drawPixmap((pixmap.width() / 2) - (pix.width() / 2), (pixmap.height() / 2) - (pix.height() / 2), pix);
0155     d->preview  = pixmap;
0156     setPixmap(d->preview);
0157     d->hasThumb = hasThumb;
0158 }
0159 
0160 void QueueListViewItem::animProgress()
0161 {
0162     QPixmap icon(d->view->progressPixmapForIndex(d->progressIndex));
0163     d->progressIndex++;
0164 
0165     if (d->view->progressPixmapForIndex(d->progressIndex).isNull())
0166     {
0167         d->progressIndex = 0;
0168     }
0169 
0170     QPixmap preview = d->preview;
0171     QPixmap mask(preview.size());
0172     mask.fill(QColor(128, 128, 128, 192));
0173     QPainter p(&preview);
0174     p.drawPixmap(0, 0, mask);
0175     p.drawPixmap((preview.width() / 2) - (icon.width() / 2), (preview.height() / 2) - (icon.height() / 2), icon);
0176     setPixmap(preview);
0177 }
0178 
0179 void QueueListViewItem::setCanceled()
0180 {
0181     setPixmap(d->preview);
0182     setIcon(1, QIcon::fromTheme(QLatin1String("dialog-cancel")));
0183     d->done          = false;
0184     d->isBusy        = false;
0185     d->progressIndex = 0;
0186 }
0187 
0188 void QueueListViewItem::setFailed()
0189 {
0190     setPixmap(d->preview);
0191     setIcon(1, QIcon::fromTheme(QLatin1String("dialog-error")));
0192     d->done          = false;
0193     d->isBusy        = false;
0194     d->progressIndex = 0;
0195 }
0196 
0197 void QueueListViewItem::setDone()
0198 {
0199     setPixmap(d->preview);
0200     setIcon(1, QIcon::fromTheme(QLatin1String("dialog-ok-apply")));
0201     d->done          = true;
0202     d->isBusy        = false;
0203     d->progressIndex = 0;
0204 }
0205 
0206 bool QueueListViewItem::isDone() const
0207 {
0208     return d->done;
0209 }
0210 
0211 void QueueListViewItem::reset()
0212 {
0213     setPixmap(d->preview);
0214     setIcon(1, QIcon());
0215     d->done          = false;
0216     d->isBusy        = false;
0217     d->progressIndex = 0;
0218 }
0219 
0220 void QueueListViewItem::setBusy()
0221 {
0222     d->isBusy = true;
0223 }
0224 
0225 bool QueueListViewItem::isBusy() const
0226 {
0227     return d->isBusy;
0228 }
0229 
0230 void QueueListViewItem::setDestFileName(const QString& str)
0231 {
0232     d->destFileName = str;
0233     setText(2, d->destFileName);
0234 }
0235 
0236 QString QueueListViewItem::destFileName() const
0237 {
0238     return d->destFileName;
0239 }
0240 
0241 QString QueueListViewItem::destBaseName() const
0242 {
0243     QFileInfo fi(d->destFileName);
0244 
0245     return fi.completeBaseName();
0246 }
0247 
0248 QString QueueListViewItem::destSuffix() const
0249 {
0250     QFileInfo fi(d->destFileName);
0251 
0252     return fi.suffix();
0253 }
0254 
0255 // ---------------------------------------------------------------------------
0256 
0257 class Q_DECL_HIDDEN QueueListView::Private
0258 {
0259 
0260 public:
0261 
0262     enum RemoveItemsType
0263     {
0264         ItemsSelected = 0,
0265         ItemsDone,
0266         ItemsAll
0267     };
0268 
0269 public:
0270 
0271     explicit Private()
0272       : showTips        (false),
0273         iconSize        (64),
0274         toolTipTimer    (nullptr),
0275         progressTimer   (nullptr),
0276         thumbLoadThread (ThumbnailLoadThread::defaultThread()),
0277         toolTip         (nullptr),
0278         toolTipItem     (nullptr),
0279         progressPix     (nullptr)
0280     {
0281     }
0282 
0283     bool                        showTips;
0284 
0285     const int                   iconSize;
0286 
0287     QTimer*                     toolTipTimer;
0288     QTimer*                     progressTimer;
0289 
0290     ThumbnailLoadThread*        thumbLoadThread;
0291 
0292     QueueSettings               settings;
0293 
0294     AssignedBatchTools          toolsList;
0295 
0296     QueueToolTip*               toolTip;
0297 
0298     QueueListViewItem*          toolTipItem;
0299 
0300     DWorkingPixmap*             progressPix;
0301 };
0302 
0303 QueueListView::QueueListView(QWidget* const parent)
0304     : QTreeWidget(parent),
0305       d          (new Private)
0306 {
0307     setIconSize(QSize(d->iconSize, d->iconSize));
0308     setSelectionMode(QAbstractItemView::ExtendedSelection);
0309     setWhatsThis(i18nc("@info", "This is the list of images to batch process."));
0310 
0311     setDragEnabled(true);
0312     setAcceptDrops(true);
0313     setDropIndicatorShown(true);
0314     viewport()->setAcceptDrops(true);
0315     viewport()->setMouseTracking(true);
0316 
0317     setColumnCount(3);
0318     setSortingEnabled(true);
0319     setRootIsDecorated(false);
0320     setUniformRowHeights(true);
0321     setAllColumnsShowFocus(true);
0322     sortByColumn(1, Qt::AscendingOrder);
0323     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0324     setContextMenuPolicy(Qt::CustomContextMenu);
0325 
0326     QStringList titles;
0327     titles.append(i18nc("@title: preview item",       "Thumbnail"));
0328     titles.append(i18nc("@title: original item name", "Original"));
0329     titles.append(i18nc("@title: target item name",   "Target"));
0330     setHeaderLabels(titles);
0331     header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
0332     header()->setSectionResizeMode(1, QHeaderView::Stretch);
0333     header()->setSectionResizeMode(2, QHeaderView::Stretch);
0334 
0335     d->toolTip       = new QueueToolTip(this);
0336     d->toolTipTimer  = new QTimer(this);
0337     d->progressTimer = new QTimer(this);
0338     d->progressPix   = new DWorkingPixmap(this);
0339 
0340     // -----------------------------------------------------------
0341 
0342     connect(CoreDbAccess::databaseWatch(), SIGNAL(collectionImageChange(CollectionImageChangeset)),
0343             this, SLOT(slotCollectionImageChange(CollectionImageChangeset)),
0344             Qt::QueuedConnection);
0345 
0346     connect(d->thumbLoadThread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
0347             this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap)));
0348 
0349     connect(this, SIGNAL(customContextMenuRequested(QPoint)),
0350             this, SLOT(slotContextMenu()));
0351 
0352     connect(d->toolTipTimer, SIGNAL(timeout()),
0353             this, SLOT(slotToolTip()));
0354 
0355     connect(d->progressTimer, SIGNAL(timeout()),
0356             this, SLOT(slotProgressTimerDone()));
0357 }
0358 
0359 QueueListView::~QueueListView()
0360 {
0361     delete d->toolTip;
0362     delete d;
0363 }
0364 
0365 QPixmap QueueListView::progressPixmapForIndex(int index) const
0366 {
0367     if ((index >= 0) && (index < d->progressPix->frameCount()))
0368     {
0369         return (d->progressPix->frameAt(index));
0370     }
0371 
0372     return QPixmap();
0373 }
0374 
0375 Qt::DropActions QueueListView::supportedDropActions() const
0376 {
0377     return (Qt::CopyAction | Qt::MoveAction);
0378 }
0379 
0380 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0381 
0382 QMimeData* QueueListView::mimeData(const QList<QTreeWidgetItem*>& items) const
0383 
0384 #else
0385 
0386 // cppcheck-suppress passedByValue
0387 QMimeData* QueueListView::mimeData(const QList<QTreeWidgetItem*> items) const    // clazy:exclude=function-args-by-ref
0388 
0389 #endif
0390 
0391 {
0392     QList<QUrl> urls;
0393     QList<int> albumIDs;
0394     QList<qlonglong> imageIDs;
0395 
0396     Q_FOREACH (QTreeWidgetItem* const itm, items)
0397     {
0398         QueueListViewItem* const vitem = dynamic_cast<QueueListViewItem*>(itm);
0399 
0400         if (vitem)
0401         {
0402             urls.append(vitem->info().fileUrl());
0403             albumIDs.append(vitem->info().albumId());
0404             imageIDs.append(vitem->info().id());
0405         }
0406     }
0407 
0408     DItemDrag* const mimeData = new DItemDrag(urls, albumIDs, imageIDs);
0409 
0410     return mimeData;
0411 }
0412 
0413 void QueueListView::startDrag(Qt::DropActions /*supportedActions*/)
0414 {
0415     QList<QTreeWidgetItem*> items = selectedItems();
0416 
0417     if (items.isEmpty())
0418     {
0419         return;
0420     }
0421 
0422     QPixmap icon(QIcon::fromTheme(QLatin1String("image-jpeg")).pixmap(48));
0423     int w = icon.width();
0424     int h = icon.height();
0425 
0426     QPixmap pix(w + 4, h + 4);
0427     QString text(QString::number(items.count()));
0428 
0429     QPainter p(&pix);
0430     p.fillRect(0, 0, pix.width() - 1, pix.height() - 1, QColor(Qt::white));
0431     p.setPen(QPen(Qt::black, 1));
0432     p.drawRect(0, 0, pix.width() - 1, pix.height() - 1);
0433     p.drawPixmap(2, 2, icon);
0434     QRect r = p.boundingRect(2, 2, w, h, Qt::AlignLeft | Qt::AlignTop, text);
0435     r.setWidth(qMax(r.width(), r.height()));
0436     r.setHeight(qMax(r.width(), r.height()));
0437     p.fillRect(r, QColor(0, 80, 0));
0438     p.setPen(Qt::white);
0439     QFont f(font());
0440     f.setBold(true);
0441     p.setFont(f);
0442     p.drawText(r, Qt::AlignCenter, text);
0443     p.end();
0444 
0445     QDrag* const drag = new QDrag(this);
0446     drag->setMimeData(mimeData(items));
0447     drag->setPixmap(pix);
0448     drag->exec();
0449 }
0450 
0451 void QueueListView::dragEnterEvent(QDragEnterEvent* e)
0452 {
0453     QTreeWidget::dragEnterEvent(e);
0454     e->accept();
0455 }
0456 
0457 void QueueListView::dragMoveEvent(QDragMoveEvent* e)
0458 {
0459     int              albumID;
0460     QList<int>       albumIDs;
0461     QList<qlonglong> imageIDs;
0462     QList<QUrl>      urls;
0463 
0464     if      (DItemDrag::decode(e->mimeData(), urls, albumIDs, imageIDs) ||
0465              DAlbumDrag::decode(e->mimeData(), urls, albumID)           ||
0466              DTagListDrag::canDecode(e->mimeData()))
0467     {
0468         if (DItemDrag::decode(e->mimeData(), urls, albumIDs, imageIDs))
0469         {
0470             ItemInfoList imageInfoList;
0471 
0472             for (QList<qlonglong>::const_iterator it = imageIDs.constBegin() ;
0473                  it != imageIDs.constEnd() ; ++it)
0474             {
0475                 ItemInfo info(*it);
0476 
0477                 if (!findItemByInfo(info))
0478                 {
0479                     imageInfoList.append(info);
0480                 }
0481             }
0482 
0483             if (!imageInfoList.isEmpty())
0484             {
0485                 QTreeWidget::dragMoveEvent(e);
0486                 e->accept();
0487                 return;
0488             }
0489         }
0490     }
0491     else if (e->mimeData()->formats().contains(QLatin1String("digikam/workflow")))
0492     {
0493         QTreeWidget::dragMoveEvent(e);
0494         e->accept();
0495         return;
0496     }
0497 
0498     e->ignore();
0499 }
0500 
0501 void QueueListView::dropEvent(QDropEvent* e)
0502 {
0503     int              albumID;
0504     QList<int>       albumIDs;
0505     QList<qlonglong> imageIDs;
0506     QList<QUrl>      urls;
0507 
0508     if      (DItemDrag::decode(e->mimeData(), urls, albumIDs, imageIDs))
0509     {
0510         ItemInfoList imageInfoList;
0511 
0512         for (QList<qlonglong>::const_iterator it = imageIDs.constBegin() ;
0513              it != imageIDs.constEnd() ; ++it)
0514         {
0515             ItemInfo info(*it);
0516 
0517             if (!findItemByInfo(info))
0518             {
0519                 imageInfoList.append(info);
0520             }
0521         }
0522 
0523         if (!imageInfoList.isEmpty())
0524         {
0525             slotAddItems(imageInfoList);
0526             e->acceptProposedAction();
0527 
0528             QueueListView* const vitem = dynamic_cast<QueueListView*>(e->source());
0529 
0530             if (vitem && vitem != this)
0531             {
0532                 Q_FOREACH (const ItemInfo& info, imageInfoList)
0533                 {
0534                     vitem->removeItemByInfo(info);
0535                 }
0536             }
0537         }
0538     }
0539     else if (DAlbumDrag::decode(e->mimeData(), urls, albumID))
0540     {
0541         QList<qlonglong> itemIDs = CoreDbAccess().db()->getItemIDsInAlbum(albumID);
0542         ItemInfoList imageInfoList;
0543 
0544         for (QList<qlonglong>::const_iterator it = itemIDs.constBegin() ;
0545              it != itemIDs.constEnd() ; ++it)
0546         {
0547             ItemInfo info(*it);
0548 
0549             if (!findItemByInfo(info))
0550             {
0551                 imageInfoList.append(info);
0552             }
0553         }
0554 
0555         if (!imageInfoList.isEmpty())
0556         {
0557             slotAddItems(imageInfoList);
0558             e->acceptProposedAction();
0559         }
0560     }
0561     else if (DTagListDrag::canDecode(e->mimeData()))
0562     {
0563         QList<int> tagIDs;
0564 
0565         if (!DTagListDrag::decode(e->mimeData(), tagIDs))
0566         {
0567             return;
0568         }
0569 
0570         QList<qlonglong> itemIDs = CoreDbAccess().db()->getItemIDsInTag(tagIDs.first(), true);
0571         ItemInfoList imageInfoList;
0572 
0573         for (QList<qlonglong>::const_iterator it = itemIDs.constBegin() ;
0574              it != itemIDs.constEnd() ; ++it)
0575         {
0576             ItemInfo info(*it);
0577 
0578             if (!findItemByInfo(info))
0579             {
0580                 imageInfoList.append(info);
0581             }
0582         }
0583 
0584         if (!imageInfoList.isEmpty())
0585         {
0586             slotAddItems(imageInfoList);
0587             e->acceptProposedAction();
0588         }
0589     }
0590     else if (e->mimeData()->formats().contains(QLatin1String("digikam/workflow")))
0591     {
0592         QByteArray ba = e->mimeData()->data(QLatin1String("digikam/workflow"));
0593 
0594         if (ba.size())
0595         {
0596             QDataStream ds(ba);
0597             QString     title;
0598             ds >> title;
0599             QueueMgrWindow::queueManagerWindow()->slotAssignQueueSettings(title);
0600 
0601             e->acceptProposedAction();
0602         }
0603     }
0604     else
0605     {
0606         e->ignore();
0607     }
0608 
0609     Q_EMIT signalQueueContentsChanged();
0610 }
0611 
0612 void QueueListView::setEnableToolTips(bool val)
0613 {
0614     d->showTips = val;
0615 
0616     if (!val)
0617     {
0618         hideToolTip();
0619     }
0620 }
0621 
0622 void QueueListView::hideToolTip()
0623 {
0624     d->toolTipItem = nullptr;
0625     d->toolTipTimer->stop();
0626     slotToolTip();
0627 }
0628 
0629 bool QueueListView::acceptToolTip(const QPoint& pos) const
0630 {
0631     if (columnAt(pos.x()) == 0)
0632     {
0633         return true;
0634     }
0635 
0636     return false;
0637 }
0638 
0639 void QueueListView::slotToolTip()
0640 {
0641     d->toolTip->setQueueItem(d->toolTipItem);
0642 }
0643 
0644 void QueueListView::mouseMoveEvent(QMouseEvent* e)
0645 {
0646     if (e->buttons() == Qt::NoButton)
0647     {
0648         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(itemAt(e->pos()));
0649 
0650         if (d->showTips)
0651         {
0652             if (!isActiveWindow())
0653             {
0654                 hideToolTip();
0655                 return;
0656             }
0657 
0658             if (item != d->toolTipItem)
0659             {
0660                 hideToolTip();
0661 
0662                 if (acceptToolTip(e->pos()))
0663                 {
0664                     d->toolTipItem = item;
0665                     d->toolTipTimer->setSingleShot(true);
0666                     d->toolTipTimer->start(500);
0667                 }
0668             }
0669 
0670             if ((item == d->toolTipItem) && !acceptToolTip(e->pos()))
0671             {
0672                 hideToolTip();
0673             }
0674         }
0675 
0676         return;
0677     }
0678 
0679     hideToolTip();
0680     QTreeWidget::mouseMoveEvent(e);
0681 }
0682 
0683 void QueueListView::wheelEvent(QWheelEvent* e)
0684 {
0685     hideToolTip();
0686     QTreeWidget::wheelEvent(e);
0687 }
0688 
0689 void QueueListView::keyPressEvent(QKeyEvent* e)
0690 {
0691     hideToolTip();
0692     QTreeWidget::keyPressEvent(e);
0693 }
0694 
0695 void QueueListView::focusOutEvent(QFocusEvent* e)
0696 {
0697     hideToolTip();
0698     QTreeWidget::focusOutEvent(e);
0699 }
0700 
0701 void QueueListView::leaveEvent(QEvent* e)
0702 {
0703     hideToolTip();
0704     QTreeWidget::leaveEvent(e);
0705 }
0706 
0707 void QueueListView::slotAddItems(const ItemInfoList& list)
0708 {
0709     if (list.count() == 0)
0710     {
0711         return;
0712     }
0713 
0714     for (ItemInfoList::ConstIterator it = list.begin() ; it != list.end() ; ++it)
0715     {
0716         ItemInfo info           = *it;
0717 
0718         // Check if the new item already exist in the list.
0719 
0720         bool find               = false;
0721         QueueListViewItem* item = nullptr;
0722 
0723         QTreeWidgetItemIterator iter(this);
0724 
0725         while (*iter)
0726         {
0727             item = dynamic_cast<QueueListViewItem*>(*iter);
0728 
0729             if (item && (item->info() == info))
0730             {
0731                 find = true;
0732             }
0733 
0734             ++iter;
0735         }
0736 
0737         if (!find)
0738         {
0739             new QueueListViewItem(this, info);
0740         }
0741     }
0742 
0743     updateDestFileNames();
0744 
0745     Q_EMIT signalQueueContentsChanged();
0746 }
0747 
0748 void QueueListView::drawRow(QPainter* p, const QStyleOptionViewItem& opt, const QModelIndex& index) const
0749 {
0750     QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(itemFromIndex(index));
0751 
0752     if (item && !item->hasValidThumbnail())
0753     {
0754         ItemInfo info = item->info();
0755         d->thumbLoadThread->find(ThumbnailIdentifier(info.fileUrl().toLocalFile()));
0756     }
0757 
0758     QTreeWidget::drawRow(p, opt, index);
0759 }
0760 
0761 void QueueListView::slotThumbnailLoaded(const LoadingDescription& desc, const QPixmap& pix)
0762 {
0763     QTreeWidgetItemIterator it(this);
0764 
0765     while (*it)
0766     {
0767         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
0768 
0769         if (item && (item->info().fileUrl() == QUrl::fromLocalFile(desc.filePath)))
0770         {
0771             if (pix.isNull())
0772             {
0773                 item->setThumb(QIcon::fromTheme(QLatin1String("view-preview")).pixmap(d->iconSize, QIcon::Disabled));
0774             }
0775             else
0776             {
0777                 item->setThumb(pix.scaled(d->iconSize, d->iconSize, Qt::KeepAspectRatio));
0778             }
0779 
0780             return;
0781         }
0782 
0783         ++it;
0784     }
0785 }
0786 
0787 void QueueListView::slotClearList()
0788 {
0789     removeItems(Private::ItemsAll);
0790 
0791     Q_EMIT signalQueueContentsChanged();
0792 }
0793 
0794 void QueueListView::slotRemoveSelectedItems()
0795 {
0796     removeItems(Private::ItemsSelected);
0797 
0798     Q_EMIT signalQueueContentsChanged();
0799 }
0800 
0801 void QueueListView::slotRemoveItemsDone()
0802 {
0803     removeItems(Private::ItemsDone);
0804 
0805     Q_EMIT signalQueueContentsChanged();
0806 }
0807 
0808 void QueueListView::removeItems(int removeType)
0809 {
0810     hideToolTip();
0811 
0812     bool find;
0813 
0814     do
0815     {
0816         find = false;
0817         QTreeWidgetItemIterator it(this);
0818 
0819         while (*it)
0820         {
0821             QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
0822 
0823             if (item)
0824             {
0825                 switch (removeType)
0826                 {
0827                     case Private::ItemsSelected:
0828                     {
0829                         if (item->isSelected())
0830                         {
0831                             delete item;
0832                             find = true;
0833                         }
0834 
0835                         break;
0836                     }
0837 
0838                     case Private::ItemsDone:
0839                     {
0840                         if (item->isDone())
0841                         {
0842                             delete item;
0843                             find = true;
0844                         }
0845 
0846                         break;
0847                     }
0848 
0849                     default:  // Private::ItemsAll
0850                     {
0851                         delete item;
0852                         find = true;
0853                         break;
0854                     }
0855                 }
0856             }
0857 
0858             ++it;
0859         }
0860     }
0861     while (find);
0862 
0863     Q_EMIT signalQueueContentsChanged();
0864 }
0865 
0866 void QueueListView::removeItemByInfo(const ItemInfo& info)
0867 {
0868     removeItemById(info.id());
0869 }
0870 
0871 void QueueListView::removeItemById(qlonglong id)
0872 {
0873     hideToolTip();
0874 
0875     bool find;
0876 
0877     do
0878     {
0879         find = false;
0880         QTreeWidgetItemIterator it(this);
0881 
0882         while (*it)
0883         {
0884             QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
0885 
0886             if (item && (item->info().id() == id))
0887             {
0888                 delete item;
0889                 find = true;
0890                 break;
0891             }
0892 
0893             ++it;
0894         }
0895     }
0896     while (find);
0897 
0898     Q_EMIT signalQueueContentsChanged();
0899 }
0900 
0901 bool QueueListView::findItemByInfo(const ItemInfo& info)
0902 {
0903     return (findItemById(info.id()) ? true : false);
0904 }
0905 
0906 QueueListViewItem* QueueListView::findItemById(qlonglong id)
0907 {
0908     QTreeWidgetItemIterator it(this);
0909 
0910     while (*it)
0911     {
0912         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
0913 
0914         if (item && (item->info().id() == id))
0915         {
0916             return item;
0917         }
0918 
0919         ++it;
0920     }
0921 
0922     return nullptr;
0923 }
0924 
0925 QueueListViewItem* QueueListView::findItemByUrl(const QUrl& url)
0926 {
0927     QTreeWidgetItemIterator it(this);
0928 
0929     while (*it)
0930     {
0931         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
0932 
0933         if (item && (item->info().fileUrl() == url))
0934         {
0935             return item;
0936         }
0937 
0938         ++it;
0939     }
0940 
0941     return nullptr;
0942 }
0943 
0944 int QueueListView::itemsCount()
0945 {
0946     int count = 0;
0947     QTreeWidgetItemIterator it(this);
0948 
0949     while (*it)
0950     {
0951         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
0952 
0953         if (item)
0954         {
0955             ++count;
0956         }
0957 
0958         ++it;
0959     }
0960 
0961     return count;
0962 }
0963 
0964 void QueueListView::cancelItems()
0965 {
0966     QTreeWidgetItemIterator it(this);
0967 
0968     while (*it)
0969     {
0970         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
0971 
0972         if (item && item->isBusy())
0973         {
0974             item->setCanceled();
0975         }
0976 
0977         ++it;
0978     }
0979 }
0980 
0981 ItemInfoList QueueListView::pendingItemsList()
0982 {
0983     ItemInfoList list;
0984     QTreeWidgetItemIterator it(this);
0985 
0986     while (*it)
0987     {
0988         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
0989 
0990         if (item && !item->isDone())
0991         {
0992             list.append(item->info());
0993         }
0994 
0995         ++it;
0996     }
0997 
0998     return list;
0999 }
1000 
1001 int QueueListView::pendingItemsCount()
1002 {
1003     return pendingItemsList().count();
1004 }
1005 
1006 int QueueListView::pendingTasksCount()
1007 {
1008     int count = 0;
1009     QTreeWidgetItemIterator it(this);
1010 
1011     while (*it)
1012     {
1013         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
1014 
1015         if (item && !item->isDone())
1016         {
1017             count += assignedTools().m_toolsList.count();
1018         }
1019 
1020         ++it;
1021     }
1022 
1023     return count;
1024 }
1025 
1026 void QueueListView::setSettings(const QueueSettings& settings)
1027 {
1028     d->settings = settings;
1029     resetQueue();
1030     updateDestFileNames();
1031 }
1032 
1033 QueueSettings QueueListView::settings() const
1034 {
1035     return d->settings;
1036 }
1037 
1038 void QueueListView::setAssignedTools(const AssignedBatchTools& tools)
1039 {
1040     d->toolsList = tools;
1041     resetQueue();
1042     updateDestFileNames();
1043 }
1044 
1045 AssignedBatchTools QueueListView::assignedTools() const
1046 {
1047     return d->toolsList;
1048 }
1049 
1050 void QueueListView::slotAssignedToolsChanged(const AssignedBatchTools& tools)
1051 {
1052     setAssignedTools(tools);
1053 }
1054 
1055 void QueueListView::resetQueue()
1056 {
1057     //reset all items
1058 
1059     QTreeWidgetItemIterator it(this);
1060 
1061     while (*it)
1062     {
1063         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
1064 
1065         if (item)
1066         {
1067             item->reset();
1068         }
1069 
1070         ++it;
1071     }
1072 }
1073 
1074 void QueueListView::updateDestFileNames()
1075 {
1076     QMap<QString, QString> renamingResults;
1077 
1078     if (settings().renamingRule == QueueSettings::CUSTOMIZE)
1079     {
1080         AdvancedRenameManager manager;
1081 
1082         ParseSettings psettings;
1083         psettings.parseString = settings().renamingParser;
1084 
1085         QList<ParseSettings> files;
1086         QTreeWidgetItemIterator it(this);
1087 
1088         while (*it)
1089         {
1090             QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
1091 
1092             if (item)
1093             {
1094                 // Update base name using queue renaming rules.
1095 
1096                 ItemInfo info = item->info();
1097                 QFileInfo fi(info.filePath());
1098 
1099                 ParseSettings ps;
1100                 ps.fileUrl = QUrl::fromLocalFile(fi.absoluteFilePath());
1101                 files << ps;
1102             }
1103 
1104             ++it;
1105         }
1106 
1107         manager.addFiles(files);
1108         manager.parseFiles(psettings);
1109         renamingResults = manager.newFileList(true);
1110     }
1111 
1112     AssignedBatchTools tools = assignedTools();
1113 
1114     QTreeWidgetItemIterator it(this);
1115 
1116     while (*it)
1117     {
1118         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
1119 
1120         if (item)
1121         {
1122             // Update base name using queue renaming rules.
1123 
1124             ItemInfo info     = item->info();
1125             QFileInfo fi(info.filePath());
1126 
1127             // Update suffix using assigned batch tool rules.
1128 
1129             bool extensionSet = false;
1130             tools.m_itemUrl   = item->info().fileUrl();
1131             QString newSuffix = tools.targetSuffix(&extensionSet);
1132             QString newName   = QString::fromUtf8("%1.%2").arg(fi.completeBaseName()).arg(newSuffix);
1133 
1134             if ((settings().renamingRule == QueueSettings::CUSTOMIZE) && !renamingResults.isEmpty())
1135             {
1136                 QFileInfo fi2(renamingResults[fi.absoluteFilePath()]);
1137 
1138                 if (extensionSet)
1139                 {
1140                     newName = QString::fromUtf8("%1.%2").arg(fi2.completeBaseName())
1141                               .arg(newSuffix);
1142                 }
1143                 else
1144                 {
1145                     newName = fi2.fileName();
1146                 }
1147             }
1148 
1149             item->setDestFileName(newName);
1150         }
1151 
1152         ++it;
1153     }
1154 }
1155 
1156 void QueueListView::slotContextMenu()
1157 {
1158     if (!viewport()->isEnabled())
1159     {
1160         return;
1161     }
1162 
1163     KActionCollection* const acol = QueueMgrWindow::queueManagerWindow()->actionCollection();
1164     QMenu popmenu(this);
1165     popmenu.addAction(acol->action(QLatin1String("queuemgr_removeitemssel")));
1166     popmenu.addSeparator();
1167     popmenu.addAction(acol->action(QLatin1String("queuemgr_clearlist")));
1168     popmenu.exec(QCursor::pos());
1169 }
1170 
1171 void QueueListView::slotCollectionImageChange(const CollectionImageChangeset& changeset)
1172 {
1173     if (QueueMgrWindow::queueManagerWindow()->isBusy())
1174     {
1175         return;
1176     }
1177 
1178     switch (changeset.operation())
1179     {
1180         case CollectionImageChangeset::Deleted:
1181         case CollectionImageChangeset::Removed:
1182         case CollectionImageChangeset::RemovedAll:
1183         {
1184             Q_FOREACH (const qlonglong& id, changeset.ids())
1185             {
1186                 removeItemById(id);
1187             }
1188 
1189             break;
1190         }
1191 
1192         default:
1193         {
1194             break;
1195         }
1196     }
1197 }
1198 
1199 void QueueListView::reloadThumbs(const QUrl& url)
1200 {
1201     d->thumbLoadThread->find(ThumbnailIdentifier(url.toLocalFile()));
1202 }
1203 
1204 void QueueListView::setItemBusy(qlonglong id)
1205 {
1206     QueueListViewItem* const item = findItemById(id);
1207 
1208     if (item)
1209     {
1210         item->setBusy();
1211         d->progressTimer->start(300);
1212     }
1213 }
1214 
1215 void QueueListView::slotProgressTimerDone()
1216 {
1217     QTreeWidgetItemIterator it(this);
1218     int active = 0;
1219 
1220     while (*it)
1221     {
1222         QueueListViewItem* const item = dynamic_cast<QueueListViewItem*>(*it);
1223 
1224         if (item && item->isBusy())
1225         {
1226             item->animProgress();
1227             active++;
1228         }
1229 
1230         ++it;
1231     }
1232 
1233     if (!active)
1234     {
1235         d->progressTimer->stop();
1236     }
1237 }
1238 
1239 } // namespace Digikam
1240 
1241 #include "moc_queuelist.cpp"