File indexing completed on 2025-01-19 03:59:21

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2012-08-21
0007  * Description : Overlays for the import interface
0008  *
0009  * SPDX-FileCopyrightText: 2012      by Islam Wazery <wazery at ubuntu dot com>
0010  * SPDX-FileCopyrightText: 2012-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "importoverlays.h"
0017 
0018 // KDE includes
0019 
0020 #include <klocalizedstring.h>
0021 
0022 // Local includes
0023 
0024 #include "importcategorizedview.h"
0025 #include "importdelegate.h"
0026 #include "camiteminfo.h"
0027 
0028 namespace Digikam
0029 {
0030 
0031 ImportOverlayWidget::ImportOverlayWidget(QWidget* const parent)
0032     : QAbstractButton(parent)
0033 {
0034 }
0035 
0036 void ImportOverlayWidget::paintEvent(QPaintEvent*)
0037 {
0038 }
0039 
0040 // -- Coordinates Overlay ------------------------------------------------------------------
0041 
0042 ImportCoordinatesOverlay::ImportCoordinatesOverlay(QObject* const parent)
0043     : AbstractWidgetDelegateOverlay(parent)
0044 {
0045 }
0046 
0047 ImportOverlayWidget* ImportCoordinatesOverlay::buttonWidget() const
0048 {
0049     return static_cast<ImportOverlayWidget*>(m_widget);
0050 }
0051 
0052 QWidget* ImportCoordinatesOverlay::createWidget()
0053 {
0054     QAbstractButton* const button = new ImportOverlayWidget(parentWidget());
0055 /*
0056     button->setCursor(Qt::PointingHandCursor);
0057 */
0058     return button;
0059 }
0060 
0061 void ImportCoordinatesOverlay::setActive(bool active)
0062 {
0063     AbstractWidgetDelegateOverlay::setActive(active);
0064 }
0065 
0066 void ImportCoordinatesOverlay::visualChange()
0067 {
0068     if (m_widget && m_widget->isVisible())
0069     {
0070         updatePosition();
0071     }
0072 }
0073 
0074 void ImportCoordinatesOverlay::updatePosition()
0075 {
0076     if (!m_index.isValid())
0077     {
0078         return;
0079     }
0080 
0081     QRect rect       = static_cast<ImportDelegate*>(delegate())->coordinatesIndicatorRect();
0082     QRect visualRect = m_view->visualRect(m_index);
0083     rect.translate(visualRect.topLeft());
0084 
0085     m_widget->setFixedSize(rect.width() + 1, rect.height() + 1);
0086     m_widget->move(rect.topLeft());
0087 }
0088 
0089 bool ImportCoordinatesOverlay::checkIndex(const QModelIndex& index) const
0090 {
0091     CamItemInfo info = ImportItemModel::retrieveCamItemInfo(index);
0092     QRect rect       = static_cast<ImportDelegate*>(delegate())->coordinatesIndicatorRect();
0093 
0094     if (!rect.isNull() && info.photoInfo.hasCoordinates)
0095     {
0096         m_widget->setToolTip(i18nc("@info:tooltip", "This item has geolocation information."));
0097         return true;
0098     }
0099 
0100     // If info.photoInfo.hasCoordinates = false, no need to show a tooltip, because there is no icon over thumbnail.
0101 
0102     return false;
0103 }
0104 
0105 void ImportCoordinatesOverlay::slotEntered(const QModelIndex& index)
0106 {
0107     AbstractWidgetDelegateOverlay::slotEntered(index);
0108     m_index = index;
0109     updatePosition();
0110 }
0111 
0112 // -- Lock Overlay ------------------------------------------------------------------
0113 
0114 ImportLockOverlay::ImportLockOverlay(QObject* const parent)
0115     : AbstractWidgetDelegateOverlay(parent)
0116 {
0117 }
0118 
0119 ImportOverlayWidget* ImportLockOverlay::buttonWidget() const
0120 {
0121     return static_cast<ImportOverlayWidget*>(m_widget);
0122 }
0123 
0124 QWidget* ImportLockOverlay::createWidget()
0125 {
0126     QAbstractButton* const button = new ImportOverlayWidget(parentWidget());
0127 /*
0128     button->setCursor(Qt::PointingHandCursor);
0129 */
0130     return button;
0131 }
0132 
0133 void ImportLockOverlay::setActive(bool active)
0134 {
0135     AbstractWidgetDelegateOverlay::setActive(active);
0136 }
0137 
0138 void ImportLockOverlay::visualChange()
0139 {
0140     if (m_widget && m_widget->isVisible())
0141     {
0142         updatePosition();
0143     }
0144 }
0145 
0146 void ImportLockOverlay::updatePosition()
0147 {
0148     if (!m_index.isValid())
0149     {
0150         return;
0151     }
0152 
0153     QRect rect       = static_cast<ImportDelegate*>(delegate())->lockIndicatorRect();
0154     QRect visualRect = m_view->visualRect(m_index);
0155     rect.translate(visualRect.topLeft());
0156 
0157     m_widget->setFixedSize(rect.width() + 1, rect.height() + 1);
0158     m_widget->move(rect.topLeft());
0159 }
0160 
0161 bool ImportLockOverlay::checkIndex(const QModelIndex& index) const
0162 {
0163     CamItemInfo info = ImportItemModel::retrieveCamItemInfo(index);
0164 
0165     if (info.writePermissions == 0)
0166     {
0167         m_widget->setToolTip(i18nc("@info:tooltip", "This item is locked."));
0168         return true;
0169     }
0170 
0171     // If info.writePermissions = 1, no need to show a tooltip, because there is no icon over thumbnail.
0172 
0173     return false;
0174 }
0175 
0176 void ImportLockOverlay::slotEntered(const QModelIndex& index)
0177 {
0178     AbstractWidgetDelegateOverlay::slotEntered(index);
0179     m_index = index;
0180     updatePosition();
0181 }
0182 
0183 // -- Download Overlay ------------------------------------------------------------------
0184 
0185 ImportDownloadOverlay::ImportDownloadOverlay(QObject* const parent)
0186     : AbstractWidgetDelegateOverlay(parent)
0187 {
0188 }
0189 
0190 ImportOverlayWidget* ImportDownloadOverlay::buttonWidget() const
0191 {
0192     return static_cast<ImportOverlayWidget*>(m_widget);
0193 }
0194 
0195 QWidget* ImportDownloadOverlay::createWidget()
0196 {
0197     QAbstractButton* const button = new ImportOverlayWidget(parentWidget());
0198 /*
0199     button->setCursor(Qt::PointingHandCursor);
0200 */
0201     return button;
0202 }
0203 
0204 void ImportDownloadOverlay::setActive(bool active)
0205 {
0206     AbstractWidgetDelegateOverlay::setActive(active);
0207 }
0208 
0209 void ImportDownloadOverlay::visualChange()
0210 {
0211     if (m_widget && m_widget->isVisible())
0212     {
0213         updatePosition();
0214     }
0215 }
0216 
0217 void ImportDownloadOverlay::updatePosition()
0218 {
0219     if (!m_index.isValid())
0220     {
0221         return;
0222     }
0223 
0224     QRect rect       = static_cast<ImportDelegate*>(delegate())->downloadIndicatorRect();
0225     QRect visualRect = m_view->visualRect(m_index);
0226     rect.translate(visualRect.topLeft());
0227 
0228     m_widget->setFixedSize(rect.width() + 1, rect.height() + 1);
0229     m_widget->move(rect.topLeft());
0230 }
0231 
0232 bool ImportDownloadOverlay::checkIndex(const QModelIndex& index) const
0233 {
0234     CamItemInfo info = ImportItemModel::retrieveCamItemInfo(index);
0235 
0236     if (info.downloaded == CamItemInfo::DownloadUnknown)
0237     {
0238         m_widget->setToolTip(i18nc("@info:tooltip", "This item has an unknown download status"));
0239         return true;
0240     }
0241 
0242     if (info.downloaded == CamItemInfo::DownloadedNo) // TODO: CamItemInfo::NewPicture
0243     {
0244         m_widget->setToolTip(i18nc("@info:tooltip", "This item has never been downloaded"));
0245         return true;
0246     }
0247 
0248     if (info.downloaded == CamItemInfo::DownloadedYes)
0249     {
0250         m_widget->setToolTip(i18nc("@info:tooltip", "This item has already been downloaded"));
0251         return true;
0252     }
0253 
0254     return false;
0255 }
0256 
0257 void ImportDownloadOverlay::slotEntered(const QModelIndex& index)
0258 {
0259     AbstractWidgetDelegateOverlay::slotEntered(index);
0260     m_index = index;
0261     updatePosition();
0262 }
0263 
0264 // -- Rating Overlay ------------------------------------------------------------------
0265 
0266 ImportRatingOverlay::ImportRatingOverlay(QObject* const parent)
0267     : AbstractWidgetDelegateOverlay(parent)
0268 {
0269 }
0270 
0271 RatingWidget* ImportRatingOverlay::ratingWidget() const
0272 {
0273     return static_cast<RatingWidget*>(m_widget);
0274 }
0275 
0276 QWidget* ImportRatingOverlay::createWidget()
0277 {
0278     RatingWidget* const w = new RatingWidget(parentWidget());
0279     w->setFading(true);
0280     w->setTracking(false);
0281 
0282     return w;
0283 }
0284 
0285 void ImportRatingOverlay::setActive(bool active)
0286 {
0287     AbstractWidgetDelegateOverlay::setActive(active);
0288 
0289     if (active)
0290     {
0291         connect(ratingWidget(), SIGNAL(signalRatingChanged(int)),
0292                 this, SLOT(slotRatingChanged(int)));
0293 
0294         if (view()->model())
0295         {
0296             connect(view()->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0297                     this, SLOT(slotDataChanged(QModelIndex,QModelIndex)));
0298         }
0299     }
0300     else
0301     {
0302         // widget is deleted
0303 
0304         if (view() && view()->model())
0305         {
0306             disconnect(view()->model(), nullptr, this, nullptr);
0307         }
0308     }
0309 }
0310 
0311 void ImportRatingOverlay::visualChange()
0312 {
0313     if (m_widget && m_widget->isVisible())
0314     {
0315         updatePosition();
0316     }
0317 }
0318 
0319 void ImportRatingOverlay::widgetEnterEvent()
0320 {
0321     widgetEnterNotifyMultiple(m_index);
0322 }
0323 
0324 void ImportRatingOverlay::widgetLeaveEvent()
0325 {
0326     widgetLeaveNotifyMultiple();
0327 }
0328 
0329 void ImportRatingOverlay::hide()
0330 {
0331     delegate()->setRatingEdited(QModelIndex());
0332     AbstractWidgetDelegateOverlay::hide();
0333 }
0334 
0335 void ImportRatingOverlay::updatePosition()
0336 {
0337     if (!m_index.isValid() || !m_widget)
0338     {
0339         return;
0340     }
0341 
0342     QRect rect = delegate()->ratingRect();
0343 
0344     if (rect.width() > ratingWidget()->maximumVisibleWidth())
0345     {
0346         int offset = (rect.width() - ratingWidget()->maximumVisibleWidth()) / 2;
0347         rect.adjust(offset, 0, -offset, 0);
0348     }
0349 
0350     QRect visualRect = m_view->visualRect(m_index);
0351     rect.translate(visualRect.topLeft());
0352 
0353     m_widget->setFixedSize(rect.width() + 1, rect.height() + 1);
0354     m_widget->move(rect.topLeft());
0355 }
0356 
0357 void ImportRatingOverlay::updateRating()
0358 {
0359     if (!m_index.isValid() || !m_widget)
0360     {
0361         return;
0362     }
0363 
0364     ImportItemModel* const model = m_index.data(ImportItemModel::ImportItemModelPointerRole).value<ImportItemModel*>();
0365     ratingWidget()->setRating(model->camItemInfoRef(m_index).rating);
0366 }
0367 
0368 void ImportRatingOverlay::slotRatingChanged(int rating)
0369 {
0370     if (m_widget && m_widget->isVisible() && m_index.isValid())
0371     {
0372         Q_EMIT ratingEdited(affectedIndexes(m_index), rating);
0373     }
0374 }
0375 
0376 void ImportRatingOverlay::slotEntered(const QModelIndex& index)
0377 {
0378     AbstractWidgetDelegateOverlay::slotEntered(index);
0379 
0380     // see bug 228810, this is a small workaround
0381 
0382     if (m_widget && m_widget->isVisible() && m_index.isValid() && (index == m_index))
0383     {
0384         ratingWidget()->setVisibleImmediately();
0385     }
0386 
0387     m_index = index;
0388 
0389     updatePosition();
0390     updateRating();
0391 
0392     delegate()->setRatingEdited(m_index);
0393     view()->update(m_index);
0394 }
0395 
0396 void ImportRatingOverlay::slotDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
0397 {
0398     if (m_widget && m_widget->isVisible() && QItemSelectionRange(topLeft, bottomRight).contains(m_index))
0399     {
0400         updateRating();
0401     }
0402 }
0403 
0404 // -- Rotate Overlay ----------------------------------------------------------------
0405 
0406 ImportRotateOverlayButton::ImportRotateOverlayButton(ImportRotateOverlayDirection dir, QAbstractItemView* const parentView)
0407     : ItemViewHoverButton(parentView),
0408       m_direction        (dir)
0409 {
0410     setup();
0411 }
0412 
0413 QSize ImportRotateOverlayButton::sizeHint() const
0414 {
0415     return QSize(32, 32);
0416 }
0417 
0418 QIcon ImportRotateOverlayButton::icon()
0419 {
0420     if (m_direction == ImportRotateOverlayLeft)
0421     {
0422         return QIcon::fromTheme(QLatin1String("object-rotate-left"));
0423     }
0424     else
0425     {
0426         return QIcon::fromTheme(QLatin1String("object-rotate-right"));
0427     }
0428 }
0429 
0430 void ImportRotateOverlayButton::updateToolTip()
0431 {
0432     if (m_direction == ImportRotateOverlayLeft)
0433     {
0434         setToolTip(i18nc("@info:tooltip", "Rotate Left"));
0435     }
0436     else
0437     {
0438         setToolTip(i18nc("@info:tooltip", "Rotate Right"));
0439     }
0440 }
0441 
0442 // --------------------------------------------------------------------
0443 
0444 ImportRotateOverlay::ImportRotateOverlay(ImportRotateOverlayDirection dir, QObject* const parent)
0445     : HoverButtonDelegateOverlay(parent),
0446       m_direction(dir)
0447 {
0448 }
0449 
0450 void ImportRotateOverlay::setActive(bool active)
0451 {
0452     HoverButtonDelegateOverlay::setActive(active);
0453 
0454     if (active)
0455     {
0456         connect(button(), SIGNAL(clicked(bool)),
0457                 this, SLOT(slotClicked()));
0458     }
0459 }
0460 
0461 ItemViewHoverButton* ImportRotateOverlay::createButton()
0462 {
0463     return new ImportRotateOverlayButton(m_direction, view());
0464 }
0465 
0466 void ImportRotateOverlay::updateButton(const QModelIndex& index)
0467 {
0468     const QRect rect = m_view->visualRect(index);
0469     const int size   = qBound(16, rect.width() / 8 - 2, 48);
0470     const int gap    = 5;
0471     const int x      = rect.right() - 2*gap - (isLeft() ? size*5 + 2 : size*4 + 2);
0472     const int y      = rect.top() + gap;
0473     button()->resize(size, size);
0474     button()->move(QPoint(x, y));
0475 }
0476 
0477 void ImportRotateOverlay::slotClicked()
0478 {
0479     QModelIndex index = button()->index();
0480 
0481     if (index.isValid())
0482     {
0483         Q_EMIT signalRotate(affectedIndexes(index));
0484     }
0485 }
0486 
0487 bool ImportRotateOverlay::checkIndex(const QModelIndex& index) const
0488 {
0489     CamItemInfo info = ImportItemModel::retrieveCamItemInfo(index);
0490     return (info.mime.contains(QLatin1String("image/")));
0491 }
0492 
0493 void ImportRotateOverlay::widgetEnterEvent()
0494 {
0495     widgetEnterNotifyMultiple(button()->index());
0496 }
0497 
0498 void ImportRotateOverlay::widgetLeaveEvent()
0499 {
0500     widgetLeaveNotifyMultiple();
0501 }
0502 
0503 } // namespace Digikam
0504 
0505 #include "moc_importoverlays.cpp"