File indexing completed on 2025-04-27 03:58:36

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-06-15
0007  * Description : multi-languages string editor
0008  *
0009  * SPDX-FileCopyrightText: 2009-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 "altlangstredit_p.h"
0016 
0017 namespace Digikam
0018 {
0019 
0020 AltLangStrEdit::AltLangStrEdit(QWidget* const parent, unsigned int lines)
0021     : QWidget(parent),
0022       d      (new Private)
0023 {
0024     d->titleWidget      = new QLabel(this);
0025 
0026     d->delValueButton   = new QToolButton(this);
0027     d->delValueButton->setIcon(QIcon::fromTheme(QLatin1String("edit-clear")));
0028     d->delValueButton->setToolTip(i18nc("@info: language edit widget", "Remove entry for this language"));
0029     d->delValueButton->setEnabled(false);
0030 
0031     d->localizeSelector = new LocalizeSelector(this);
0032     d->trengine         = new DOnlineTranslator(this);
0033 
0034     d->languageCB       = new QComboBox(this);
0035     d->languageCB->setSizeAdjustPolicy(QComboBox::AdjustToContents);
0036     d->languageCB->setWhatsThis(i18nc("@info: language edit widget", "Select item language here."));
0037 
0038     d->valueEdit        = new DTextEdit(lines, this);
0039     d->valueEdit->setAcceptRichText(false);
0040     d->valueEdit->setClearButtonEnabled(false);
0041 
0042     // --------------------------------------------------------
0043 
0044     d->grid             = new QGridLayout(this);
0045     d->grid->setAlignment(Qt::AlignTop);
0046     d->grid->addWidget(d->languageCB,       0, 2, 1,  1);
0047     d->grid->addWidget(d->delValueButton,   0, 3, 1,  1);
0048     d->grid->addWidget(d->localizeSelector, 0, 4, 1,  2);
0049     d->grid->addWidget(d->valueEdit,        1, 0, 1, -1);
0050     d->grid->setColumnStretch(1, 10);
0051     d->grid->setContentsMargins(QMargins());
0052     d->grid->setSpacing(qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing),
0053                              QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)));
0054 
0055     populateLangAltListEntries();
0056 
0057     // --------------------------------------------------------
0058 
0059     connect(d->languageCB, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
0060             this, &AltLangStrEdit::slotSelectionChanged);
0061 
0062     connect(d->delValueButton, &QToolButton::clicked,
0063             this, &AltLangStrEdit::slotDeleteValue);
0064 
0065     connect(d->localizeSelector, &LocalizeSelector::signalTranslate,
0066             this, &AltLangStrEdit::slotTranslate);
0067 
0068     connect(d->valueEdit, &QTextEdit::textChanged,
0069             this, &AltLangStrEdit::slotTextChanged);
0070 
0071     connect(d->trengine, &DOnlineTranslator::signalFinished,
0072             this, &AltLangStrEdit::slotTranslationFinished);
0073 }
0074 
0075 AltLangStrEdit::~AltLangStrEdit()
0076 {
0077     delete d;
0078 }
0079 
0080 QString AltLangStrEdit::languageNameRFC3066(const QString& code)
0081 {
0082     LanguageCodeMap::const_iterator it = s_rfc3066ForXMP.find(code);
0083 
0084     if (it != s_rfc3066ForXMP.end())
0085     {
0086         return it.value().toString();
0087     }
0088 
0089     return QString();
0090 }
0091 
0092 QStringList AltLangStrEdit::allLanguagesRFC3066()
0093 {
0094     return s_rfc3066ForXMP.keys();
0095 }
0096 
0097 void AltLangStrEdit::slotEnabledInternalWidgets(bool b)
0098 {
0099     d->languageCB->setEnabled(b);
0100     d->delValueButton->setEnabled(b);
0101     d->localizeSelector->setEnabled(b);
0102     d->valueEdit->setEnabled(b);
0103 }
0104 
0105 QString AltLangStrEdit::currentLanguageCode() const
0106 {
0107     return d->currentLanguage;
0108 }
0109 
0110 void AltLangStrEdit::setCurrentLanguageCode(const QString& lang)
0111 {
0112     if      (d->currentLanguage.isEmpty())
0113     {
0114         d->currentLanguage = QLatin1String("x-default");
0115     }
0116     else if (!lang.isEmpty())
0117     {
0118         d->currentLanguage = lang;
0119     }
0120 }
0121 
0122 QString AltLangStrEdit::languageCode(int index) const
0123 {
0124     return d->languageCB->itemText(index);
0125 }
0126 
0127 void AltLangStrEdit::setTitle(const QString& title)
0128 {
0129     QLabel* const tlabel = new QLabel(this);
0130     tlabel->setText(title);
0131     setTitleWidget(tlabel);
0132 }
0133 
0134 void AltLangStrEdit::setTitleWidget(QWidget* const twdg)
0135 {
0136     if (d->titleWidget)
0137     {
0138         delete d->titleWidget;
0139     }
0140 
0141     d->titleWidget = twdg;
0142     d->grid->addWidget(d->titleWidget, 0, 0, 1, 1);
0143 }
0144 
0145 QWidget* AltLangStrEdit::titleWidget() const
0146 {
0147     return d->titleWidget;
0148 }
0149 
0150 void AltLangStrEdit::setPlaceholderText(const QString& msg)
0151 {
0152     d->valueEdit->setPlaceholderText(msg);
0153 }
0154 
0155 void AltLangStrEdit::reset()
0156 {
0157     setValues(MetaEngine::AltLangMap());
0158 }
0159 
0160 void AltLangStrEdit::slotDeleteValue()
0161 {
0162     d->values.remove(d->currentLanguage);
0163     setValues(d->values);
0164 
0165     Q_EMIT signalValueDeleted(d->currentLanguage);
0166 }
0167 
0168 void AltLangStrEdit::slotSelectionChanged()
0169 {
0170     d->currentLanguage = d->languageCB->currentText();
0171 
0172     // There are bogus signals caused by spell checking, see bug #141663.
0173     // so we must block signals here.
0174 
0175     d->valueEdit->blockSignals(true);
0176 
0177     QString text = d->values.value(d->currentLanguage);
0178     d->valueEdit->setPlainText(text);
0179     d->delValueButton->setEnabled(!text.isNull());
0180     d->localizeSelector->setEnabled(!text.isNull());
0181 
0182     d->valueEdit->blockSignals(false);
0183 
0184     d->languageCB->setToolTip(s_rfc3066ForXMP.value(d->currentLanguage).toString());
0185 
0186     // NOTE: if no specific language is set, spell-checker failback to auto-detection.
0187 
0188     if (d->currentLanguage == QLatin1String("x-default"))
0189     {
0190         d->valueEdit->setCurrentLanguage(QString());
0191     }
0192     else
0193     {
0194         d->valueEdit->setCurrentLanguage(d->currentLanguage.left(2));
0195     }
0196 
0197     Q_EMIT signalSelectionChanged(d->currentLanguage);
0198 }
0199 
0200 void AltLangStrEdit::setValues(const MetaEngine::AltLangMap& values)
0201 {
0202     d->values    = values;
0203     populateLangAltListEntries();
0204 
0205     d->valueEdit->blockSignals(true);
0206 
0207     QString text = d->values.value(d->currentLanguage);
0208     d->valueEdit->setPlainText(text);
0209     d->delValueButton->setEnabled(!text.isNull());
0210     d->localizeSelector->setEnabled(!text.isNull());
0211 
0212     d->valueEdit->blockSignals(false);
0213 }
0214 
0215 MetaEngine::AltLangMap& AltLangStrEdit::values() const
0216 {
0217     return d->values;
0218 }
0219 
0220 void AltLangStrEdit::populateLangAltListEntries()
0221 {
0222     d->languageCB->blockSignals(true);
0223 
0224     d->languageCB->clear();
0225 
0226     // In first we fill already assigned languages.
0227 
0228     QStringList list = d->values.keys();
0229 
0230     if (!list.isEmpty())
0231     {
0232         Q_FOREACH (const QString& lg, list)
0233         {
0234             d->languageCB->addItem(lg);
0235             d->languageCB->setItemIcon(d->languageCB->count() - 1,
0236                                        QIcon::fromTheme(QLatin1String("dialog-ok-apply")).pixmap(16, 16));
0237             d->languageCB->setItemData(d->languageCB->findText(lg), i18nc("@info", "Switch to %1", languageNameRFC3066(lg)), Qt::ToolTipRole);
0238         }
0239 
0240         d->languageCB->insertSeparator(d->languageCB->count());
0241     }
0242 
0243     // ...and now, all the rest...
0244 
0245     LocalizeContainer set = LocalizeSettings::instance()->settings();
0246     QStringList lang      = set.alternativeLang;
0247 
0248     Q_FOREACH (const QString& lg, lang)
0249     {
0250         if (!list.contains(lg))
0251         {
0252             d->languageCB->addItem(lg);
0253             d->languageCB->setItemData(d->languageCB->findText(lg), i18nc("@info", "Switch to %1", languageNameRFC3066(lg)), Qt::ToolTipRole);
0254         }
0255     }
0256 
0257     d->languageCB->setCurrentIndex(d->languageCB->findText(d->currentLanguage));
0258 
0259     d->languageCB->blockSignals(false);
0260 }
0261 
0262 QString AltLangStrEdit::defaultAltLang() const
0263 {
0264     return d->values.value(QLatin1String("x-default"));
0265 }
0266 
0267 bool AltLangStrEdit::asDefaultAltLang() const
0268 {
0269     return !defaultAltLang().isNull();
0270 }
0271 
0272 void AltLangStrEdit::slotTextChanged()
0273 {
0274     QString editedText   = d->valueEdit->toPlainText();
0275     QString previousText = d->values.value(d->currentLanguage);
0276     bool textChanged     = (editedText != previousText);
0277 
0278     if      (editedText.isEmpty() && textChanged)
0279     {
0280         slotDeleteValue();
0281     }
0282     else if (previousText.isNull() && textChanged)
0283     {
0284         addCurrent();
0285     }
0286     else if (textChanged)
0287     {
0288         // we cannot trust that the text actually changed
0289         // (there are bogus signals caused by spell checking, see bug #141663)
0290         // so we have to check before marking the metadata as modified.
0291 
0292         d->values.insert(d->currentLanguage, editedText);
0293 
0294         Q_EMIT signalModified(d->currentLanguage, editedText);
0295     }
0296 }
0297 
0298 void AltLangStrEdit::addCurrent()
0299 {
0300     QString text = d->valueEdit->toPlainText();
0301 
0302     d->values.insert(d->currentLanguage, text);
0303     populateLangAltListEntries();
0304     d->delValueButton->setEnabled(true);
0305     d->localizeSelector->setEnabled(true);
0306 
0307     Q_EMIT signalValueAdded(d->currentLanguage, text);
0308 }
0309 
0310 void AltLangStrEdit::setLinesVisible(uint lines)
0311 {
0312     d->linesVisible = lines;
0313 
0314     if (d->linesVisible == 0)
0315     {
0316         d->valueEdit->setFixedHeight(QWIDGETSIZE_MAX); // reset
0317     }
0318     else
0319     {
0320         d->valueEdit->setFixedHeight(d->valueEdit->fontMetrics().lineSpacing() * d->linesVisible         +
0321                                      d->valueEdit->contentsMargins().top()                               +
0322                                      d->valueEdit->contentsMargins().bottom()                            +
0323                                      1                                                                   +
0324                                      2*(d->valueEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) +
0325                                         d->valueEdit->style()->pixelMetric(QStyle::PM_FocusFrameVMargin))
0326                                     );
0327     }
0328 
0329     // It's not possible to display scrollbar properly if size is too small
0330 
0331     if (d->linesVisible < 3)
0332     {
0333         d->valueEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0334     }
0335 }
0336 
0337 uint AltLangStrEdit::linesVisible() const
0338 {
0339     return d->linesVisible;
0340 }
0341 
0342 void AltLangStrEdit::changeEvent(QEvent* e)
0343 {
0344     if (e->type() == QEvent::FontChange)
0345     {
0346         setLinesVisible(linesVisible());
0347     }
0348 
0349     QWidget::changeEvent(e);
0350 }
0351 
0352 DTextEdit* AltLangStrEdit::textEdit() const
0353 {
0354     return d->valueEdit;
0355 }
0356 
0357 void AltLangStrEdit::slotTranslate(const QString& lang)
0358 {
0359     if (d->trengine->isRunning())
0360     {
0361         return;
0362     }
0363 
0364     setDisabled(true);
0365 
0366     d->trCode       = lang;
0367     QString srcCode = currentLanguageCode();
0368     DOnlineTranslator::Language trLang;
0369     DOnlineTranslator::Language srcLang;
0370 
0371     if (srcCode == QLatin1String("x-default"))
0372     {
0373         srcLang = DOnlineTranslator::Auto;
0374     }
0375     else
0376     {
0377         srcLang = DOnlineTranslator::language(DOnlineTranslator::fromRFC3066(LocalizeSettings::instance()->settings().translatorEngine, srcCode));
0378     }
0379 
0380     trLang       = DOnlineTranslator::language(DOnlineTranslator::fromRFC3066(LocalizeSettings::instance()->settings().translatorEngine, d->trCode));
0381     QString text = textEdit()->text();
0382 
0383     qCDebug(DIGIKAM_WIDGETS_LOG) << "Request to translate with Web-service:";
0384     qCDebug(DIGIKAM_WIDGETS_LOG) << "Text to translate        :" << text;
0385     qCDebug(DIGIKAM_WIDGETS_LOG) << "To target language       :" << trLang;
0386     qCDebug(DIGIKAM_WIDGETS_LOG) << "With source language     :" << srcLang;
0387 
0388     d->trengine->translate(text,                                                            // String to translate
0389                            LocalizeSettings::instance()->settings().translatorEngine,       // Web service
0390                            trLang,                                                          // Target language
0391                            srcLang,                                                         // Source langage
0392                            DOnlineTranslator::Auto);
0393 }
0394 
0395 void AltLangStrEdit::slotTranslationFinished()
0396 {
0397     setDisabled(false);
0398 
0399     if (d->trengine->error() == DOnlineTranslator::NoError)
0400     {
0401         if (d->trCode.isEmpty())
0402         {
0403             return;
0404         }
0405 
0406         QString translation = d->trengine->translation();
0407         qCDebug(DIGIKAM_WIDGETS_LOG) << "Text translated          :" << translation;
0408 
0409         MetaEngine::AltLangMap vals = values();
0410         vals.insert(d->trCode, translation);
0411         setValues(vals);
0412 
0413         Q_EMIT signalValueAdded(d->trCode, translation);
0414 
0415         d->languageCB->setCurrentText(d->trCode);
0416         d->trCode.clear();
0417     }
0418     else
0419     {
0420         qCDebug(DIGIKAM_WIDGETS_LOG) << "Translation Error       :" << d->trengine->error();
0421 
0422         QMessageBox::information(qApp->activeWindow(),
0423                                  i18nc("@title:window", "Failed to Translate String with %1 Web-Service",
0424                                  DOnlineTranslator::engineName(LocalizeSettings::instance()->settings().translatorEngine)),
0425                                  i18nc("@info", "Error message: %1",
0426                                  d->trengine->errorString()));
0427 
0428     }
0429 }
0430 
0431 } // namespace Digikam
0432 
0433 #include "moc_altlangstredit.cpp"