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"