Krita Source Code Documentation
Loading...
Searching...
No Matches
kswitchlanguagedialog_p.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the KDE Libraries
3 * SPDX-FileCopyrightText: 2007 Krzysztof Lichota (lichota@mimuw.edu.pl)
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 *
7 */
8
9#include <QApplication>
10#include <QDialogButtonBox>
11#include <QDir>
12#include <QLayout>
13#include <QLabel>
14#include <QPushButton>
15#include <QEvent>
16#include <QMap>
17#include <QSettings>
18#include <QSharedPointer>
19#include <QStandardPaths>
20#include <QDebug>
21
23
24#include <klanguagebutton.h>
25#include <klocalizedstring.h>
26#include <kmessagebox.h>
27
28// Believe it or not we can't use KConfig from here
29// (we need KConfig during QCoreApplication ctor which is too early for it)
30// So we cooked a QSettings based solution
32
34{
35 const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
36 const QDir configDir(configPath);
37 if (!configDir.exists()) {
38 configDir.mkpath(QStringLiteral("."));
39 }
40 return QSettingsPtr(new QSettings(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat));
41}
42
43static QByteArray getApplicationSpecificLanguage(const QByteArray &defaultCode = QByteArray())
44{
46 settings->beginGroup(QStringLiteral("Language"));
47 //qDebug() << "our language" << settings->value(qAppName(), defaultCode).toByteArray();
48 return settings->value(qAppName(), defaultCode).toByteArray();
49}
50
51static void setApplicationSpecificLanguage(const QByteArray &languageCode)
52{
54 settings->beginGroup(QStringLiteral("Language"));
55
56 if (languageCode.isEmpty()) {
57 settings->remove(qAppName());
58 } else {
59 settings->setValue(qAppName(), languageCode);
60 }
61}
62
64{
65 const QByteArray languageCode = getApplicationSpecificLanguage();
66
67 if (!languageCode.isEmpty()) {
68 QByteArray languages = qgetenv("LANGUAGE");
69 if (languages.isEmpty()) {
70 qputenv("LANGUAGE", languageCode);
71 } else {
72 QByteArray ba(languageCode);
73 ba.append(":");
74 ba.append(languages);
75 qputenv("LANGUAGE", ba);
76 }
77 }
78 //qDebug() << ">>>>>>>>>>>>>> LANGUAGE" << qgetenv("LANGUAGE");
79 //qDebug() << ">>>>>>>>>>>>>> DATADIRS" << qgetenv("XDG_DATA_DIRS");
80}
81
82Q_COREAPP_STARTUP_FUNCTION(initializeLanguages)
83
84namespace KDEPrivate
85{
86
88
89 QLabel *label {nullptr};
91 QPushButton *removeButton {nullptr};
92
94 QLabel *label,
96 QPushButton *removeButton
97 )
98 {
99 this->label = label;
100 this->languageButton = languageButton;
101 this->removeButton = removeButton;
102 }
103
104};
105
107{
108public:
110
111 KisKSwitchLanguageDialog *p {nullptr}; //parent class
112
117
121 void addLanguageButton(const QString &languageCode, bool primaryLanguage);
122
127
128 QMap<QPushButton *, LanguageRowData> languageRows;
130 QGridLayout *languagesLayout {nullptr};
131};
132
133/*************************** KisKSwitchLanguageDialog **************************/
134
136 : QDialog(parent),
138{
139 setWindowTitle(i18n("Switch Application Language"));
140
141 QVBoxLayout *topLayout = new QVBoxLayout(this);
142
143 QLabel *label = new QLabel(i18n("Please choose the language which should be used for this application:"), this);
144 topLayout->addWidget(label);
145
146 QHBoxLayout *languageHorizontalLayout = new QHBoxLayout();
147 topLayout->addLayout(languageHorizontalLayout);
148
149 d->languagesLayout = new QGridLayout();
150 languageHorizontalLayout->addLayout(d->languagesLayout);
151 languageHorizontalLayout->addStretch();
152
153 const QStringList defaultLanguages = d->applicationLanguageList();
154
155 int count = defaultLanguages.count();
156 for (int i = 0; i < count; ++i) {
157 QString language = defaultLanguages[i];
158 bool primaryLanguage = (i == 0);
159 d->addLanguageButton(language, primaryLanguage);
160 }
161
162 if (!count) {
163 QLocale l;
164 d->addLanguageButton(l.name(), true);
165 }
166
167 QHBoxLayout *addButtonHorizontalLayout = new QHBoxLayout();
168 topLayout->addLayout(addButtonHorizontalLayout);
169
170 QPushButton *addLangButton = new QPushButton(i18n("Add Fallback Language"), this);
171 addLangButton->setToolTip(i18n("Adds one more language which will be used if other translations do not contain a proper translation."));
172 connect(addLangButton, SIGNAL(clicked()), this, SLOT(slotAddLanguageButton()));
173 addButtonHorizontalLayout->addWidget(addLangButton);
174 addButtonHorizontalLayout->addStretch();
175
176 topLayout->addStretch(10);
177
178 QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
179 buttonBox->setStandardButtons(QDialogButtonBox::Ok
180 | QDialogButtonBox::Cancel
181 | QDialogButtonBox::RestoreDefaults);
182 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
183 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
184 KGuiItem::assign(buttonBox->button(QDialogButtonBox::RestoreDefaults), KStandardGuiItem::defaults());
185
186 topLayout->addWidget(buttonBox);
187
188 connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk()));
189 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
190 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
191 connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()),
192 this, SLOT(slotDefault()));
193}
194
199
201{
202 //adding new button with en_US as it should always be present
203 d->addLanguageButton(QStringLiteral("en_US"), d->languageButtons.isEmpty());
204}
205
207{
208 QObject const *signalSender = sender();
209 if (!signalSender) {
210 qCritical() << "KisKSwitchLanguageDialog::removeButtonClicked() called directly, not using signal" << Qt::endl;
211 return;
212 }
213
214 QPushButton *removeButton = const_cast<QPushButton *>(::qobject_cast<const QPushButton *>(signalSender));
215 if (!removeButton) {
216 qCritical() << "KisKSwitchLanguageDialog::removeButtonClicked() called from something else than QPushButton" << Qt::endl;
217 return;
218 }
219
220 QMap<QPushButton *, LanguageRowData>::iterator it = d->languageRows.find(removeButton);
221 if (it == d->languageRows.end()) {
222 qCritical() << "KisKSwitchLanguageDialog::removeButtonClicked called from unknown QPushButton" << Qt::endl;
223 return;
224 }
225
226 LanguageRowData languageRowData = it.value();
227
228 d->languageButtons.removeAll(languageRowData.languageButton);
229
230 languageRowData.label->deleteLater();
231 languageRowData.languageButton->deleteLater();
232 languageRowData.removeButton->deleteLater();
233 d->languageRows.erase(it);
234}
235
237{
238 Q_UNUSED(languageCode);
239#if 0
240 for (int i = 0, count = d->languageButtons.count(); i < count; ++i) {
241 KLanguageButton *languageButton = d->languageButtons[i];
242 if (languageButton->current() == languageCode) {
243 //update all buttons which have matching id
244 //might update buttons which were not changed, but well...
245 languageButton->setText(KLocale::global()->languageCodeToName(languageCode));
246 }
247 }
248#endif
249}
250
252{
253 QStringList languages;
254
255 for (int i = 0, count = d->languageButtons.count(); i < count; ++i) {
256 KLanguageButton *languageButton = d->languageButtons[i];
257 languages << languageButton->current();
258 }
259
260 if (d->applicationLanguageList() != languages) {
261 QString languageString = languages.join(QLatin1Char(':'));
262 //list is different from defaults or saved languages list
263 setApplicationSpecificLanguage(languageString.toLatin1());
264
265 QMessageBox::information(this,
266 i18nc("@title:window:", "Application Language Changed"), //caption
267 i18n("The language for this application has been changed. The change will take effect the next time the application is started."));
268 }
269
270 accept();
271}
272
274{
275 setApplicationSpecificLanguage(QByteArray());
276 accept();
277}
278
279/************************ KisKSwitchLanguageDialogPrivate ***********************/
280
283 : p(parent)
284{
285 //NOTE: do NOT use "p" in constructor, it is not fully constructed
286}
287
288static bool stripCountryCode(QString *languageCode)
289{
290 const int idx = languageCode->indexOf(QLatin1String("_"));
291 if (idx != -1) {
292 *languageCode = languageCode->left(idx);
293 return true;
294 }
295 return false;
296}
297
299{
300 QLocale defaultLocale;
301 QLocale cLocale(QLocale::C);
302 QLocale::setDefault(cLocale);
303 QSet<QString> insertedLanguages;
304
305 const QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
306 Q_FOREACH (const QLocale &l, allLocales) {
307 QString languageCode = l.name();
308 if (l != cLocale) {
309 const QString nativeName = l.nativeLanguageName();
310 // For some languages the native name might be empty.
311 // In this case use the non native language name as fallback.
312 // See: QTBUG-51323
313 const QString languageName = nativeName.isEmpty() ? QLocale::languageToString(l.language()) : nativeName;
314 if (!insertedLanguages.contains(languageCode) && KLocalizedString::isApplicationTranslatedInto(languageCode)) {
315 QString displayName;
316 // Check if languageCode contains a country name.
317 // For en and en_GB their native names already contain "American"
318 // and "British", so no need to append the country name.
319 // Same applies for zh_CN and zh_TW, however we will need to
320 // disambiguate between zh_TW and zh_HK if it is eventually
321 // added.
322 if (l.language() != QLocale::English && l.language() != QLocale::Chinese && languageCode.contains('_')) {
323 QString countryName = l.nativeCountryName();
324 // Fallback just in case.
325 if (countryName.isEmpty()) {
326 countryName = QLocale::countryToString(l.country());
327 }
328 // Append the country name for disambiguation.
329 displayName = languageName % " (" % countryName % ")";
330 } else {
331 displayName = languageName;
332 }
333 button->insertLanguage(languageCode, displayName);
334 insertedLanguages << languageCode;
335 } else if (stripCountryCode(&languageCode)) {
336 if (!insertedLanguages.contains(languageCode) && KLocalizedString::isApplicationTranslatedInto(languageCode)) {
337 button->insertLanguage(languageCode, languageName);
338 insertedLanguages << languageCode;
339 }
340 }
341 }
342 }
343
344 QLocale::setDefault(defaultLocale);
345}
346
348{
349 QStringList languagesList;
350
351 QByteArray languageCode = getApplicationSpecificLanguage();
352 if (!languageCode.isEmpty()) {
353 languagesList = QString::fromLatin1(languageCode).split(QLatin1Char(':'));
354 }
355 if (languagesList.isEmpty()) {
356 QLocale l;
357 languagesList = l.uiLanguages();
358
359 // We get en-US here but we use en_US
360 for (int i = 0; i < languagesList.count(); ++i) {
361 languagesList[i].replace(QLatin1String("-"), QLatin1String("_"));
362 }
363 }
364
365 for (int i = 0; i < languagesList.count();) {
366 QString languageCode = languagesList[i];
367 if (!KLocalizedString::isApplicationTranslatedInto(languageCode)) {
368 if (stripCountryCode(&languageCode)) {
369 if (KLocalizedString::isApplicationTranslatedInto(languageCode)) {
370 languagesList[i] = languageCode;
371 ++i;
372 continue;
373 }
374 }
375 languagesList.removeAt(i);
376 } else {
377 ++i;
378 }
379 }
380
381 return languagesList;
382}
383
384void KisKSwitchLanguageDialogPrivate::addLanguageButton(const QString &languageCode, bool primaryLanguage)
385{
386 QString labelText = primaryLanguage ? i18n("Primary language:") : i18n("Fallback language:");
387
388 KLanguageButton *languageButton = new KLanguageButton(p);
389
390 fillApplicationLanguages(languageButton);
391
392 languageButton->setCurrentItem(languageCode);
393
394 QObject::connect(
395 languageButton,
396 SIGNAL(activated(QString)),
397 p,
398 SLOT(languageOnButtonChanged(QString))
399 );
400
401 LanguageRowData languageRowData;
402 QPushButton *removeButton = 0;
403
404 if (!primaryLanguage) {
405 removeButton = new QPushButton(i18n("Remove"), p);
406
407 QObject::connect(
408 removeButton,
409 SIGNAL(clicked()),
410 p,
411 SLOT(removeButtonClicked())
412 );
413 }
414
415 languageButton->setToolTip(primaryLanguage
416 ? i18n("This is the main application language which will be used first, before any other languages.")
417 : i18n("This is the language which will be used if any previous languages do not contain a proper translation."));
418
419 int numRows = languagesLayout->rowCount();
420
421 QLabel *languageLabel = new QLabel(labelText, p);
422 languagesLayout->addWidget(languageLabel, numRows + 1, 1, Qt::AlignLeft);
423 languagesLayout->addWidget(languageButton, numRows + 1, 2, Qt::AlignLeft);
424
425 if (!primaryLanguage) {
426 languagesLayout->addWidget(removeButton, numRows + 1, 3, Qt::AlignLeft);
427 languageRowData.setRowWidgets(languageLabel, languageButton, removeButton);
428 removeButton->show();
429 }
430
431 languageRows.insert(removeButton, languageRowData);
432
433 languageButtons.append(languageButton);
434 languageButton->show();
435 languageLabel->show();
436}
437
438}
const Params2D p
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KisKSwitchLanguageDialogPrivate(KisKSwitchLanguageDialog *parent)
void addLanguageButton(const QString &languageCode, bool primaryLanguage)
QMap< QPushButton *, LanguageRowData > languageRows
Standard "switch application language" dialog box.
virtual void languageOnButtonChanged(const QString &)
KisKSwitchLanguageDialogPrivate *const d
QString current() const
void setCurrentItem(const QString &languageCode)
void setText(const QString &text)
QString button(const QWheelEvent &ev)
static QSettingsPtr localeOverridesSettings()
static QByteArray getApplicationSpecificLanguage(const QByteArray &defaultCode=QByteArray())
static void setApplicationSpecificLanguage(const QByteArray &languageCode)
QSharedPointer< QSettings > QSettingsPtr
static void initializeLanguages()
static bool stripCountryCode(QString *languageCode)
void setRowWidgets(QLabel *label, KLanguageButton *languageButton, QPushButton *removeButton)