/** =========================================================== * @file * * This file is a part of digiKam project * http://www.digikam.org * * @date 2009-06-15 * @brief multi-languages string editor * * @author Copyright (C) 2009-2013 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "moc_altlangstredit.cpp" // Qt includes #include #include #include #include #include #include // KDE includes #include #include #include #include #include #include #include // Local includes #include "msgtextedit.h" namespace KExiv2Iface { class AltLangStrEdit::Private { public: Private() { valueEdit = 0; titleLabel = 0; delValueButton = 0; languageCB = 0; linesVisible = 0; currentLanguage = "x-default"; // We cannot use KLocale::allLanguagesList() here because KDE only // support 2 characters country codes. XMP require 2+2 characters language+country // following ISO 3066 (http://babelwiki.babelzilla.org/index.php?title=Language_codes) // The first one from the list is the Default Language code specified by XMP paper languageCodeMap.insert( "x-default", i18n("Default Language") ); // Standard ISO 3066 country codes. languageCodeMap.insert( "af-ZA", i18n("Afrikaans (South Africa)") ); languageCodeMap.insert( "am-ET", i18n("Amharic (Ethiopia)") ); languageCodeMap.insert( "ar-AE", i18n("Arabic (UAE)") ); languageCodeMap.insert( "ar-BH", i18n("Arabic (Bahrain)") ); languageCodeMap.insert( "ar-DZ", i18n("Arabic (Algeria)") ); languageCodeMap.insert( "ar-EG", i18n("Arabic (Egypt)") ); languageCodeMap.insert( "ar-IQ", i18n("Arabic (Iraq)") ); languageCodeMap.insert( "ar-JO", i18n("Arabic (Jordan)") ); languageCodeMap.insert( "ar-KW", i18n("Arabic (Kuwait)") ); languageCodeMap.insert( "ar-LB", i18n("Arabic (Lebanon)") ); languageCodeMap.insert( "ar-LY", i18n("Arabic (Libya)") ); languageCodeMap.insert( "ar-MA", i18n("Arabic (Morocco)") ); languageCodeMap.insert( "ar-OM", i18n("Arabic (Oman)") ); languageCodeMap.insert( "ar-QA", i18n("Arabic (Qatar)") ); languageCodeMap.insert( "ar-SA", i18n("Arabic (Saudi Arabia)") ); languageCodeMap.insert( "ar-SY", i18n("Arabic (Syria)") ); languageCodeMap.insert( "ar-TN", i18n("Arabic (Tunisia)") ); languageCodeMap.insert( "ar-YE", i18n("Arabic (Yemen)") ); languageCodeMap.insert( "as-IN", i18n("Assamese (India)") ); languageCodeMap.insert( "ba-RU", i18n("Bashkir (Russia)") ); languageCodeMap.insert( "be-BY", i18n("Belarusian (Belarus)") ); languageCodeMap.insert( "bg-BG", i18n("Bulgarian (Bulgaria)") ); languageCodeMap.insert( "bn-IN", i18n("Bengali (India)") ); languageCodeMap.insert( "bo-BT", i18n("Tibetan (Bhutan)") ); languageCodeMap.insert( "bo-CN", i18n("Tibetan (PRC)") ); languageCodeMap.insert( "br-FR", i18n("Breton (France)") ); languageCodeMap.insert( "ca-AD", i18n("Catalan (Andorra)") ); languageCodeMap.insert( "ca-ES", i18n("Catalan (Spain)") ); languageCodeMap.insert( "ca-FR", i18n("Catalan (France)") ); languageCodeMap.insert( "co-FR", i18n("Corsican (France)") ); languageCodeMap.insert( "cs-CZ", i18n("Czech (Czech Republic)") ); languageCodeMap.insert( "cy-GB", i18n("Welsh (United Kingdom)") ); languageCodeMap.insert( "da-DK", i18n("Danish (Denmark)") ); languageCodeMap.insert( "de-AT", i18n("German (Austria)") ); languageCodeMap.insert( "de-CH", i18n("German (Switzerland)") ); languageCodeMap.insert( "de-DE", i18n("German (Germany)") ); languageCodeMap.insert( "de-LI", i18n("German (Liechtenstein)") ); languageCodeMap.insert( "de-LU", i18n("German (Luxembourg)") ); languageCodeMap.insert( "el-GR", i18n("Greek (Greece)") ); languageCodeMap.insert( "en-AU", i18n("English (Australia)") ); languageCodeMap.insert( "en-BZ", i18n("English (Belize)") ); languageCodeMap.insert( "en-CA", i18n("English (Canada)") ); languageCodeMap.insert( "en-CB", i18n("English (Caribbean)") ); languageCodeMap.insert( "en-GB", i18n("English (United Kingdom)") ); languageCodeMap.insert( "en-IE", i18n("English (Ireland)") ); languageCodeMap.insert( "en-IN", i18n("English (India)") ); languageCodeMap.insert( "en-JA", i18n("English (Jamaica)") ); languageCodeMap.insert( "en-MY", i18n("English (Malaysia)") ); languageCodeMap.insert( "en-NZ", i18n("English (New Zealand)") ); languageCodeMap.insert( "en-PH", i18n("English (Philippines)") ); languageCodeMap.insert( "en-SG", i18n("English (Singapore)") ); languageCodeMap.insert( "en-TT", i18n("English (Trinidad)") ); languageCodeMap.insert( "en-US", i18n("English (United States)") ); languageCodeMap.insert( "en-ZA", i18n("English (South Africa)") ); languageCodeMap.insert( "en-ZW", i18n("English (Zimbabwe)") ); languageCodeMap.insert( "es-AR", i18n("Spanish (Argentina)") ); languageCodeMap.insert( "es-BO", i18n("Spanish (Bolivia)") ); languageCodeMap.insert( "es-CL", i18n("Spanish (Chile)") ); languageCodeMap.insert( "es-CO", i18n("Spanish (Colombia)") ); languageCodeMap.insert( "es-CR", i18n("Spanish (Costa Rica)") ); languageCodeMap.insert( "es-DO", i18n("Spanish (Dominican Republic)") ); languageCodeMap.insert( "es-EC", i18n("Spanish (Ecuador)") ); languageCodeMap.insert( "es-ES", i18n("Spanish (Spain)") ); languageCodeMap.insert( "es-GT", i18n("Spanish (Guatemala)") ); languageCodeMap.insert( "es-HN", i18n("Spanish (Honduras)") ); languageCodeMap.insert( "es-MX", i18n("Spanish (Mexico)") ); languageCodeMap.insert( "es-NI", i18n("Spanish (Nicaragua)") ); languageCodeMap.insert( "es-PA", i18n("Spanish (Panama)") ); languageCodeMap.insert( "es-PE", i18n("Spanish (Peru)") ); languageCodeMap.insert( "es-PR", i18n("Spanish (Puerto Rico)") ); languageCodeMap.insert( "es-PY", i18n("Spanish (Paraguay)") ); languageCodeMap.insert( "es-SV", i18n("Spanish (El Salvador)") ); languageCodeMap.insert( "es-UR", i18n("Spanish (Uruguay)") ); languageCodeMap.insert( "es-US", i18n("Spanish (United States)") ); languageCodeMap.insert( "es-VE", i18n("Spanish (Venezuela)") ); languageCodeMap.insert( "et-EE", i18n("Estonian (Estonia)") ); languageCodeMap.insert( "eu-ES", i18n("Basque (Basque Country)") ); languageCodeMap.insert( "fa-IR", i18n("Persian (Iran)") ); languageCodeMap.insert( "fi-FI", i18n("Finnish (Finland)") ); languageCodeMap.insert( "fo-FO", i18n("Faeroese (Faero Islands)") ); languageCodeMap.insert( "fr-BE", i18n("French (Belgium)") ); languageCodeMap.insert( "fr-CA", i18n("French (Canada)") ); languageCodeMap.insert( "fr-CH", i18n("French (Switzerland)") ); languageCodeMap.insert( "fr-FR", i18n("French (France)") ); languageCodeMap.insert( "fr-LU", i18n("French (Luxembourg)") ); languageCodeMap.insert( "fr-MC", i18n("French (Monaco)") ); languageCodeMap.insert( "fy-NL", i18n("Frisian (Netherlands)") ); languageCodeMap.insert( "ga-IE", i18n("Irish (Ireland)") ); languageCodeMap.insert( "gl-ES", i18n("Galician (Galicia)") ); languageCodeMap.insert( "gu-IN", i18n("Gujarati (India)") ); languageCodeMap.insert( "he-IL", i18n("Hebrew (Israel)") ); languageCodeMap.insert( "hi-IN", i18n("Hindi (India)") ); languageCodeMap.insert( "hr-BA", i18n("Croatian (Bosnia and Herzegovina, Latin)") ); languageCodeMap.insert( "hr-HR", i18n("Croatian (Croatia)") ); languageCodeMap.insert( "hu-HU", i18n("Hungarian (Hungary)") ); languageCodeMap.insert( "hy-AM", i18n("Armenian (Armenia)") ); languageCodeMap.insert( "id-ID", i18n("(Indonesian)") ); languageCodeMap.insert( "ii-CN", i18n("Yi (PRC)") ); languageCodeMap.insert( "is-IS", i18n("Icelandic (Iceland)") ); languageCodeMap.insert( "it-CH", i18n("Italian (Switzerland)") ); languageCodeMap.insert( "it-IT", i18n("Italian (Italy)") ); languageCodeMap.insert( "ja-JP", i18n("Japanese (Japan)") ); languageCodeMap.insert( "ka-GE", i18n("Georgian (Georgia)") ); languageCodeMap.insert( "kk-KZ", i18n("Kazakh (Kazakhstan)") ); languageCodeMap.insert( "kl-GL", i18n("Greenlandic (Greenland)") ); languageCodeMap.insert( "km-KH", i18n("Khmer (Cambodia)") ); languageCodeMap.insert( "kn-IN", i18n("Kannada (India)") ); languageCodeMap.insert( "ko-KR", i18n("Korean (South Korea)") ); languageCodeMap.insert( "ky-KG", i18n("Kyrgyz (Kyrgyzstan)") ); languageCodeMap.insert( "lb-LU", i18n("Luxembourgish (Luxembourg)") ); languageCodeMap.insert( "lo-LA", i18n("Lao (Lao PDR)") ); languageCodeMap.insert( "lt-LT", i18n("Lithuanian (Lithuania)") ); languageCodeMap.insert( "lv-LV", i18n("Latvian (Latvia)") ); languageCodeMap.insert( "mi-NZ", i18n("Maori (New Zealand)") ); languageCodeMap.insert( "mk-MK", i18n("Macedonian (Macedonia)") ); languageCodeMap.insert( "ml-IN", i18n("Malayalam (India)") ); languageCodeMap.insert( "mn-CN", i18n("Mongolian (PRC)") ); languageCodeMap.insert( "mn-MN", i18n("Mongolian (Mongolia)") ); languageCodeMap.insert( "mr-IN", i18n("Marathi (India)") ); languageCodeMap.insert( "ms-BN", i18n("Malay (Brunei Darussalam)") ); languageCodeMap.insert( "ms-MY", i18n("Malay (Malaysia)") ); languageCodeMap.insert( "mt-MT", i18n("Maltese (Malta)") ); languageCodeMap.insert( "nb-NO", i18n("Norwegian Bokmål (Norway)") ); languageCodeMap.insert( "ne-NP", i18n("Nepali (Nepal)") ); languageCodeMap.insert( "nl-BE", i18n("Dutch (Belgium)") ); languageCodeMap.insert( "nl-NL", i18n("Dutch (Netherlands)") ); languageCodeMap.insert( "nn-NO", i18n("Norwegian Nynorsk (Norway)") ); languageCodeMap.insert( "ns-ZA", i18n("Sesotho sa Leboa (South Africa)") ); languageCodeMap.insert( "oc-FR", i18n("Occitan (France)") ); languageCodeMap.insert( "or-IN", i18n("Oriya (India)") ); languageCodeMap.insert( "pa-IN", i18n("Punjabi (India)") ); languageCodeMap.insert( "pl-PL", i18n("Polish (Poland)") ); languageCodeMap.insert( "ps-AF", i18n("Pashto (Afghanistan)") ); languageCodeMap.insert( "pt-BR", i18n("Portuguese (Brazil)") ); languageCodeMap.insert( "pt-PT", i18n("Portuguese (Portugal)") ); languageCodeMap.insert( "rm-CH", i18n("Romansh (Switzerland)") ); languageCodeMap.insert( "ro-RO", i18n("Romanian (Romania)") ); languageCodeMap.insert( "ru-RU", i18n("Russian (Russia)") ); languageCodeMap.insert( "rw-RW", i18n("Kinyarwanda (Rwanda)") ); languageCodeMap.insert( "sa-IN", i18n("Sanskrit (India)") ); languageCodeMap.insert( "se-FI", i18n("Sami (Northern, Finland)") ); languageCodeMap.insert( "se-NO", i18n("Sami (Northern, Norway)") ); languageCodeMap.insert( "se-SE", i18n("Sami (Northern, Sweden)") ); languageCodeMap.insert( "si-LK", i18n("Sinhala (Sri Lanka)") ); languageCodeMap.insert( "sk-SK", i18n("Slovak (Slovakia)") ); languageCodeMap.insert( "sl-SI", i18n("Slovenian (Slovenia)") ); languageCodeMap.insert( "sq-AL", i18n("Albanian (Albania)") ); languageCodeMap.insert( "sv-FI", i18n("Swedish (Finland)") ); languageCodeMap.insert( "sv-SE", i18n("Swedish (Sweden)") ); languageCodeMap.insert( "sw-KE", i18n("Swahili (Kenya)") ); languageCodeMap.insert( "ta-IN", i18n("Tamil (India)") ); languageCodeMap.insert( "te-IN", i18n("Telugu (India)") ); languageCodeMap.insert( "th-TH", i18n("Thai (Thailand)") ); languageCodeMap.insert( "tk-TM", i18n("Turkmen (Turkmenistan)") ); languageCodeMap.insert( "tn-ZA", i18n("Setswana Tswana (South Africa)") ); languageCodeMap.insert( "tr-IN", i18n("Urdu (India)") ); languageCodeMap.insert( "tr-TR", i18n("Turkish (Turkey)") ); languageCodeMap.insert( "tt-RU", i18n("Tatar (Russia)") ); languageCodeMap.insert( "ug-CN", i18n("Uighur (PRC)") ); languageCodeMap.insert( "uk-UA", i18n("Ukrainian (Ukraine)") ); languageCodeMap.insert( "ur-PK", i18n("Urdu (Pakistan)") ); languageCodeMap.insert( "vi-VN", i18n("Vietnamese (Vietnam)") ); languageCodeMap.insert( "wo-SN", i18n("Wolof (Senegal)") ); languageCodeMap.insert( "xh-ZA", i18n("isiXhosa Xhosa (South Africa)") ); languageCodeMap.insert( "yo-NG", i18n("Yoruba (Nigeria)") ); languageCodeMap.insert( "zh-CN", i18n("Chinese (PRC)") ); languageCodeMap.insert( "zh-HK", i18n("Chinese (Hong Kong SAR, PRC)") ); languageCodeMap.insert( "zh-MO", i18n("Chinese (Macao SAR)") ); languageCodeMap.insert( "zh-SG", i18n("Chinese (Singapore)") ); languageCodeMap.insert( "zh-TW", i18n("Chinese (Taiwan)") ); languageCodeMap.insert( "zu-ZA", i18n("isiZulu Zulu (South Africa)") ); } ~Private() { languageCodeMap.clear(); } public: typedef QMap LanguageCodeMap; LanguageCodeMap languageCodeMap; QString currentLanguage; uint linesVisible; QLabel* titleLabel; QToolButton* delValueButton; MsgTextEdit* valueEdit; KComboBox* languageCB; KExiv2::AltLangMap values; }; AltLangStrEdit::AltLangStrEdit(QWidget* parent) : QWidget(parent), d(new Private) { QGridLayout* const grid = new QGridLayout(this); d->titleLabel = new QLabel(this); d->delValueButton = new QToolButton(this); d->delValueButton->setIcon(SmallIcon("edit-clear")); d->delValueButton->setToolTip(i18n("Remove entry for this language")); d->delValueButton->setEnabled(false); d->languageCB = new KComboBox(this); d->languageCB->setSizeAdjustPolicy(KComboBox::AdjustToContents); d->languageCB->setWhatsThis(i18n("Select item language here.")); d->valueEdit = new MsgTextEdit(this); d->valueEdit->setCheckSpellingEnabled(true); // -------------------------------------------------------- grid->setAlignment( Qt::AlignTop ); grid->addWidget(d->titleLabel, 0, 0, 1, 1); grid->addWidget(d->languageCB, 0, 2, 1, 1); grid->addWidget(d->delValueButton, 0, 3, 1, 1); grid->addWidget(d->valueEdit, 1, 0, 1,-1); grid->setColumnStretch(1, 10); grid->setMargin(0); grid->setSpacing(KDialog::spacingHint()); loadLangAltListEntries(); // -------------------------------------------------------- connect(d->languageCB, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSelectionChanged())); connect(d->delValueButton, SIGNAL(clicked()), this, SLOT(slotDeleteValue())); connect(d->valueEdit, SIGNAL(textChanged()), this, SLOT(slotTextChanged())); } AltLangStrEdit::~AltLangStrEdit() { delete d; } QString AltLangStrEdit::currentLanguageCode() const { return d->currentLanguage; } void AltLangStrEdit::setCurrentLanguageCode(const QString& lang) { if(d->currentLanguage.isEmpty()) { d->currentLanguage = "x-default"; } else { d->currentLanguage = lang; } } QString AltLangStrEdit::languageCode(int index) const { return d->languageCB->itemText(index); } void AltLangStrEdit::setTitle(const QString& title) { d->titleLabel->setText(title); } void AltLangStrEdit::setClickMessage(const QString& msg) { d->valueEdit->setClickMessage(msg); } void AltLangStrEdit::reset() { setValues(KExiv2::AltLangMap()); } void AltLangStrEdit::slotDeleteValue() { d->values.remove(d->currentLanguage); setValues(d->values); emit signalValueDeleted(d->currentLanguage); } void AltLangStrEdit::slotSelectionChanged() { d->currentLanguage = d->languageCB->currentText(); // There are bogus signals caused by spell checking, see bug #141663. // so we must block signals here. d->valueEdit->blockSignals(true); QString langISO3066 = d->currentLanguage; langISO3066.replace('-', '_'); d->valueEdit->setSpellCheckingLanguage(langISO3066); QString text = d->values.value(d->currentLanguage); d->valueEdit->setText(text); d->delValueButton->setEnabled(!text.isNull()); d->valueEdit->blockSignals(false); d->languageCB->setToolTip(d->languageCodeMap.value(d->currentLanguage)); emit signalSelectionChanged(d->currentLanguage); } void AltLangStrEdit::setValues(const KExiv2::AltLangMap& values) { d->values = values; loadLangAltListEntries(); d->valueEdit->blockSignals(true); QString text = d->values.value(d->currentLanguage); d->valueEdit->setText(text); d->delValueButton->setEnabled(!text.isNull()); d->valueEdit->blockSignals(false); } KExiv2::AltLangMap& AltLangStrEdit::values() { return d->values; } void AltLangStrEdit::loadLangAltListEntries() { d->languageCB->blockSignals(true); d->languageCB->clear(); // In first we fill already assigned languages. QStringList list = d->values.keys(); if (!list.isEmpty()) { foreach(const QString& item, list) { d->languageCB->addItem(item); d->languageCB->setItemIcon(d->languageCB->count()-1, SmallIcon("dialog-ok")); } d->languageCB->insertSeparator(d->languageCB->count()); } // ...and now, all the rest... for (Private::LanguageCodeMap::Iterator it = d->languageCodeMap.begin(); it != d->languageCodeMap.end(); ++it) { if (!list.contains(it.key())) { d->languageCB->addItem(it.key()); } } d->languageCB->setCurrentItem(d->currentLanguage); d->languageCB->blockSignals(false); } QString AltLangStrEdit::defaultAltLang() const { return d->values.value(QString("x-default")); } bool AltLangStrEdit::asDefaultAltLang() const { return !defaultAltLang().isNull(); } void AltLangStrEdit::slotTextChanged() { QString editedText = d->valueEdit->toPlainText(); QString previousText = d->values.value(d->currentLanguage); // Special case : if edited and previous strings are empty, do nothing. // See bug #152948. if (editedText.isEmpty() && previousText.isNull()) { return; } if (editedText.isEmpty()) { slotDeleteValue(); } else if (previousText.isNull()) { addCurrent(); } else if (editedText != previousText) { // we cannot trust that the text actually changed // (there are bogus signals caused by spell checking, see bug #141663) // so we have to check before marking the metadata as modified. d->values.insert(d->currentLanguage, editedText); emit signalModified(d->currentLanguage, editedText); } } void AltLangStrEdit::addCurrent() { QString text = d->valueEdit->toPlainText(); d->values.insert(d->currentLanguage, text); loadLangAltListEntries(); d->delValueButton->setEnabled(true); emit signalValueAdded(d->currentLanguage, text); } void AltLangStrEdit::setLinesVisible(uint lines) { d->linesVisible = lines; if (d->linesVisible == 0) { d->valueEdit->setFixedHeight(QWIDGETSIZE_MAX); // reset } else { d->valueEdit->setFixedHeight(d->valueEdit->fontMetrics().lineSpacing() * d->linesVisible + d->valueEdit->contentsMargins().top() + d->valueEdit->contentsMargins().bottom() + 1 + 2*(d->valueEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) + d->valueEdit->style()->pixelMetric(QStyle::PM_FocusFrameVMargin)) ); } // It's not possible to display scrollbar properlly if size is too small if (d->linesVisible < 3) { d->valueEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } } uint AltLangStrEdit::linesVisible() const { return d->linesVisible; } void AltLangStrEdit::changeEvent(QEvent* e) { if (e->type() == QEvent::FontChange) { setLinesVisible(linesVisible()); } QWidget::changeEvent(e); } } // namespace KExiv2Iface