File indexing completed on 2024-04-21 14:54:20

0001 /*
0002     SPDX-FileCopyrightText: 1999-2003 Hans Petter Bieker <bieker@kde.org>
0003     SPDX-FileCopyrightText: 2007 David Jarvie <djarvie@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "klanguagebutton.h"
0009 
0010 #include <QDir>
0011 #include <QFile>
0012 #include <QHBoxLayout>
0013 #include <QLocale>
0014 #include <QMenu>
0015 #include <QPushButton>
0016 
0017 #include <KConfig>
0018 #include <KConfigGroup>
0019 
0020 static void checkInsertPos(QMenu *popup, const QString &str, int &index)
0021 {
0022     if (index != -1) {
0023         return;
0024     }
0025 
0026     int a = 0;
0027     const QList<QAction *> actions = popup->actions();
0028     int b = actions.count();
0029 
0030     while (a < b) {
0031         int w = (a + b) / 2;
0032         QAction *ac = actions[w];
0033         int j = str.localeAwareCompare(ac->text());
0034         if (j > 0) {
0035             a = w + 1;
0036         } else {
0037             b = w;
0038         }
0039     }
0040 
0041     index = a; // it doesn't really matter ... a == b here.
0042 
0043     Q_ASSERT(a == b);
0044 }
0045 
0046 class KLanguageButtonPrivate
0047 {
0048 public:
0049     explicit KLanguageButtonPrivate(KLanguageButton *parent);
0050     ~KLanguageButtonPrivate()
0051     {
0052         delete button;
0053         delete popup;
0054     }
0055     void setCurrentItem(QAction *);
0056     void clear();
0057     QAction *findAction(const QString &data) const;
0058 
0059     QPushButton *button = nullptr;
0060     QStringList ids;
0061     QMenu *popup = nullptr;
0062     QString current;
0063     QString locale;
0064     bool staticText : 1;
0065     bool showCodes : 1;
0066 };
0067 
0068 KLanguageButton::KLanguageButton(QWidget *parent)
0069     : QWidget(parent)
0070     , d(new KLanguageButtonPrivate(this))
0071 {
0072 }
0073 
0074 KLanguageButton::KLanguageButton(const QString &text, QWidget *parent)
0075     : QWidget(parent)
0076     , d(new KLanguageButtonPrivate(this))
0077 {
0078     setText(text);
0079 }
0080 
0081 KLanguageButtonPrivate::KLanguageButtonPrivate(KLanguageButton *parent)
0082     : button(new QPushButton(parent))
0083     , popup(new QMenu(parent))
0084     , locale(QLocale::system().name())
0085     , staticText(false)
0086     , showCodes(false)
0087 {
0088     QHBoxLayout *layout = new QHBoxLayout(parent);
0089     layout->setContentsMargins(0, 0, 0, 0);
0090     layout->addWidget(button);
0091 
0092     parent->setFocusProxy(button);
0093     parent->setFocusPolicy(button->focusPolicy());
0094 
0095     button->setMenu(popup);
0096 
0097     QObject::connect(popup, &QMenu::triggered, parent, &KLanguageButton::slotTriggered);
0098     QObject::connect(popup, &QMenu::hovered, parent, &KLanguageButton::slotHovered);
0099 }
0100 
0101 KLanguageButton::~KLanguageButton() = default;
0102 
0103 void KLanguageButton::setText(const QString &text)
0104 {
0105     d->staticText = true;
0106     d->button->setText(text);
0107 }
0108 
0109 void KLanguageButton::setLocale(const QString &locale)
0110 {
0111     d->locale = locale;
0112 }
0113 
0114 void KLanguageButton::showLanguageCodes(bool show)
0115 {
0116     d->showCodes = show;
0117 }
0118 
0119 static QString nameFromEntryFile(const QString &entryFile)
0120 {
0121     const KConfig entry(entryFile, KConfig::SimpleConfig);
0122     const KConfigGroup group(&entry, "KCM Locale");
0123     return group.readEntry("Name", QString());
0124 }
0125 
0126 void KLanguageButton::insertLanguage(const QString &languageCode, const QString &name, int index)
0127 {
0128     QString text;
0129     bool showCodes = d->showCodes;
0130     if (name.isEmpty()) {
0131         const QString entryFile =
0132             QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("locale/") + languageCode + QLatin1String("/kf5_entry.desktop"));
0133         if (QFile::exists(entryFile)) {
0134             text = nameFromEntryFile(entryFile);
0135         }
0136 
0137         if (text.isEmpty()) {
0138             text = languageCode;
0139             QLocale locale(languageCode);
0140             if (locale != QLocale::c()) {
0141                 text = locale.nativeLanguageName();
0142                 // For some languages the native name might be empty.
0143                 // In this case use the non native language name as fallback.
0144                 // See: QTBUG-51323
0145                 text = text.isEmpty() ? QLocale::languageToString(locale.language()) : text;
0146             } else {
0147                 showCodes = false;
0148             }
0149         }
0150     } else {
0151         text = name;
0152     }
0153     if (showCodes) {
0154         text += QLatin1String(" (") + languageCode + QLatin1Char(')');
0155     }
0156 
0157     checkInsertPos(d->popup, text, index);
0158     QAction *a = new QAction(QIcon(), text, this);
0159     a->setData(languageCode);
0160     if (index >= 0 && index < d->popup->actions().count() - 1) {
0161         d->popup->insertAction(d->popup->actions()[index], a);
0162     } else {
0163         d->popup->addAction(a);
0164     }
0165     d->ids.append(languageCode);
0166 }
0167 
0168 void KLanguageButton::insertSeparator(int index)
0169 {
0170     if (index >= 0 && index < d->popup->actions().count() - 1) {
0171         d->popup->insertSeparator(d->popup->actions()[index]);
0172     } else {
0173         d->popup->addSeparator();
0174     }
0175 }
0176 
0177 void KLanguageButton::loadAllLanguages()
0178 {
0179     const QStringList localeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("locale"), QStandardPaths::LocateDirectory);
0180     for (const QString &localeDir : localeDirs) {
0181         const QStringList entries = QDir(localeDir).entryList(QDir::Dirs, QDir::Name);
0182         for (const QString &d : entries) {
0183             const QString entryFile = localeDir + QLatin1Char('/') + d + QStringLiteral("/kf5_entry.desktop");
0184             if (QFile::exists(entryFile)) {
0185                 insertLanguage(d);
0186             }
0187         }
0188     }
0189 
0190     d->ids.removeDuplicates();
0191     setCurrentItem(d->locale);
0192 }
0193 
0194 void KLanguageButton::slotTriggered(QAction *a)
0195 {
0196     // qCDebug(KCONFIG_WIDGETS_LOG) << "slotTriggered" << index;
0197     if (!a) {
0198         return;
0199     }
0200 
0201     d->setCurrentItem(a);
0202 
0203     // Forward event from popup menu as if it was emitted from this widget:
0204     Q_EMIT activated(d->current);
0205 }
0206 
0207 void KLanguageButton::slotHovered(QAction *a)
0208 {
0209     // qCDebug(KCONFIG_WIDGETS_LOG) << "slotHovered" << index;
0210 
0211     Q_EMIT highlighted(a->data().toString());
0212 }
0213 
0214 int KLanguageButton::count() const
0215 {
0216     return d->ids.count();
0217 }
0218 
0219 void KLanguageButton::clear()
0220 {
0221     d->clear();
0222 }
0223 
0224 void KLanguageButtonPrivate::clear()
0225 {
0226     ids.clear();
0227     popup->clear();
0228 
0229     if (!staticText) {
0230         button->setText(QString());
0231     }
0232 }
0233 
0234 bool KLanguageButton::contains(const QString &languageCode) const
0235 {
0236     return d->ids.contains(languageCode);
0237 }
0238 
0239 QString KLanguageButton::current() const
0240 {
0241     return d->current.isEmpty() ? QStringLiteral("en") : d->current;
0242 }
0243 
0244 QAction *KLanguageButtonPrivate::findAction(const QString &data) const
0245 {
0246     const auto listActions = popup->actions();
0247     for (QAction *a : listActions) {
0248         if (!a->data().toString().compare(data)) {
0249             return a;
0250         }
0251     }
0252     return nullptr;
0253 }
0254 
0255 void KLanguageButton::setCurrentItem(const QString &languageCode)
0256 {
0257     if (d->ids.isEmpty()) {
0258         return;
0259     }
0260     QAction *a;
0261     if (d->ids.indexOf(languageCode) < 0) {
0262         a = d->findAction(d->ids[0]);
0263     } else {
0264         a = d->findAction(languageCode);
0265     }
0266     if (a) {
0267         d->setCurrentItem(a);
0268     }
0269 }
0270 
0271 void KLanguageButtonPrivate::setCurrentItem(QAction *a)
0272 {
0273     if (!a->data().isValid()) {
0274         return;
0275     }
0276     current = a->data().toString();
0277 
0278     if (!staticText) {
0279         button->setText(a->text());
0280     }
0281 }
0282 
0283 #include "moc_klanguagebutton.cpp"