1/*
2 SPDX-FileCopyrightText: 1999-2003 Hans Petter Bieker <bieker@kde.org>
3 SPDX-FileCopyrightText: 2007 David Jarvie <djarvie@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "klanguagebutton.h"
9
10#include <QDir>
11#include <QFile>
12#include <QHBoxLayout>
13#include <QLocale>
14#include <QMenu>
15#include <QPushButton>
16
17#include <KConfig>
18#include <KConfigGroup>
19
20static void checkInsertPos(QMenu *popup, const QString &str, int &index)
21{
22 if (index != -1) {
23 return;
24 }
25
26 int a = 0;
27 const QList<QAction *> actions = popup->actions();
28 int b = actions.count();
29
30 while (a < b) {
31 int w = (a + b) / 2;
32 QAction *ac = actions[w];
33 int j = str.localeAwareCompare(s: ac->text());
34 if (j > 0) {
35 a = w + 1;
36 } else {
37 b = w;
38 }
39 }
40
41 index = a; // it doesn't really matter ... a == b here.
42
43 Q_ASSERT(a == b);
44}
45
46class KLanguageButtonPrivate
47{
48public:
49 explicit KLanguageButtonPrivate(KLanguageButton *parent);
50 ~KLanguageButtonPrivate()
51 {
52 delete button;
53 delete popup;
54 }
55 void setCurrentItem(QAction *);
56 void clear();
57 QAction *findAction(const QString &data) const;
58
59 QPushButton *button = nullptr;
60 QStringList ids;
61 QMenu *popup = nullptr;
62 QString current;
63 QString locale;
64 bool staticText : 1;
65 bool showCodes : 1;
66};
67
68KLanguageButton::KLanguageButton(QWidget *parent)
69 : QWidget(parent)
70 , d(new KLanguageButtonPrivate(this))
71{
72}
73
74KLanguageButton::KLanguageButton(const QString &text, QWidget *parent)
75 : QWidget(parent)
76 , d(new KLanguageButtonPrivate(this))
77{
78 setText(text);
79}
80
81KLanguageButtonPrivate::KLanguageButtonPrivate(KLanguageButton *parent)
82 : button(new QPushButton(parent))
83 , popup(new QMenu(parent))
84 , locale(QLocale::system().name())
85 , staticText(false)
86 , showCodes(false)
87{
88 QHBoxLayout *layout = new QHBoxLayout(parent);
89 layout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
90 layout->addWidget(button);
91
92 parent->setFocusProxy(button);
93 parent->setFocusPolicy(button->focusPolicy());
94
95 button->setMenu(popup);
96
97 QObject::connect(sender: popup, signal: &QMenu::triggered, context: parent, slot: &KLanguageButton::slotTriggered);
98 QObject::connect(sender: popup, signal: &QMenu::hovered, context: parent, slot: &KLanguageButton::slotHovered);
99}
100
101KLanguageButton::~KLanguageButton() = default;
102
103void KLanguageButton::setText(const QString &text)
104{
105 d->staticText = true;
106 d->button->setText(text);
107}
108
109void KLanguageButton::setLocale(const QString &locale)
110{
111 d->locale = locale;
112}
113
114void KLanguageButton::showLanguageCodes(bool show)
115{
116 d->showCodes = show;
117}
118
119static QString nameFromEntryFile(const QString &entryFile)
120{
121 const KConfig entry(entryFile, KConfig::SimpleConfig);
122 const KConfigGroup group(&entry, QStringLiteral("KCM Locale"));
123 return group.readEntry(key: "Name", aDefault: QString());
124}
125
126void KLanguageButton::insertLanguage(const QString &languageCode, const QString &name, int index)
127{
128 QString text;
129 bool showCodes = d->showCodes;
130 if (name.isEmpty()) {
131 const QString entryFile =
132 QStandardPaths::locate(type: QStandardPaths::GenericDataLocation, fileName: QLatin1String("locale/") + languageCode + QLatin1String("/kf6_entry.desktop"));
133 if (QFile::exists(fileName: entryFile)) {
134 text = nameFromEntryFile(entryFile);
135 }
136
137 if (text.isEmpty()) {
138 text = languageCode;
139 QLocale locale(languageCode);
140 if (locale != QLocale::c()) {
141 text = locale.nativeLanguageName();
142 // For some languages the native name might be empty.
143 // In this case use the non native language name as fallback.
144 // See: QTBUG-51323
145 text = text.isEmpty() ? QLocale::languageToString(language: locale.language()) : text;
146 } else {
147 showCodes = false;
148 }
149 }
150 } else {
151 text = name;
152 }
153 if (showCodes) {
154 text += QLatin1String(" (") + languageCode + QLatin1Char(')');
155 }
156
157 checkInsertPos(popup: d->popup, str: text, index);
158 QAction *a = new QAction(QIcon(), text, this);
159 a->setData(languageCode);
160 if (index >= 0 && index < d->popup->actions().count() - 1) {
161 d->popup->insertAction(before: d->popup->actions()[index], action: a);
162 } else {
163 d->popup->addAction(action: a);
164 }
165 d->ids.append(t: languageCode);
166}
167
168void KLanguageButton::insertSeparator(int index)
169{
170 if (index >= 0 && index < d->popup->actions().count() - 1) {
171 d->popup->insertSeparator(before: d->popup->actions()[index]);
172 } else {
173 d->popup->addSeparator();
174 }
175}
176
177void KLanguageButton::loadAllLanguages()
178{
179 const QStringList localeDirs = QStandardPaths::locateAll(type: QStandardPaths::GenericDataLocation, QStringLiteral("locale"), options: QStandardPaths::LocateDirectory);
180 for (const QString &localeDir : localeDirs) {
181 const QStringList entries = QDir(localeDir).entryList(filters: QDir::Dirs, sort: QDir::Name);
182 for (const QString &d : entries) {
183 const QString entryFile = localeDir + QLatin1Char('/') + d + QStringLiteral("/kf6_entry.desktop");
184 if (QFile::exists(fileName: entryFile)) {
185 insertLanguage(languageCode: d);
186 }
187 }
188 }
189
190 d->ids.removeDuplicates();
191 setCurrentItem(d->locale);
192}
193
194void KLanguageButton::slotTriggered(QAction *a)
195{
196 // qCDebug(KCONFIG_WIDGETS_LOG) << "slotTriggered" << index;
197 if (!a) {
198 return;
199 }
200
201 d->setCurrentItem(a);
202
203 // Forward event from popup menu as if it was emitted from this widget:
204 Q_EMIT activated(languageCode: d->current);
205}
206
207void KLanguageButton::slotHovered(QAction *a)
208{
209 // qCDebug(KCONFIG_WIDGETS_LOG) << "slotHovered" << index;
210
211 Q_EMIT highlighted(languageCode: a->data().toString());
212}
213
214int KLanguageButton::count() const
215{
216 return d->ids.count();
217}
218
219void KLanguageButton::clear()
220{
221 d->clear();
222}
223
224void KLanguageButtonPrivate::clear()
225{
226 ids.clear();
227 popup->clear();
228
229 if (!staticText) {
230 button->setText(QString());
231 }
232}
233
234bool KLanguageButton::contains(const QString &languageCode) const
235{
236 return d->ids.contains(str: languageCode);
237}
238
239QString KLanguageButton::current() const
240{
241 return d->current.isEmpty() ? QStringLiteral("en") : d->current;
242}
243
244QAction *KLanguageButtonPrivate::findAction(const QString &data) const
245{
246 const auto listActions = popup->actions();
247 for (QAction *a : listActions) {
248 if (!a->data().toString().compare(s: data)) {
249 return a;
250 }
251 }
252 return nullptr;
253}
254
255void KLanguageButton::setCurrentItem(const QString &languageCode)
256{
257 if (d->ids.isEmpty()) {
258 return;
259 }
260 QAction *a;
261 if (d->ids.indexOf(str: languageCode) < 0) {
262 a = d->findAction(data: d->ids[0]);
263 } else {
264 a = d->findAction(data: languageCode);
265 }
266 if (a) {
267 d->setCurrentItem(a);
268 }
269}
270
271void KLanguageButtonPrivate::setCurrentItem(QAction *a)
272{
273 if (!a->data().isValid()) {
274 return;
275 }
276 current = a->data().toString();
277
278 if (!staticText) {
279 button->setText(a->text());
280 }
281}
282
283#include "moc_klanguagebutton.cpp"
284

source code of kconfigwidgets/src/klanguagebutton.cpp