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"