File indexing completed on 2024-04-28 15:39:46

0001 // SPDX-FileCopyrightText: 2009 Laurent Montel <montel@kde.org>
0002 // SPDX-FileCopyrightText: 2009-2010 Hassan Ibraheem <hasan.ibraheem@gmail.com>
0003 // SPDX-FileCopyrightText: 2010 Tuomas Suutari <tuomas@nepnep.net>
0004 // SPDX-FileCopyrightText: 2010-2022 Jesper K. Pedersen <jesper.pedersen@kdab.com>
0005 // SPDX-FileCopyrightText: 2013-2024 Johannes Zarl-Zierl <johannes@zarl-zierl.at>
0006 // SPDX-FileCopyrightText: 2014-2019 Tobias Leupold <tl@stonemx.de>
0007 //
0008 // SPDX-License-Identifier: GPL-2.0-or-later
0009 
0010 #include "ImagePreviewWidget.h"
0011 
0012 #include <DB/ImageDB.h>
0013 #include <DB/ImageInfo.h>
0014 #include <MainWindow/DeleteDialog.h>
0015 
0016 #include <KActionCollection>
0017 #include <KLocalizedString>
0018 #include <QApplication>
0019 #include <QCheckBox>
0020 #include <QComboBox>
0021 #include <QHBoxLayout>
0022 #include <QPushButton>
0023 #include <QVBoxLayout>
0024 #include <QWidget>
0025 
0026 using namespace AnnotationDialog;
0027 
0028 ImagePreviewWidget::ImagePreviewWidget(KActionCollection *actions)
0029     : QWidget()
0030     , m_singleEdit(false)
0031     , m_actions(actions)
0032 {
0033     Q_ASSERT(actions);
0034     QVBoxLayout *layout = new QVBoxLayout(this);
0035     m_preview = new ImagePreview(this);
0036     layout->addWidget(m_preview, 1);
0037     connect(this, &ImagePreviewWidget::areaVisibilityChanged, m_preview, &ImagePreview::setAreaCreationEnabled);
0038 
0039     m_controlWidget = new QWidget;
0040     layout->addWidget(m_controlWidget);
0041     QVBoxLayout *controlLayout = new QVBoxLayout(m_controlWidget);
0042     QHBoxLayout *controlButtonsLayout = new QHBoxLayout;
0043     controlLayout->addLayout(controlButtonsLayout);
0044     controlButtonsLayout->addStretch(1);
0045 
0046     m_prevBut = new QPushButton(this);
0047     m_prevBut->setIcon(QIcon::fromTheme(QString::fromLatin1("arrow-left")));
0048     m_prevBut->setFixedWidth(40);
0049     controlButtonsLayout->addWidget(m_prevBut);
0050     m_prevBut->setToolTip(i18n("Annotate previous image (Page Up)"));
0051 
0052     m_nextBut = new QPushButton(this);
0053     m_nextBut->setIcon(QIcon::fromTheme(QString::fromLatin1("arrow-right")));
0054     m_nextBut->setFixedWidth(40);
0055     controlButtonsLayout->addWidget(m_nextBut);
0056     m_nextBut->setToolTip(i18n("Annotate next image (Page Down)"));
0057 
0058     controlButtonsLayout->addStretch(1);
0059 
0060     m_toggleFullscreenPreview = new QPushButton;
0061     m_toggleFullscreenPreview->setIcon(QIcon::fromTheme(QString::fromUtf8("file-zoom-in")));
0062     m_toggleFullscreenPreview->setFixedWidth(40);
0063 
0064     controlButtonsLayout->addWidget(m_toggleFullscreenPreview);
0065     connect(m_toggleFullscreenPreview, &QPushButton::clicked, this, &ImagePreviewWidget::toggleFullscreenPreview);
0066 
0067     m_rotateLeft = new QPushButton(this);
0068     controlButtonsLayout->addWidget(m_rotateLeft);
0069     m_rotateLeft->setIcon(QIcon::fromTheme(QString::fromLatin1("object-rotate-left")));
0070     m_rotateLeft->setFixedWidth(40);
0071 
0072     m_rotateRight = new QPushButton(this);
0073     controlButtonsLayout->addWidget(m_rotateRight);
0074     m_rotateRight->setIcon(QIcon::fromTheme(QString::fromLatin1("object-rotate-right")));
0075     m_rotateRight->setFixedWidth(40);
0076 
0077     m_copyPreviousBut = new QPushButton(this);
0078     controlButtonsLayout->addWidget(m_copyPreviousBut);
0079     m_copyPreviousBut->setIcon(QIcon::fromTheme(QString::fromLatin1("go-bottom")));
0080     m_copyPreviousBut->setFixedWidth(40);
0081     m_copyPreviousBut->setWhatsThis(i18nc("@info:whatsthis", "<para>Set the same tags on this image than on the previous one. The image date, label, rating, and description are left unchanged.</para>"));
0082 
0083     m_toggleAreasBut = new QPushButton(this);
0084     controlButtonsLayout->addWidget(m_toggleAreasBut);
0085     m_toggleAreasBut->setIcon(QIcon::fromTheme(QString::fromLatin1("document-preview")));
0086     m_toggleAreasBut->setFixedWidth(40);
0087     m_toggleAreasBut->setCheckable(true);
0088     m_toggleAreasBut->setChecked(true);
0089     // tooltip text is set in updateTexts()
0090 
0091     controlButtonsLayout->addStretch(1);
0092     m_delBut = new QPushButton(this);
0093     m_delBut->setIcon(QIcon::fromTheme(QString::fromLatin1("edit-delete")));
0094     controlButtonsLayout->addWidget(m_delBut);
0095     m_delBut->setAutoDefault(false);
0096 
0097     controlButtonsLayout->addStretch(1);
0098 
0099     connect(m_copyPreviousBut, &QPushButton::clicked, this, &ImagePreviewWidget::slotCopyPrevious);
0100     connect(m_delBut, &QPushButton::clicked, this, &ImagePreviewWidget::slotDeleteImage);
0101     connect(m_nextBut, &QPushButton::clicked, this, &ImagePreviewWidget::slotNext);
0102     connect(m_prevBut, &QPushButton::clicked, this, &ImagePreviewWidget::slotPrev);
0103     connect(m_rotateLeft, &QPushButton::clicked, this, &ImagePreviewWidget::rotateLeft);
0104     connect(m_rotateRight, &QPushButton::clicked, this, &ImagePreviewWidget::rotateRight);
0105     connect(m_toggleAreasBut, &QPushButton::clicked, this, &ImagePreviewWidget::slotShowAreas);
0106 
0107     QHBoxLayout *defaultAreaCategoryLayout = new QHBoxLayout;
0108     controlLayout->addLayout(defaultAreaCategoryLayout);
0109     m_defaultAreaCategoryLabel = new QLabel(i18n("Category for new areas:"));
0110     m_defaultAreaCategoryLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
0111     defaultAreaCategoryLayout->addWidget(m_defaultAreaCategoryLabel);
0112     m_defaultAreaCategory = new QComboBox(this);
0113     defaultAreaCategoryLayout->addWidget(m_defaultAreaCategory);
0114 
0115     m_current = -1;
0116     updateTexts();
0117 }
0118 
0119 void ImagePreviewWidget::updatePositionableCategories(QList<QString> positionableCategories)
0120 {
0121     if (positionableCategories.size() <= 1) {
0122         m_defaultAreaCategoryLabel->hide();
0123         m_defaultAreaCategory->hide();
0124     } else {
0125         m_defaultAreaCategoryLabel->show();
0126         m_defaultAreaCategory->show();
0127     }
0128 
0129     m_defaultAreaCategory->clear();
0130 
0131     for (const QString &categoryName : positionableCategories) {
0132         m_defaultAreaCategory->addItem(categoryName);
0133     }
0134 }
0135 
0136 QString ImagePreviewWidget::defaultPositionableCategory() const
0137 {
0138     return m_defaultAreaCategory->currentText();
0139 }
0140 
0141 int ImagePreviewWidget::angle() const
0142 {
0143     return m_preview->angle();
0144 }
0145 
0146 void ImagePreviewWidget::anticipate(DB::ImageInfo &info1)
0147 {
0148     m_preview->anticipate(info1);
0149 }
0150 
0151 void ImagePreviewWidget::configure(QList<DB::ImageInfo> *imageList, bool singleEdit)
0152 {
0153     m_imageList = imageList;
0154     m_current = 0;
0155     setImage(m_imageList->at(m_current));
0156     m_singleEdit = singleEdit;
0157 
0158     m_delBut->setEnabled(m_singleEdit);
0159     m_copyPreviousBut->setEnabled(m_singleEdit);
0160     m_rotateLeft->setEnabled(m_singleEdit);
0161     m_rotateRight->setEnabled(m_singleEdit);
0162 }
0163 
0164 void ImagePreviewWidget::reconfigure(QList<DB::ImageInfo> *imageList, int index)
0165 {
0166     m_imageList = imageList;
0167     m_current = index;
0168     setImage(m_imageList->at(m_current));
0169 }
0170 
0171 void ImagePreviewWidget::slotPrev()
0172 {
0173     if ((m_current <= 0))
0174         return;
0175 
0176     m_current--;
0177     if (m_current != 0)
0178         m_preview->anticipate((*m_imageList)[m_current - 1]);
0179     setImage(m_imageList->at(m_current));
0180 
0181     Q_EMIT indexChanged(m_current);
0182 }
0183 
0184 void ImagePreviewWidget::slotNext()
0185 {
0186     if ((m_current == -1) || (m_current == (int)m_imageList->count() - 1))
0187         return;
0188 
0189     m_current++;
0190 
0191     if (m_current != (int)m_imageList->count() - 1)
0192         m_preview->anticipate((*m_imageList)[m_current + 1]);
0193     setImage(m_imageList->at(m_current));
0194 
0195     Q_EMIT indexChanged(m_current);
0196 }
0197 
0198 void ImagePreviewWidget::slotCopyPrevious()
0199 {
0200     Q_EMIT copyPrevClicked();
0201 }
0202 
0203 void ImagePreviewWidget::rotateLeft()
0204 {
0205     rotate(-90);
0206 }
0207 
0208 void ImagePreviewWidget::rotateRight()
0209 {
0210     rotate(90);
0211 }
0212 
0213 void ImagePreviewWidget::rotate(int angle)
0214 {
0215     if (!m_singleEdit)
0216         return;
0217 
0218     m_preview->rotate(angle);
0219 
0220     Q_EMIT imageRotated(angle);
0221 }
0222 
0223 void ImagePreviewWidget::slotDeleteImage()
0224 {
0225     if (!m_singleEdit)
0226         return;
0227 
0228     MainWindow::DeleteDialog dialog(this);
0229     DB::ImageInfo info = m_imageList->at(m_current);
0230 
0231     const DB::FileNameList deleteList = DB::FileNameList() << info.fileName();
0232 
0233     int ret = dialog.exec(deleteList);
0234     if (ret == QDialog::Rejected) // Delete Dialog rejected, do nothing
0235         return;
0236 }
0237 
0238 void ImagePreviewWidget::setImage(const DB::ImageInfo &info)
0239 {
0240     m_nextBut->setEnabled(m_current != (int)m_imageList->count() - 1);
0241     m_prevBut->setEnabled(m_current != 0);
0242     m_copyPreviousBut->setEnabled(m_current != 0 && m_singleEdit);
0243 
0244     m_preview->setImage(info);
0245 
0246     Q_EMIT imageChanged(info);
0247 }
0248 
0249 void ImagePreviewWidget::setImage(const int index)
0250 {
0251     m_current = index;
0252 
0253     setImage(m_imageList->at(m_current));
0254 }
0255 
0256 void ImagePreviewWidget::setImage(const QString &fileName)
0257 {
0258     m_preview->setImage(fileName);
0259     m_current = -1;
0260 
0261     m_nextBut->setEnabled(false);
0262     m_prevBut->setEnabled(false);
0263     m_rotateLeft->setEnabled(false);
0264     m_rotateRight->setEnabled(false);
0265     m_delBut->setEnabled(false);
0266     m_copyPreviousBut->setEnabled(false);
0267 }
0268 
0269 ImagePreview *ImagePreviewWidget::preview() const
0270 {
0271     return m_preview;
0272 }
0273 
0274 void ImagePreviewWidget::slotShowAreas(bool show)
0275 {
0276     // slot can be triggered by something else than the button:
0277     m_toggleAreasBut->setChecked(show);
0278 
0279     Q_EMIT areaVisibilityChanged(show);
0280 }
0281 
0282 bool ImagePreviewWidget::showAreas() const
0283 {
0284     return m_toggleAreasBut->isChecked();
0285 }
0286 
0287 void ImagePreviewWidget::canCreateAreas(bool state)
0288 {
0289     if (m_toggleAreasBut->isEnabled() != state) {
0290         m_toggleAreasBut->setChecked(state);
0291         m_toggleAreasBut->setEnabled(state);
0292         Q_EMIT areaVisibilityChanged(state);
0293     }
0294     m_preview->setAreaCreationEnabled(state);
0295     updateTexts();
0296 }
0297 
0298 void ImagePreviewWidget::updateTexts()
0299 {
0300     if (m_toggleAreasBut->isEnabled()) {
0301         // positionable tags enabled
0302         m_toggleAreasBut->setToolTip(i18nc("@info:tooltip", "Hide or show areas on the image"));
0303     } else {
0304         if (m_singleEdit) {
0305             // positionable tags disabled
0306             m_toggleAreasBut->setToolTip(i18nc("@info:tooltip",
0307                                                "If you enable <emphasis>positionable tags</emphasis> for at least one category in "
0308                                                "<interface>Settings|Configure KPhotoAlbum...|Categories</interface>, you can "
0309                                                "associate specific image areas with tags."));
0310         } else {
0311             m_toggleAreasBut->setToolTip(i18nc("@info:tooltip",
0312                                                "Areas on an image can only be shown in single-image annotation mode."));
0313         }
0314     }
0315 }
0316 
0317 void ImagePreviewWidget::setSearchMode(bool state)
0318 {
0319     m_controlWidget->setVisible(!state);
0320 }
0321 
0322 void ImagePreviewWidget::toggleFullscreenPreview()
0323 {
0324     Q_EMIT togglePreview();
0325 }
0326 
0327 void ImagePreviewWidget::setToggleFullscreenPreviewEnabled(bool state)
0328 {
0329     m_toggleFullscreenPreview->setEnabled(state);
0330 }
0331 
0332 void AnnotationDialog::ImagePreviewWidget::showEvent(QShowEvent *)
0333 {
0334     auto setToolTip = [this](QWidget *widget, QString tooltip, const char *key) {
0335         const auto action = m_actions->action(QString::fromLatin1(key));
0336         QKeySequence shortcut = action ? action->shortcut() : QKeySequence();
0337         if (!shortcut.isEmpty())
0338             tooltip += QString::fromLatin1(" (%1)").arg(shortcut.toString());
0339         widget->setToolTip(tooltip);
0340     };
0341 
0342     setToolTip(m_toggleFullscreenPreview, i18nc("@info:tooltip", "Toggle full-screen preview"), "annotationdialog-toggle-viewer");
0343     setToolTip(m_rotateLeft, i18nc("@info:tooltip", "Rotate counterclockwise"), "annotationdialog-rotate-left");
0344     setToolTip(m_rotateRight, i18nc("@info:tooltip", "Rotate clockwise"), "annotationdialog-rotate-right");
0345     setToolTip(m_copyPreviousBut, i18nc("@info:tooltip", "Copy tags from previously tagged image"), "annotationdialog-copy-previous");
0346     setToolTip(m_delBut, i18nc("@info:tooltip", "Delete image"), "annotationdialog-delete-image");
0347 }
0348 // vi:expandtab:tabstop=4 shiftwidth=4:
0349 
0350 #include "moc_ImagePreviewWidget.cpp"