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

source code of kconfigwidgets/src/klanguagebutton.cpp