File indexing completed on 2025-01-19 03:51:20
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2011-03-14 0007 * Description : a dialog to edit EXIF,IPTC and XMP metadata 0008 * 0009 * SPDX-FileCopyrightText: 2011 by Victor Dodon <dodon dot victor at gmail dot com> 0010 * SPDX-FileCopyrightText: 2006-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 "metadataeditdialog.h" 0017 0018 // Qt includes 0019 0020 #include <QKeyEvent> 0021 #include <QPointer> 0022 #include <QObject> 0023 #include <QApplication> 0024 #include <QMenu> 0025 #include <QTabWidget> 0026 #include <QDialogButtonBox> 0027 #include <QPushButton> 0028 #include <QVBoxLayout> 0029 #include <QImage> 0030 #include <QBuffer> 0031 #include <QPainter> 0032 #include <QPalette> 0033 #include <QIcon> 0034 0035 // KDE includes 0036 0037 #include <ksharedconfig.h> 0038 #include <kconfiggroup.h> 0039 #include <klocalizedstring.h> 0040 0041 // Local includes 0042 0043 #include "exifeditwidget.h" 0044 #include "iptceditwidget.h" 0045 #include "xmpeditwidget.h" 0046 #include "thumbnailloadthread.h" 0047 #include "dxmlguiwindow.h" 0048 0049 namespace DigikamGenericMetadataEditPlugin 0050 { 0051 0052 class Q_DECL_HIDDEN MetadataEditDialog::Private 0053 { 0054 public: 0055 0056 explicit Private() 0057 : isReadOnly (false), 0058 tabWidget (nullptr), 0059 tabExif (nullptr), 0060 tabIptc (nullptr), 0061 tabXmp (nullptr), 0062 catcher (nullptr), 0063 iface (nullptr) 0064 { 0065 } 0066 0067 bool isReadOnly; 0068 0069 QString preview; 0070 0071 QList<QUrl> urls; 0072 QList<QUrl>::iterator currItem; 0073 0074 QTabWidget* tabWidget; 0075 0076 EXIFEditWidget* tabExif; 0077 IPTCEditWidget* tabIptc; 0078 XMPEditWidget* tabXmp; 0079 0080 ThumbnailImageCatcher* catcher; 0081 0082 DInfoInterface* iface; 0083 }; 0084 0085 MetadataEditDialog::MetadataEditDialog(QWidget* const parent, DInfoInterface* const iface) 0086 : DPluginDialog(parent, QLatin1String("Metadata Edit Dialog")), 0087 d (new Private) 0088 { 0089 d->iface = iface; 0090 0091 setWindowTitle(i18nc("@title:window", "Metadata Editor")); 0092 setModal(true); 0093 0094 ThumbnailLoadThread* const thread = new ThumbnailLoadThread; 0095 thread->setThumbnailSize(48); 0096 thread->setPixmapRequested(false); 0097 d->catcher = new ThumbnailImageCatcher(thread, this); 0098 0099 d->urls = d->iface->currentSelectedItems(); 0100 d->currItem = d->urls.begin(); 0101 updatePreview(); 0102 0103 QDialogButtonBox::StandardButtons btns = QDialogButtonBox::Ok | 0104 QDialogButtonBox::Apply | 0105 QDialogButtonBox::Close | 0106 QDialogButtonBox::No | // NextPrevious item 0107 QDialogButtonBox::Yes; // Previous item 0108 0109 m_buttons = new QDialogButtonBox(btns, this); 0110 m_buttons->button(QDialogButtonBox::Ok)->setDefault(true); 0111 m_buttons->button(QDialogButtonBox::Apply)->setEnabled(false); 0112 m_buttons->button(QDialogButtonBox::No)->setText(i18nc("@action: button", "Next")); 0113 m_buttons->button(QDialogButtonBox::No)->setIcon(QIcon::fromTheme(QLatin1String("go-next"))); 0114 m_buttons->button(QDialogButtonBox::Yes)->setText(i18nc("@action: button", "Previous")); 0115 m_buttons->button(QDialogButtonBox::Yes)->setIcon(QIcon::fromTheme(QLatin1String("go-previous"))); 0116 0117 if (d->urls.count() <= 1) 0118 { 0119 m_buttons->button(QDialogButtonBox::No)->setDisabled(true); 0120 m_buttons->button(QDialogButtonBox::Yes)->setDisabled(true); 0121 } 0122 0123 d->tabWidget = new QTabWidget(this); 0124 d->tabExif = new EXIFEditWidget(this); 0125 d->tabIptc = new IPTCEditWidget(this); 0126 d->tabXmp = new XMPEditWidget(this); 0127 d->tabWidget->addTab(d->tabExif, i18nc("@item", "Edit EXIF")); 0128 d->tabWidget->addTab(d->tabIptc, i18nc("@item", "Edit IPTC")); 0129 d->tabWidget->addTab(d->tabXmp, i18nc("@item", "Edit XMP")); 0130 0131 QVBoxLayout* const vbx = new QVBoxLayout(this); 0132 vbx->addWidget(d->tabWidget); 0133 vbx->addWidget(m_buttons); 0134 setLayout(vbx); 0135 0136 //---------------------------------------------------------- 0137 0138 connect(d->tabExif, SIGNAL(signalModified()), 0139 this, SLOT(slotModified())); 0140 0141 connect(d->tabIptc, SIGNAL(signalModified()), 0142 this, SLOT(slotModified())); 0143 0144 connect(d->tabXmp, SIGNAL(signalModified()), 0145 this, SLOT(slotModified())); 0146 0147 connect(d->tabExif, SIGNAL(signalSetReadOnly(bool)), 0148 this, SLOT(slotSetReadOnly(bool))); 0149 0150 connect(d->tabIptc, SIGNAL(signalSetReadOnly(bool)), 0151 this, SLOT(slotSetReadOnly(bool))); 0152 0153 connect(d->tabXmp, SIGNAL(signalSetReadOnly(bool)), 0154 this, SLOT(slotSetReadOnly(bool))); 0155 0156 connect(m_buttons->button(QDialogButtonBox::Apply), SIGNAL(clicked()), 0157 this, SLOT(slotApply())); 0158 0159 connect(m_buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), 0160 this, SLOT(slotOk())); 0161 0162 connect(m_buttons->button(QDialogButtonBox::Close), SIGNAL(clicked()), 0163 this, SLOT(slotClose())); 0164 0165 connect(m_buttons->button(QDialogButtonBox::No), SIGNAL(clicked()), 0166 this, SLOT(slotNext())); 0167 0168 connect(m_buttons->button(QDialogButtonBox::Yes), SIGNAL(clicked()), 0169 this, SLOT(slotPrevious())); 0170 0171 connect(this, SIGNAL(signalMetadataChangedForUrl(QUrl)), 0172 d->iface, SLOT(slotMetadataChangedForUrl(QUrl))); 0173 0174 //---------------------------------------------------------- 0175 0176 readSettings(); 0177 slotItemChanged(); 0178 } 0179 0180 MetadataEditDialog::~MetadataEditDialog() 0181 { 0182 d->catcher->thread()->stopAllTasks(); 0183 d->catcher->cancel(); 0184 0185 delete d->catcher->thread(); 0186 delete d->catcher; 0187 delete d; 0188 } 0189 0190 QString MetadataEditDialog::currentItemTitleHeader(const QString& title) const 0191 { 0192 QString start = QLatin1String("<qt><table cellspacing=\"0\" cellpadding=\"0\" width=\"250\" border=\"0\">"); 0193 QString end = QLatin1String("</table></qt>"); 0194 0195 return QString::fromLatin1("%1<tr><td>%2</td><td>%3</td></tr>%4").arg(start).arg(d->preview).arg(title).arg(end); 0196 } 0197 0198 void MetadataEditDialog::updatePreview() 0199 { 0200 d->catcher->setActive(true); 0201 0202 d->catcher->thread()->find(ThumbnailIdentifier(d->currItem->toLocalFile())); 0203 d->catcher->enqueue(); 0204 QList<QImage> images = d->catcher->waitForThumbnails(); 0205 0206 QImage img(48, 48, QImage::Format_ARGB32); 0207 QImage thm = images.first(); 0208 QPainter p(&img); 0209 p.fillRect(img.rect(), QPalette().window()); 0210 p.setPen(Qt::black); 0211 p.drawRect(img.rect().left(), img.rect().top(), img.rect().right()-1, img.rect().bottom()-1); 0212 p.drawImage((img.width() - thm.width())/2, (img.height() - thm.height())/2, thm); 0213 0214 QByteArray byteArray; 0215 QBuffer buffer(&byteArray); 0216 img.save(&buffer, "PNG"); 0217 d->preview = QString::fromLatin1("<img src=\"data:image/png;base64,%1\"> ").arg(QLatin1String(byteArray.toBase64().data())); 0218 0219 d->catcher->setActive(false); 0220 } 0221 0222 QList<QUrl>::iterator MetadataEditDialog::currentItem() const 0223 { 0224 return d->currItem; 0225 } 0226 0227 void MetadataEditDialog::slotModified() 0228 { 0229 bool modified = false; 0230 0231 switch (d->tabWidget->currentIndex()) 0232 { 0233 case 0: 0234 modified = d->tabExif->isModified(); 0235 break; 0236 0237 case 1: 0238 modified = d->tabIptc->isModified(); 0239 break; 0240 0241 case 2: 0242 modified = d->tabXmp->isModified(); 0243 break; 0244 } 0245 0246 m_buttons->button(QDialogButtonBox::Apply)->setEnabled(modified); 0247 } 0248 0249 void MetadataEditDialog::slotOk() 0250 { 0251 slotApply(); 0252 saveSettings(); 0253 accept(); 0254 } 0255 0256 void MetadataEditDialog::slotClose() 0257 { 0258 saveSettings(); 0259 close(); 0260 } 0261 0262 void MetadataEditDialog::slotApply() 0263 { 0264 d->tabExif->apply(); 0265 d->tabIptc->apply(); 0266 d->tabXmp->apply(); 0267 Q_EMIT signalMetadataChangedForUrl(*d->currItem); 0268 slotItemChanged(); 0269 } 0270 0271 void MetadataEditDialog::slotNext() 0272 { 0273 slotApply(); 0274 ++d->currItem; 0275 slotItemChanged(); 0276 } 0277 0278 void MetadataEditDialog::slotPrevious() 0279 { 0280 slotApply(); 0281 --d->currItem; 0282 slotItemChanged(); 0283 } 0284 0285 void MetadataEditDialog::readSettings() 0286 { 0287 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0288 KConfigGroup group = config->group(QLatin1String("Metadata Edit Dialog")); 0289 d->tabWidget->setCurrentIndex(group.readEntry(QLatin1String("Tab Index"), 0)); 0290 } 0291 0292 void MetadataEditDialog::saveSettings() 0293 { 0294 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0295 KConfigGroup group = config->group(QLatin1String("Metadata Edit Dialog")); 0296 group.writeEntry(QLatin1String("Tab Index"), d->tabWidget->currentIndex()); 0297 0298 d->tabExif->saveSettings(); 0299 d->tabIptc->saveSettings(); 0300 d->tabXmp->saveSettings(); 0301 } 0302 0303 void MetadataEditDialog::slotItemChanged() 0304 { 0305 updatePreview(); 0306 d->tabExif->slotItemChanged(); 0307 d->tabIptc->slotItemChanged(); 0308 d->tabXmp->slotItemChanged(); 0309 0310 setWindowTitle(i18nc("@title:window", "%1 (%2/%3) - Edit Metadata", 0311 (*d->currItem).fileName(), 0312 d->urls.indexOf(*(d->currItem))+1, 0313 d->urls.count())); 0314 0315 m_buttons->button(QDialogButtonBox::No)->setEnabled(*(d->currItem) != d->urls.last()); 0316 m_buttons->button(QDialogButtonBox::Yes)->setEnabled(*(d->currItem) != d->urls.first()); 0317 m_buttons->button(QDialogButtonBox::Apply)->setEnabled(!d->isReadOnly); 0318 } 0319 0320 bool MetadataEditDialog::eventFilter(QObject*, QEvent* e) 0321 { 0322 if (e->type() == QEvent::KeyPress) 0323 { 0324 QKeyEvent* const k = (QKeyEvent*)e; 0325 0326 if ((k->modifiers() == Qt::ControlModifier) && 0327 ((k->key() == Qt::Key_Enter) || (k->key() == Qt::Key_Return))) 0328 { 0329 slotApply(); 0330 0331 if (m_buttons->button(QDialogButtonBox::No)->isEnabled()) 0332 { 0333 slotNext(); 0334 } 0335 0336 return true; 0337 } 0338 else if ((k->modifiers() == Qt::ShiftModifier) && 0339 ((k->key() == Qt::Key_Enter) || (k->key() == Qt::Key_Return))) 0340 { 0341 slotApply(); 0342 0343 if (m_buttons->button(QDialogButtonBox::Yes)->isEnabled()) 0344 { 0345 slotPrevious(); 0346 } 0347 0348 return true; 0349 } 0350 0351 return false; 0352 } 0353 0354 return false; 0355 } 0356 0357 void MetadataEditDialog::closeEvent(QCloseEvent* e) 0358 { 0359 if (!e) 0360 { 0361 return; 0362 } 0363 0364 saveSettings(); 0365 e->accept(); 0366 } 0367 0368 void MetadataEditDialog::slotSetReadOnly(bool state) 0369 { 0370 d->isReadOnly = state; 0371 } 0372 0373 } // namespace DigikamGenericMetadataEditPlugin 0374 0375 #include "moc_metadataeditdialog.cpp"