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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2006-10-12
0007  * Description : EXIF caption settings page.
0008  *
0009  * SPDX-FileCopyrightText: 2006-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 "exifcaption.h"
0016 
0017 // Qt includes
0018 
0019 #include <QIcon>
0020 #include <QStyle>
0021 #include <QCheckBox>
0022 #include <QGridLayout>
0023 #include <QApplication>
0024 
0025 // KDE includes
0026 
0027 #include <klocalizedstring.h>
0028 
0029 // Local includes
0030 
0031 #include "dtextedit.h"
0032 
0033 namespace DigikamGenericMetadataEditPlugin
0034 {
0035 
0036 class Q_DECL_HIDDEN EXIFCaption::Private
0037 {
0038 public:
0039 
0040     explicit Private()
0041       : documentNameCheck    (nullptr),
0042         imageDescCheck       (nullptr),
0043         artistCheck          (nullptr),
0044         copyrightCheck       (nullptr),
0045         userCommentCheck     (nullptr),
0046         syncJFIFCommentCheck (nullptr),
0047         syncXMPCaptionCheck  (nullptr),
0048         syncIPTCCaptionCheck (nullptr),
0049         userCommentEdit      (nullptr),
0050         documentNameEdit     (nullptr),
0051         imageDescEdit        (nullptr),
0052         artistEdit           (nullptr),
0053         copyrightEdit        (nullptr),
0054         documentNameIcon     (nullptr),
0055         imageDescIcon        (nullptr),
0056         artistIcon           (nullptr),
0057         copyrightIcon        (nullptr)
0058     {
0059     }
0060 
0061     QCheckBox*            documentNameCheck;
0062     QCheckBox*            imageDescCheck;
0063     QCheckBox*            artistCheck;
0064     QCheckBox*            copyrightCheck;
0065     QCheckBox*            userCommentCheck;
0066     QCheckBox*            syncJFIFCommentCheck;
0067     QCheckBox*            syncXMPCaptionCheck;
0068     QCheckBox*            syncIPTCCaptionCheck;
0069 
0070     DPlainTextEdit*       userCommentEdit;
0071 
0072     DTextEdit*            documentNameEdit;
0073     DTextEdit*            imageDescEdit;
0074     DTextEdit*            artistEdit;
0075     DTextEdit*            copyrightEdit;
0076 
0077     QLabel*               documentNameIcon;
0078     QLabel*               imageDescIcon;
0079     QLabel*               artistIcon;
0080     QLabel*               copyrightIcon;
0081 };
0082 
0083 EXIFCaption::EXIFCaption(QWidget* const parent)
0084     : MetadataEditPage(parent),
0085       d               (new Private)
0086 {
0087     QGridLayout* const grid = new QGridLayout(widget());
0088 
0089     QString asciiPrint;     // List of printable ASCII characters.
0090 
0091     for (int i = 32 ; i <= 127 ; ++i)
0092     {
0093         asciiPrint.append(QChar(i));
0094     }
0095 
0096     // --------------------------------------------------------
0097 
0098     d->documentNameCheck = new QCheckBox(i18nc("name of the document this image has been scanned from", "Name (*):"), this);
0099     d->documentNameEdit  = new DTextEdit(this);
0100     d->documentNameEdit->setAcceptedCharacters(asciiPrint);
0101     d->documentNameEdit->setPlaceholderText(i18n("Set here the original document name."));
0102     d->documentNameEdit->setWhatsThis(i18n("Enter the name of the document from which "
0103                                            "this image was been scanned. This field is limited "
0104                                            "to ASCII characters."));
0105 
0106     // --------------------------------------------------------
0107 
0108     d->imageDescCheck = new QCheckBox(i18nc("image description", "Description (*):"), this);
0109     d->imageDescEdit  = new DTextEdit(this);
0110     d->imageDescEdit->setAcceptedCharacters(asciiPrint);
0111     d->imageDescEdit->setPlaceholderText(i18n("Set here the image description."));
0112     d->imageDescEdit->setWhatsThis(i18n("Enter the image description. This field is limited "
0113                                         "to ASCII characters."));
0114 
0115     // --------------------------------------------------------
0116 
0117     d->artistCheck = new QCheckBox(i18n("Artist (*):"), this);
0118     d->artistEdit  = new DTextEdit(this);
0119     d->artistEdit->setAcceptedCharacters(asciiPrint);
0120     d->artistEdit->setPlaceholderText(i18n("Set here the author's name."));
0121     d->artistEdit->setWhatsThis(i18n("Enter the image author's name separated by semi-colons. "
0122                                      "This field is limited to ASCII characters."));
0123 
0124     // --------------------------------------------------------
0125 
0126     d->copyrightCheck = new QCheckBox(i18n("Copyright (*):"), this);
0127     d->copyrightEdit  = new DTextEdit(this);
0128     d->copyrightEdit->setAcceptedCharacters(asciiPrint);
0129     d->copyrightEdit->setPlaceholderText(i18n("Set here the copyright owner."));
0130     d->copyrightEdit->setWhatsThis(i18n("Enter the copyright owner of the image. "
0131                                         "This field is limited to ASCII characters."));
0132 
0133     // --------------------------------------------------------
0134 
0135     d->userCommentCheck = new QCheckBox(i18nc("image caption", "Caption:"), this);
0136     d->userCommentEdit  = new DPlainTextEdit(this);
0137     d->userCommentEdit->setLinesVisible(4);
0138     d->userCommentEdit->setPlaceholderText(i18n("Set here the image's caption."));
0139     d->userCommentEdit->setWhatsThis(i18n("Enter the image's caption. "
0140                                           "This field is not limited. UTF8 encoding "
0141                                           "will be used to save the text."));
0142 
0143     d->syncJFIFCommentCheck = new QCheckBox(i18n("Sync JFIF Comment section"), this);
0144     d->syncXMPCaptionCheck  = new QCheckBox(i18n("Sync XMP caption"), this);
0145     d->syncIPTCCaptionCheck = new QCheckBox(i18n("Sync IPTC caption (warning: limited to 2000 characters)"), this);
0146 
0147     if (!DMetadata::supportXmp())
0148         d->syncXMPCaptionCheck->setEnabled(false);
0149 
0150     // --------------------------------------------------------
0151 
0152     QLabel* const note = new QLabel(i18n("<b>Note:</b> "
0153                  "<b><a href='https://en.wikipedia.org/wiki/EXIF'>Exif</a></b> "
0154                  "text tags marked by (*) only support printable "
0155                  "<b><a href='https://en.wikipedia.org/wiki/Ascii'>ASCII</a></b> "
0156                  "characters."), this);
0157     note->setOpenExternalLinks(true);
0158     note->setWordWrap(true);
0159     note->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
0160 
0161     // --------------------------------------------------------
0162 
0163     grid->addWidget(d->documentNameCheck,       0, 0, 1, 1);
0164     grid->addWidget(d->documentNameEdit,        1, 0, 1, 1);
0165     grid->addWidget(d->imageDescCheck,          2, 0, 1, 1);
0166     grid->addWidget(d->imageDescEdit,           3, 0, 1, 1);
0167     grid->addWidget(d->artistCheck,             4, 0, 1, 1);
0168     grid->addWidget(d->artistEdit,              5, 0, 1, 1);
0169     grid->addWidget(d->copyrightCheck,          6, 0, 1, 1);
0170     grid->addWidget(d->copyrightEdit,           7, 0, 1, 1);
0171     grid->addWidget(d->userCommentCheck,        8, 0, 1, 1);
0172     grid->addWidget(d->userCommentEdit,         9, 0, 1, 1);
0173     grid->addWidget(d->syncJFIFCommentCheck,   10, 0, 1, 1);
0174     grid->addWidget(d->syncXMPCaptionCheck,    11, 0, 1, 1);
0175     grid->addWidget(d->syncIPTCCaptionCheck,   12, 0, 1, 1);
0176     grid->addWidget(note,                      13, 0, 1, 1);
0177     grid->setRowStretch(14, 10);
0178     grid->setColumnStretch(0, 10);
0179 
0180     int spacing = qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing),
0181                        QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing));
0182 
0183     grid->setContentsMargins(spacing, spacing, spacing, spacing);
0184     grid->setSpacing(spacing);
0185 
0186     // --------------------------------------------------------
0187 
0188     connect(d->documentNameCheck, SIGNAL(toggled(bool)),
0189             d->documentNameEdit, SLOT(setEnabled(bool)));
0190 
0191     connect(d->imageDescCheck, SIGNAL(toggled(bool)),
0192             d->imageDescEdit, SLOT(setEnabled(bool)));
0193 
0194     connect(d->artistCheck, SIGNAL(toggled(bool)),
0195             d->artistEdit, SLOT(setEnabled(bool)));
0196 
0197     connect(d->copyrightCheck, SIGNAL(toggled(bool)),
0198             d->copyrightEdit, SLOT(setEnabled(bool)));
0199 
0200     connect(d->userCommentCheck, SIGNAL(toggled(bool)),
0201             d->userCommentEdit, SLOT(setEnabled(bool)));
0202 
0203     connect(d->userCommentCheck, SIGNAL(toggled(bool)),
0204             d->syncJFIFCommentCheck, SLOT(setEnabled(bool)));
0205 
0206     connect(d->userCommentCheck, SIGNAL(toggled(bool)),
0207             d->syncXMPCaptionCheck, SLOT(setEnabled(bool)));
0208 
0209     connect(d->userCommentCheck, SIGNAL(toggled(bool)),
0210             d->syncIPTCCaptionCheck, SLOT(setEnabled(bool)));
0211 
0212     // --------------------------------------------------------
0213 
0214     connect(d->documentNameCheck, SIGNAL(toggled(bool)),
0215             this, SIGNAL(signalModified()));
0216 
0217     connect(d->imageDescCheck, SIGNAL(toggled(bool)),
0218             this, SIGNAL(signalModified()));
0219 
0220     connect(d->artistCheck, SIGNAL(toggled(bool)),
0221             this, SIGNAL(signalModified()));
0222 
0223     connect(d->copyrightCheck, SIGNAL(toggled(bool)),
0224             this, SIGNAL(signalModified()));
0225 
0226     connect(d->userCommentCheck, SIGNAL(toggled(bool)),
0227             this, SIGNAL(signalModified()));
0228 
0229     // --------------------------------------------------------
0230 
0231     connect(d->userCommentEdit, SIGNAL(textChanged()),
0232             this, SIGNAL(signalModified()));
0233 
0234     connect(d->documentNameEdit, SIGNAL(textChanged()),
0235             this, SIGNAL(signalModified()));
0236 
0237     connect(d->imageDescEdit, SIGNAL(textChanged()),
0238             this, SIGNAL(signalModified()));
0239 
0240     connect(d->artistEdit, SIGNAL(textChanged()),
0241             this, SIGNAL(signalModified()));
0242 
0243     connect(d->copyrightEdit, SIGNAL(textChanged()),
0244             this, SIGNAL(signalModified()));
0245 }
0246 
0247 EXIFCaption::~EXIFCaption()
0248 {
0249     delete d;
0250 }
0251 
0252 bool EXIFCaption::syncJFIFCommentIsChecked() const
0253 {
0254     return d->syncJFIFCommentCheck->isChecked();
0255 }
0256 
0257 bool EXIFCaption::syncXMPCaptionIsChecked() const
0258 {
0259     return d->syncXMPCaptionCheck->isChecked();
0260 }
0261 
0262 bool EXIFCaption::syncIPTCCaptionIsChecked() const
0263 {
0264     return d->syncIPTCCaptionCheck->isChecked();
0265 }
0266 
0267 QString EXIFCaption::getEXIFUserComments() const
0268 {
0269     return d->userCommentEdit->toPlainText();
0270 }
0271 
0272 void EXIFCaption::setCheckedSyncJFIFComment(bool c)
0273 {
0274     d->syncJFIFCommentCheck->setChecked(c);
0275 }
0276 
0277 void EXIFCaption::setCheckedSyncXMPCaption(bool c)
0278 {
0279     d->syncXMPCaptionCheck->setChecked(c);
0280 }
0281 
0282 void EXIFCaption::setCheckedSyncIPTCCaption(bool c)
0283 {
0284     d->syncIPTCCaptionCheck->setChecked(c);
0285 }
0286 
0287 void EXIFCaption::readMetadata(const DMetadata& meta)
0288 {
0289     blockSignals(true);
0290 
0291     QString data;
0292 
0293     d->documentNameEdit->clear();
0294     d->documentNameCheck->setChecked(false);
0295     data = meta.getExifTagString("Exif.Image.DocumentName", false);
0296 
0297     if (!data.isNull())
0298     {
0299         d->documentNameEdit->setText(data);
0300         d->documentNameCheck->setChecked(true);
0301     }
0302 
0303     d->documentNameEdit->setEnabled(d->documentNameCheck->isChecked());
0304 
0305     d->imageDescEdit->clear();
0306     d->imageDescCheck->setChecked(false);
0307     data = meta.getExifTagString("Exif.Image.ImageDescription", false);
0308 
0309     if (!data.isNull())
0310     {
0311         d->imageDescEdit->setText(data);
0312         d->imageDescCheck->setChecked(true);
0313     }
0314 
0315     d->imageDescEdit->setEnabled(d->imageDescCheck->isChecked());
0316 
0317     d->artistEdit->clear();
0318     d->artistCheck->setChecked(false);
0319     data = meta.getExifTagString("Exif.Image.Artist", false);
0320 
0321     if (!data.isNull())
0322     {
0323         d->artistEdit->setText(data);
0324         d->artistCheck->setChecked(true);
0325     }
0326 
0327     d->artistEdit->setEnabled(d->artistCheck->isChecked());
0328 
0329     d->copyrightEdit->clear();
0330     d->copyrightCheck->setChecked(false);
0331     data = meta.getExifTagString("Exif.Image.Copyright", false);
0332 
0333     if (!data.isNull())
0334     {
0335         d->copyrightEdit->setText(data);
0336         d->copyrightCheck->setChecked(true);
0337     }
0338 
0339     d->copyrightEdit->setEnabled(d->copyrightCheck->isChecked());
0340 
0341     d->userCommentEdit->clear();
0342     d->userCommentCheck->setChecked(false);
0343     data = meta.getExifComment(false);
0344 
0345     if (!data.isNull())
0346     {
0347         d->userCommentEdit->setPlainText(data);
0348         d->userCommentCheck->setChecked(true);
0349     }
0350 
0351     d->userCommentEdit->setEnabled(d->userCommentCheck->isChecked());
0352     d->syncJFIFCommentCheck->setEnabled(d->userCommentCheck->isChecked());
0353     d->syncXMPCaptionCheck->setEnabled(d->userCommentCheck->isChecked());
0354     d->syncIPTCCaptionCheck->setEnabled(d->userCommentCheck->isChecked());
0355 
0356     blockSignals(false);
0357 }
0358 
0359 void EXIFCaption::applyMetadata(const DMetadata& meta)
0360 {
0361     if (d->documentNameCheck->isChecked())
0362         meta.setExifTagString("Exif.Image.DocumentName", d->documentNameEdit->text());
0363     else
0364         meta.removeExifTag("Exif.Image.DocumentName");
0365 
0366     if (d->imageDescCheck->isChecked())
0367         meta.setExifTagString("Exif.Image.ImageDescription", d->imageDescEdit->text());
0368     else
0369         meta.removeExifTag("Exif.Image.ImageDescription");
0370 
0371     if (d->artistCheck->isChecked())
0372         meta.setExifTagString("Exif.Image.Artist", d->artistEdit->text());
0373     else
0374         meta.removeExifTag("Exif.Image.Artist");
0375 
0376     if (d->copyrightCheck->isChecked())
0377         meta.setExifTagString("Exif.Image.Copyright", d->copyrightEdit->text());
0378     else
0379         meta.removeExifTag("Exif.Image.Copyright");
0380 
0381     if (d->userCommentCheck->isChecked())
0382     {
0383         meta.setExifComment(d->userCommentEdit->toPlainText(), false);
0384 
0385         if (syncJFIFCommentIsChecked())
0386             meta.setComments(d->userCommentEdit->toPlainText().toUtf8());
0387 
0388         if (meta.supportXmp() && syncXMPCaptionIsChecked())
0389         {
0390             meta.setXmpTagStringLangAlt("Xmp.dc.description",
0391                                         d->userCommentEdit->toPlainText(),
0392                                         QString());
0393 
0394             meta.setXmpTagStringLangAlt("Xmp.exif.UserComment",
0395                                         d->userCommentEdit->toPlainText(),
0396                                         QString());
0397         }
0398 
0399         if (syncIPTCCaptionIsChecked())
0400             meta.setIptcTagString("Iptc.Application2.Caption", d->userCommentEdit->toPlainText());
0401     }
0402     else
0403         meta.removeExifTag("Exif.Photo.UserComment");
0404 }
0405 
0406 } // namespace DigikamGenericMetadataEditPlugin
0407 
0408 #include "moc_exifcaption.cpp"