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 | |
20 | static void (QMenu *, 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 | |
46 | class KLanguageButtonPrivate |
47 | { |
48 | public: |
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 * = nullptr; |
62 | QString current; |
63 | QString locale; |
64 | bool staticText : 1; |
65 | bool showCodes : 1; |
66 | }; |
67 | |
68 | KLanguageButton::KLanguageButton(QWidget *parent) |
69 | : QWidget(parent) |
70 | , d(new KLanguageButtonPrivate(this)) |
71 | { |
72 | } |
73 | |
74 | KLanguageButton::KLanguageButton(const QString &text, QWidget *parent) |
75 | : QWidget(parent) |
76 | , d(new KLanguageButtonPrivate(this)) |
77 | { |
78 | setText(text); |
79 | } |
80 | |
81 | KLanguageButtonPrivate::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 | |
101 | KLanguageButton::~KLanguageButton() = default; |
102 | |
103 | void KLanguageButton::setText(const QString &text) |
104 | { |
105 | d->staticText = true; |
106 | d->button->setText(text); |
107 | } |
108 | |
109 | void KLanguageButton::setLocale(const QString &locale) |
110 | { |
111 | d->locale = locale; |
112 | } |
113 | |
114 | void KLanguageButton::showLanguageCodes(bool show) |
115 | { |
116 | d->showCodes = show; |
117 | } |
118 | |
119 | static 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 | |
126 | void 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 | |
168 | void 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 | |
177 | void 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 | |
194 | void 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 | |
207 | void KLanguageButton::slotHovered(QAction *a) |
208 | { |
209 | // qCDebug(KCONFIG_WIDGETS_LOG) << "slotHovered" << index; |
210 | |
211 | Q_EMIT highlighted(languageCode: a->data().toString()); |
212 | } |
213 | |
214 | int KLanguageButton::count() const |
215 | { |
216 | return d->ids.count(); |
217 | } |
218 | |
219 | void KLanguageButton::clear() |
220 | { |
221 | d->clear(); |
222 | } |
223 | |
224 | void KLanguageButtonPrivate::clear() |
225 | { |
226 | ids.clear(); |
227 | popup->clear(); |
228 | |
229 | if (!staticText) { |
230 | button->setText(QString()); |
231 | } |
232 | } |
233 | |
234 | bool KLanguageButton::contains(const QString &languageCode) const |
235 | { |
236 | return d->ids.contains(str: languageCode); |
237 | } |
238 | |
239 | QString KLanguageButton::current() const |
240 | { |
241 | return d->current.isEmpty() ? QStringLiteral("en" ) : d->current; |
242 | } |
243 | |
244 | QAction *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 | |
255 | void 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 | |
271 | void 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 | |