@@ -0,0 +1,165 @@
2+ * Copyright (C) 2016 Canonical Ltd.
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
13+ * You should have received a copy of the GNU General Public License along
14+ * with this program. If not, see <http://www.gnu.org/licenses/>.
17+#include <QDBusMetaType>
20+#include "keyboardLayoutsModel.h"
22+typedef QList<QMap<QString, QString>> StringMapList;
23+Q_DECLARE_METATYPE(StringMapList)
25+struct KeyboardLayoutInfo {
28+ QString shortName; // language
29+ QString layoutAndVariant;
32+KeyboardLayoutsModel::KeyboardLayoutsModel(QObject *parent)
33+ : QAbstractListModel(parent)
36+ {LayoutIdRole, "layoutId"},
37+ {DisplayNameRole, "displayName"},
38+ {ShortNameRole, "shortName"},
39+ {LayoutAndVariantRole, "layoutAndVariant"}
42+ qDBusRegisterMetaType<StringMapList>();
43+ m_xkbInfo = gnome_xkb_info_new();
45+ connect(this, &KeyboardLayoutsModel::languageChanged, this, &KeyboardLayoutsModel::buildModel);
49+KeyboardLayoutsModel::~KeyboardLayoutsModel()
51+ if (m_xkbInfo != nullptr) {
52+ g_object_unref(m_xkbInfo);
56+QString KeyboardLayoutsModel::language() const
61+void KeyboardLayoutsModel::setLanguage(const QString &language)
63+ if (m_language == language)
66+ m_language = language;
67+ Q_EMIT languageChanged(language);
70+static bool compareLayouts(const KeyboardLayoutInfo &layout0, const KeyboardLayoutInfo &layout1)
72+ QString name0(layout0.displayName);
73+ QString name1(layout1.displayName);
75+ if (name0 == name1) {
76+ name0 = layout0.shortName;
77+ name1 = layout1.shortName;
79+ if (name0 == name1) {
85+ return QString::localeAwareCompare(name0, name1) < 0;
88+void KeyboardLayoutsModel::buildModel()
92+ if (m_language.isEmpty()) {
93+ GList *sources, *tmp;
94+ const gchar *display_name;
95+ const gchar *short_name;
96+ const gchar *xkb_layout;
97+ const gchar *xkb_variant;
98+ sources = gnome_xkb_info_get_all_layouts(m_xkbInfo);
101+ m_layouts.reserve(g_list_length(sources));
103+ for (tmp = sources; tmp != NULL; tmp = tmp->next) {
104+ gnome_xkb_info_get_layout_info(m_xkbInfo, (const gchar *)tmp->data,
105+ &display_name, &short_name, &xkb_layout, &xkb_variant);
107+ KeyboardLayoutInfo layout;
108+ layout.id = QString::fromUtf8((const gchar *)tmp->data);
109+ layout.shortName = QString::fromUtf8(short_name);
110+ layout.displayName = QString::fromUtf8(display_name);
111+ layout.layoutAndVariant = QString::fromUtf8(g_strconcat(xkb_layout, "+", xkb_variant, NULL));
113+ if (!layout.shortName.isEmpty()) {
114+ qDebug() << "Appending layout with id:" << layout.id;
115+ m_layouts.append(layout);
119+ g_list_free(sources);
120+ std::sort(m_layouts.begin(), m_layouts.end(), compareLayouts);
128+int KeyboardLayoutsModel::rowCount(const QModelIndex &parent) const
131+ return m_layouts.count();
134+QVariant KeyboardLayoutsModel::data(const QModelIndex &index, int role) const
136+ const int row = index.row();
138+ if (row >= m_layouts.count()) {
139+ qWarning() << Q_FUNC_INFO << "index out of bounds";
143+ KeyboardLayoutInfo layout = m_layouts.at(row);
146+ case Qt::DisplayRole:
147+ case DisplayNameRole:
148+ return layout.displayName;
152+ return layout.shortName;
153+ case LayoutAndVariantRole:
154+ return layout.layoutAndVariant;
156+ qWarning() << Q_FUNC_INFO << "unsupported data role";
162+QHash<int, QByteArray> KeyboardLayoutsModel::roleNames() const