File indexing completed on 2025-01-05 03:57:58

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2021-09-27
0007  * Description : List-view for the Showfoto stack view.
0008  *
0009  * SPDX-FileCopyrightText: 2021-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "showfotostackviewlist.h"
0016 
0017 // Qt includes
0018 
0019 #include <QApplication>
0020 #include <QActionGroup>
0021 #include <QStyle>
0022 #include <QIcon>
0023 #include <QFileInfo>
0024 #include <QHeaderView>
0025 #include <QDir>
0026 #include <QTimer>
0027 #include <QMenu>
0028 #include <QMimeData>
0029 #include <QScrollBar>
0030 
0031 // KDE includes
0032 
0033 #include <klocalizedstring.h>
0034 
0035 // Local includes
0036 
0037 #include "digikam_debug.h"
0038 #include "digikam_globals.h"
0039 #include "showfotothumbnailbar.h"
0040 #include "showfotoiteminfo.h"
0041 #include "showfotostackviewtooltip.h"
0042 #include "showfotostackviewitem.h"
0043 #include "showfotosettings.h"
0044 #include "dfileoperations.h"
0045 
0046 namespace ShowFoto
0047 {
0048 
0049 class Q_DECL_HIDDEN ShowfotoStackViewList::Private
0050 {
0051 
0052 public:
0053 
0054     explicit Private()
0055       : view        (nullptr),
0056         thumbbar    (nullptr),
0057         toolTipTimer(nullptr),
0058         toolTip     (nullptr)
0059     {
0060     }
0061 
0062     ShowfotoStackViewSideBar* view;
0063     ShowfotoThumbnailBar*     thumbbar;
0064     QTimer*                   toolTipTimer;
0065     ShowfotoStackViewToolTip* toolTip;
0066     QModelIndex               toolTipIndex;
0067 };
0068 
0069 ShowfotoStackViewList::ShowfotoStackViewList(ShowfotoStackViewSideBar* const view)
0070     : QTreeWidget(view),
0071       d          (new Private)
0072 {
0073     d->view     = view;
0074 
0075     setObjectName(QLatin1String("ShowfotoStackViewList"));
0076     setRootIsDecorated(false);
0077     setItemsExpandable(false);
0078     setUniformRowHeights(true);
0079     setAllColumnsShowFocus(true);
0080     setAlternatingRowColors(true);
0081     setExpandsOnDoubleClick(false);
0082     setIconSize(QSize(SizeSmall, SizeSmall));
0083     setSelectionBehavior(QAbstractItemView::SelectRows);
0084     setSelectionMode(QAbstractItemView::SingleSelection);
0085     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0086 
0087     setColumnCount(4);
0088     QStringList titles;
0089     titles.append(i18nc("@title: item file name",          "Name"));
0090     titles.append(i18nc("@title: item file size",          "Size"));
0091     titles.append(i18nc("@title: item file type",          "Type"));
0092     titles.append(i18nc("@title: item date from metadata", "Date"));
0093     setHeaderLabels(titles);
0094     header()->setSectionResizeMode(FileName, QHeaderView::ResizeToContents);
0095     header()->setSectionResizeMode(FileSize, QHeaderView::Stretch);
0096     header()->setSectionResizeMode(FileType, QHeaderView::Stretch);
0097     header()->setSectionResizeMode(FileDate, QHeaderView::Stretch);
0098 
0099     setDragEnabled(true);
0100     setDragDropMode(QAbstractItemView::DragOnly);
0101     viewport()->setMouseTracking(true);
0102 
0103     d->toolTip       = new ShowfotoStackViewToolTip(this);
0104     d->toolTipTimer  = new QTimer(this);
0105 
0106     connect(d->toolTipTimer, SIGNAL(timeout()),
0107             this, SLOT(slotToolTip()));
0108 }
0109 
0110 ShowfotoStackViewList::~ShowfotoStackViewList()
0111 {
0112     delete d->toolTip;
0113     delete d;
0114 }
0115 
0116 int ShowfotoStackViewList::sortOrder() const
0117 {
0118     return d->view->sortOrder();
0119 }
0120 
0121 int ShowfotoStackViewList::sortRole() const
0122 {
0123     return d->view->sortRole();
0124 }
0125 
0126 void ShowfotoStackViewList::setThumbbar(ShowfotoThumbnailBar* const thumbbar)
0127 {
0128     d->thumbbar = thumbbar;
0129 
0130     connect(d->thumbbar->showfotoItemModel(), SIGNAL(itemInfosAboutToBeAdded(QList<ShowfotoItemInfo>)),
0131             this, SLOT(slotItemsAdded(QList<ShowfotoItemInfo>)));
0132 
0133     connect(d->thumbbar->showfotoItemModel(), SIGNAL(itemInfosAboutToBeRemoved(QList<ShowfotoItemInfo>)),
0134             this, SLOT(slotItemsRemoved(QList<ShowfotoItemInfo>)));
0135 
0136     connect(d->thumbbar->showfotoItemModel(), SIGNAL(itemInfosAdded(QList<ShowfotoItemInfo>)),
0137             this, SLOT(slotItemsListChanged()));
0138 
0139     connect(d->thumbbar->showfotoItemModel(), SIGNAL(itemInfosRemoved(QList<ShowfotoItemInfo>)),
0140             this, SLOT(slotItemsListChanged()));
0141 
0142     connect(d->thumbbar->showfotoThumbnailModel(), SIGNAL(signalItemThumbnail(ShowfotoItemInfo,QPixmap)),
0143             this, SLOT(slotItemThumbnail(ShowfotoItemInfo,QPixmap)));
0144 
0145     connect(d->thumbbar, SIGNAL(selected(QList<ShowfotoItemInfo>)),
0146             this, SLOT(slotItemsSelected(QList<ShowfotoItemInfo>)));
0147 
0148     connect(d->thumbbar, SIGNAL(deselected(QList<ShowfotoItemInfo>)),
0149             this, SLOT(slotItemsDeselected(QList<ShowfotoItemInfo>)));
0150 
0151     connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
0152             this, SLOT(slotSelectionChanged(QTreeWidgetItem*)));
0153 
0154     connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
0155             this, SLOT(slotItemDoubleClicked(QTreeWidgetItem*)));
0156 }
0157 
0158 void ShowfotoStackViewList::slotItemsAdded(const QList<ShowfotoItemInfo>& items)
0159 {
0160     Q_FOREACH (const ShowfotoItemInfo& info, items)
0161     {
0162         ShowfotoStackViewItem* const it = new ShowfotoStackViewItem(this);
0163         it->setInfo(info);
0164     }
0165 }
0166 
0167 void ShowfotoStackViewList::slotItemsListChanged()
0168 {
0169     Q_EMIT signalItemListChanged(topLevelItemCount());
0170 }
0171 
0172 void ShowfotoStackViewList::slotItemsRemoved(const QList<ShowfotoItemInfo>& items)
0173 {
0174     QTreeWidgetItemIterator iter(this);
0175     ShowfotoStackViewItem* sitem = nullptr;
0176     QList<ShowfotoStackViewItem*> list;
0177 
0178     while (*iter)
0179     {
0180         sitem = dynamic_cast<ShowfotoStackViewItem*>(*iter);
0181 
0182         if (sitem && (items.contains(sitem->info())))
0183         {
0184             list << sitem;
0185         }
0186 
0187         ++iter;
0188     }
0189 
0190     Q_FOREACH (ShowfotoStackViewItem* const it, list)
0191     {
0192         delete it;
0193     }
0194 }
0195 
0196 void ShowfotoStackViewList::slotItemsSelected(const QList<ShowfotoItemInfo>& items)
0197 {
0198     blockSignals(true);
0199     QTreeWidgetItemIterator iter(this);
0200     ShowfotoStackViewItem* sitem = nullptr;
0201 
0202     while (*iter)
0203     {
0204         sitem = dynamic_cast<ShowfotoStackViewItem*>(*iter);
0205 
0206         if (sitem && items.contains(sitem->info()))
0207         {
0208             sitem->setSelected(true);
0209         }
0210 
0211         ++iter;
0212     }
0213 
0214     blockSignals(false);
0215 }
0216 
0217 void ShowfotoStackViewList::slotItemsDeselected(const QList<ShowfotoItemInfo>& items)
0218 {
0219     blockSignals(true);
0220 
0221     QTreeWidgetItemIterator iter(this);
0222     ShowfotoStackViewItem* sitem = nullptr;
0223 
0224     while (*iter)
0225     {
0226         sitem = dynamic_cast<ShowfotoStackViewItem*>(*iter);
0227 
0228         if (sitem && items.contains(sitem->info()))
0229         {
0230             sitem->setSelected(false);
0231         }
0232 
0233         ++iter;
0234     }
0235 
0236     blockSignals(false);
0237 }
0238 
0239 void ShowfotoStackViewList::slotSelectionChanged(QTreeWidgetItem* item)
0240 {
0241     ShowfotoStackViewItem* const sitem = dynamic_cast<ShowfotoStackViewItem*>(item);
0242 
0243     if (sitem)
0244     {
0245         d->thumbbar->setCurrentInfo(sitem->info());
0246     }
0247 }
0248 
0249 void ShowfotoStackViewList::slotItemDoubleClicked(QTreeWidgetItem* item)
0250 {
0251     ShowfotoStackViewItem* const sitem = dynamic_cast<ShowfotoStackViewItem*>(item);
0252 
0253     if (sitem)
0254     {
0255         Q_EMIT signalShowfotoItemInfoActivated(sitem->info());
0256     }
0257 }
0258 
0259 void ShowfotoStackViewList::slotItemThumbnail(const ShowfotoItemInfo& info, const QPixmap& pix)
0260 {
0261     QTreeWidgetItemIterator iter(this);
0262     ShowfotoStackViewItem* sitem = nullptr;
0263 
0264     while (*iter)
0265     {
0266         sitem = dynamic_cast<ShowfotoStackViewItem*>(*iter);
0267 
0268         if (sitem && (sitem->info() == info))
0269         {
0270             sitem->setThumbnail(pix);
0271             return;
0272         }
0273 
0274         ++iter;
0275     }
0276 }
0277 
0278 void ShowfotoStackViewList::drawRow(QPainter* p, const QStyleOptionViewItem& opt, const QModelIndex& index) const
0279 {
0280     ShowfotoItemInfo info = infoFromIndex(index);
0281 
0282     if (!info.isNull() && index.isValid())
0283     {
0284         ShowfotoStackViewItem* const sitem = dynamic_cast<ShowfotoStackViewItem*>(itemFromIndex(index));
0285 
0286         if (
0287             sitem &&
0288             (
0289                 sitem->icon(FileName).isNull() ||
0290                 (sitem->icon(FileName).actualSize(iconSize()) != iconSize())
0291             )
0292            )
0293         {
0294             d->thumbbar->showfotoThumbnailModel()->thumbnailLoadThread()->find(ThumbnailIdentifier(info.url.toLocalFile()));
0295         }
0296     }
0297 
0298     QTreeWidget::drawRow(p, opt, index);
0299 }
0300 
0301 ShowfotoItemInfo ShowfotoStackViewList::infoFromIndex(const QModelIndex& index) const
0302 {
0303     ShowfotoItemInfo info;
0304 
0305     if (index.isValid())
0306     {
0307         ShowfotoStackViewItem* const sitem = dynamic_cast<ShowfotoStackViewItem*>(itemFromIndex(index));
0308 
0309         if (sitem)
0310         {
0311             info = sitem->info();
0312         }
0313     }
0314 
0315     return info;
0316 }
0317 
0318 void ShowfotoStackViewList::slotOpenInFileManager()
0319 {
0320     QModelIndex index  = currentIndex();
0321     QList<QUrl> urls;
0322 
0323     if (index.isValid())
0324     {
0325         ShowfotoStackViewItem* const sitem = dynamic_cast<ShowfotoStackViewItem*>(itemFromIndex(index));
0326 
0327         if (sitem)
0328         {
0329             urls << QUrl::fromLocalFile(sitem->info().url.toLocalFile());
0330 
0331             DFileOperations::openInFileManager(urls);
0332         }
0333     }
0334 }
0335 
0336 void ShowfotoStackViewList::slotIconSizeChanged(int size)
0337 {
0338     setIconSize(QSize(size, size));
0339 }
0340 
0341 void ShowfotoStackViewList::contextMenuEvent(QContextMenuEvent* e)
0342 {
0343     QMenu* const ctxmenu        = new QMenu(this);
0344     ctxmenu->setTitle(i18nc("@title", "Stack-View"));
0345 
0346     QMenu* const iconMenu       = new QMenu(i18nc("@title:menu", "Icon Size"), ctxmenu);
0347     iconMenu->setIcon(QIcon::fromTheme(QLatin1String("file-zoom-in")));
0348 
0349     QActionGroup* const sizeGrp = new QActionGroup(iconMenu);
0350 
0351     QAction* const sizeSmall    = iconMenu->addAction(i18nc("@action:inmenu", "Small (%1x%2)",  SizeSmall,  SizeSmall));
0352     sizeSmall->setCheckable(true);
0353     sizeGrp->addAction(sizeSmall);
0354 
0355     QAction* const sizeMedium   = iconMenu->addAction(i18nc("@action:inmenu", "Medium (%1x%2)", SizeMedium, SizeMedium));
0356     sizeMedium->setCheckable(true);
0357     sizeGrp->addAction(sizeMedium);
0358 
0359     QAction* const sizeLarge    = iconMenu->addAction(i18nc("@action:inmenu", "Large (%1x%2)",  SizeLarge,  SizeLarge));
0360     sizeLarge->setCheckable(true);
0361     sizeGrp->addAction(sizeLarge);
0362 
0363     QAction* const sizeHuge     = iconMenu->addAction(i18nc("@action:inmenu", "Huge (%1x%2)",   SizeHuge,   SizeHuge));
0364     sizeHuge->setCheckable(true);
0365     sizeGrp->addAction(sizeHuge);
0366 
0367     sizeGrp->setExclusive(true);
0368 
0369     switch (iconSize().width())
0370     {
0371         case SizeMedium:
0372         {
0373             sizeMedium->setChecked(true);
0374             break;
0375         }
0376 
0377         case SizeLarge:
0378         {
0379             sizeLarge->setChecked(true);
0380             break;
0381         }
0382 
0383         case SizeHuge:
0384         {
0385             sizeHuge->setChecked(true);
0386             break;
0387         }
0388 
0389         default:
0390         {
0391             sizeSmall->setChecked(true);
0392             break;
0393         }
0394     }
0395 
0396     ctxmenu->addMenu(iconMenu);
0397     ctxmenu->addSeparator();
0398     ctxmenu->addActions(d->view->pluginActions());
0399     ctxmenu->addSeparator();
0400 
0401     QAction* const addFavorite     = new QAction(QIcon::fromTheme(QLatin1String("list-add")),
0402                                                  i18nc("@action: context menu", "Add as Favorite"), ctxmenu);
0403     ctxmenu->addAction(addFavorite);
0404 
0405     connect(addFavorite, SIGNAL(triggered()),
0406             this, SIGNAL(signalAddFavorite()));
0407 
0408     QAction* const openFileMngr    = new QAction(QIcon::fromTheme(QLatin1String("folder-open")),
0409                                                  i18nc("@action: context menu", "Open in File Manager"), ctxmenu);
0410     ctxmenu->addAction(openFileMngr);
0411 
0412     connect(openFileMngr, SIGNAL(triggered()),
0413             this, SLOT(slotOpenInFileManager()));
0414 
0415     QAction* const clearListAction = new QAction(QIcon::fromTheme(QLatin1String("edit-clear")),
0416                                                  i18nc("@action: context menu", "Clear All Items"), this);
0417 
0418     ctxmenu->addAction(clearListAction);
0419 
0420     connect(clearListAction, SIGNAL(triggered()),
0421             this, SIGNAL(signalClearItemsList()));
0422 
0423     QAction* const removeItemsAction = new QAction(QIcon::fromTheme(QLatin1String("edit-clear")),
0424                                                    i18nc("@action: context menu", "Remove Items"), this);
0425 
0426     ctxmenu->addAction(removeItemsAction);
0427 
0428     connect(removeItemsAction, SIGNAL(triggered()),
0429             this, SLOT(slotRemoveItems()));
0430 
0431     ctxmenu->exec(e->globalPos());
0432 
0433     if      (sizeSmall->isChecked())
0434     {
0435         setIconSize(QSize(SizeSmall, SizeSmall));
0436     }
0437     else if (sizeMedium->isChecked())
0438     {
0439         setIconSize(QSize(SizeMedium, SizeMedium));
0440     }
0441     else if (sizeLarge->isChecked())
0442     {
0443         setIconSize(QSize(SizeLarge, SizeLarge));
0444     }
0445     else if (sizeHuge->isChecked())
0446     {
0447         setIconSize(QSize(SizeHuge, SizeHuge));
0448     }
0449 
0450     delete ctxmenu;
0451 
0452     QTreeView::contextMenuEvent(e);
0453 }
0454 
0455 void ShowfotoStackViewList::slotRemoveItems()
0456 {
0457     QList<QTreeWidgetItem*> sel = selectedItems();
0458 
0459     if (sel.isEmpty())
0460     {
0461         return;
0462     }
0463 
0464     QList<ShowfotoItemInfo> infos;
0465 
0466     Q_FOREACH (QTreeWidgetItem* const item, sel)
0467     {
0468         ShowfotoStackViewItem* const sitem = dynamic_cast<ShowfotoStackViewItem*>(item);
0469 
0470         if (sitem)
0471         {
0472             infos << sitem->info();
0473             delete sitem;
0474         }
0475     }
0476 
0477     Q_EMIT signalRemoveItemInfos(infos);
0478 }
0479 
0480 void ShowfotoStackViewList::hideToolTip()
0481 {
0482     d->toolTipIndex = QModelIndex();
0483     d->toolTipTimer->stop();
0484     slotToolTip();
0485 }
0486 
0487 void ShowfotoStackViewList::slotToolTip()
0488 {
0489     d->toolTip->setIndex(d->toolTipIndex);
0490 }
0491 
0492 bool ShowfotoStackViewList::acceptToolTip(const QModelIndex& index) const
0493 {
0494     ShowfotoItemInfo info = infoFromIndex(index);
0495 
0496     if (!info.isNull())
0497     {
0498         return true;
0499     }
0500 
0501     return false;
0502 }
0503 
0504 void ShowfotoStackViewList::mouseMoveEvent(QMouseEvent* e)
0505 {
0506     if (e->buttons() == Qt::NoButton)
0507     {
0508         QModelIndex index = indexAt(e->pos());
0509 
0510         if (ShowfotoSettings::instance()->getShowToolTip())
0511         {
0512             if (!isActiveWindow())
0513             {
0514                 hideToolTip();
0515                 return;
0516             }
0517 
0518             if (index != d->toolTipIndex)
0519             {
0520                 hideToolTip();
0521 
0522                 if (acceptToolTip(index))
0523                 {
0524                     d->toolTipIndex = index;
0525                     d->toolTipTimer->setSingleShot(true);
0526                     d->toolTipTimer->start(500);
0527                 }
0528             }
0529 
0530             if ((index == d->toolTipIndex) && !acceptToolTip(index))
0531             {
0532                 hideToolTip();
0533             }
0534         }
0535 
0536         return;
0537     }
0538 
0539     hideToolTip();
0540     QTreeView::mouseMoveEvent(e);
0541 }
0542 
0543 void ShowfotoStackViewList::wheelEvent(QWheelEvent* e)
0544 {
0545     hideToolTip();
0546     QTreeView::wheelEvent(e);
0547 }
0548 
0549 void ShowfotoStackViewList::keyPressEvent(QKeyEvent* e)
0550 {
0551     hideToolTip();
0552     QTreeView::keyPressEvent(e);
0553 }
0554 
0555 void ShowfotoStackViewList::focusOutEvent(QFocusEvent* e)
0556 {
0557     hideToolTip();
0558     QTreeView::focusOutEvent(e);
0559 }
0560 
0561 void ShowfotoStackViewList::leaveEvent(QEvent* e)
0562 {
0563     hideToolTip();
0564     QTreeView::leaveEvent(e);
0565 }
0566 
0567 QList<QUrl> ShowfotoStackViewList::urls()
0568 {
0569     QTreeWidgetItemIterator iter(this);
0570     ShowfotoStackViewItem* sitem = nullptr;
0571     QList<QUrl> list;
0572 
0573     while (*iter)
0574     {
0575         sitem = dynamic_cast<ShowfotoStackViewItem*>(*iter);
0576 
0577         if (sitem)
0578         {
0579             list << sitem->info().url;
0580         }
0581 
0582         ++iter;
0583     }
0584 
0585     return list;
0586 }
0587 
0588 QUrl ShowfotoStackViewList::currentUrl() const
0589 {
0590     ShowfotoStackViewItem* sitem = dynamic_cast<ShowfotoStackViewItem*>(currentItem());
0591 
0592     if (sitem)
0593     {
0594         return (sitem->info().url);
0595     }
0596 
0597     return QUrl();
0598 }
0599 
0600 } // namespace ShowFoto
0601 
0602 #include "moc_showfotostackviewlist.cpp"