@@ -0,0 +1,165 @@
1+/*
2+ * Copyright (C) 2016 Canonical Ltd.
3+ *
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.
7+ *
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.
12+ *
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/>.
15+ */
16+
17+#include <QDBusMetaType>
18+#include <QDebug>
19+
20+#include "keyboardLayoutsModel.h"
21+
22+typedef QList<QMap<QString, QString>> StringMapList;
23+Q_DECLARE_METATYPE(StringMapList)
24+
25+struct KeyboardLayoutInfo {
26+ QString id;
27+ QString displayName;
28+ QString shortName; // language
29+ QString layoutAndVariant;
30+};
31+
32+KeyboardLayoutsModel::KeyboardLayoutsModel(QObject *parent)
33+ : QAbstractListModel(parent)
34+{
35+ m_roleNames = {
36+ {LayoutIdRole, "layoutId"},
37+ {DisplayNameRole, "displayName"},
38+ {ShortNameRole, "shortName"},
39+ {LayoutAndVariantRole, "layoutAndVariant"}
40+ };
41+
42+ qDBusRegisterMetaType<StringMapList>();
43+ m_xkbInfo = gnome_xkb_info_new();
44+
45+ connect(this, &KeyboardLayoutsModel::languageChanged, this, &KeyboardLayoutsModel::buildModel);
46+ buildModel();
47+}
48+
49+KeyboardLayoutsModel::~KeyboardLayoutsModel()
50+{
51+ if (m_xkbInfo != nullptr) {
52+ g_object_unref(m_xkbInfo);
53+ }
54+}
55+
56+QString KeyboardLayoutsModel::language() const
57+{
58+ return m_language;
59+}
60+
61+void KeyboardLayoutsModel::setLanguage(const QString &language)
62+{
63+ if (m_language == language)
64+ return;
65+
66+ m_language = language;
67+ Q_EMIT languageChanged(language);
68+}
69+
70+static bool compareLayouts(const KeyboardLayoutInfo &layout0, const KeyboardLayoutInfo &layout1)
71+{
72+ QString name0(layout0.displayName);
73+ QString name1(layout1.displayName);
74+
75+ if (name0 == name1) {
76+ name0 = layout0.shortName;
77+ name1 = layout1.shortName;
78+
79+ if (name0 == name1) {
80+ name0 = layout0.id;
81+ name1 = layout1.id;
82+ }
83+ }
84+
85+ return QString::localeAwareCompare(name0, name1) < 0;
86+}
87+
88+void KeyboardLayoutsModel::buildModel()
89+{
90+ beginResetModel();
91+
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);
99+
100+ m_layouts.clear();
101+ m_layouts.reserve(g_list_length(sources));
102+
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);
106+
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));
112+
113+ if (!layout.shortName.isEmpty()) {
114+ qDebug() << "Appending layout with id:" << layout.id;
115+ m_layouts.append(layout);
116+ }
117+
118+ }
119+ g_list_free(sources);
120+ std::sort(m_layouts.begin(), m_layouts.end(), compareLayouts);
121+ } else {
122+ // TODO
123+ }
124+
125+ endResetModel();
126+}
127+
128+int KeyboardLayoutsModel::rowCount(const QModelIndex &parent) const
129+{
130+ Q_UNUSED(parent)
131+ return m_layouts.count();
132+}
133+
134+QVariant KeyboardLayoutsModel::data(const QModelIndex &index, int role) const
135+{
136+ const int row = index.row();
137+
138+ if (row >= m_layouts.count()) {
139+ qWarning() << Q_FUNC_INFO << "index out of bounds";
140+ return QVariant();
141+ }
142+
143+ KeyboardLayoutInfo layout = m_layouts.at(row);
144+
145+ switch (role) {
146+ case Qt::DisplayRole:
147+ case DisplayNameRole:
148+ return layout.displayName;
149+ case LayoutIdRole:
150+ return layout.id;
151+ case ShortNameRole:
152+ return layout.shortName;
153+ case LayoutAndVariantRole:
154+ return layout.layoutAndVariant;
155+ default: {
156+ qWarning() << Q_FUNC_INFO << "unsupported data role";
157+ return QVariant();
158+ }
159+ }
160+}
161+
162+QHash<int, QByteArray> KeyboardLayoutsModel::roleNames() const
163+{
164+ return m_roleNames;
165+}