Unity 8
keyboardLayoutsModel.cpp
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 #define GNOME_DESKTOP_USE_UNSTABLE_API
21 #include <libgnome-desktop/gnome-xkb-info.h>
22 
23 #include "keyboardLayoutsModel.h"
24 
25 typedef QList<QMap<QString, QString>> StringMapList;
26 Q_DECLARE_METATYPE(StringMapList)
27 
28 KeyboardLayoutsModel::KeyboardLayoutsModel(QObject *parent)
29  : QAbstractListModel(parent)
30 {
31  m_roleNames = {
32  {LayoutIdRole, "layoutId"},
33  {DisplayNameRole, "displayName"},
34  {LanguageRole, "language"}
35  };
36 
37  qDBusRegisterMetaType<StringMapList>();
38 
39  buildModel();
40  connect(this, &KeyboardLayoutsModel::languageChanged, this, &KeyboardLayoutsModel::updateModel);
41 }
42 
43 QString KeyboardLayoutsModel::language() const
44 {
45  return m_language;
46 }
47 
48 void KeyboardLayoutsModel::setLanguage(const QString &language)
49 {
50  if (m_language == language)
51  return;
52 
53  m_language = language;
54  Q_EMIT languageChanged(language);
55 }
56 
57 static bool compareLayouts(const KeyboardLayoutInfo &layout0, const KeyboardLayoutInfo &layout1)
58 {
59  QString name0(layout0.id);
60  QString name1(layout1.id);
61 
62  if (name0 == name1) {
63  name0 = layout0.displayName;
64  name1 = layout1.displayName;
65 
66  if (name0 == name1) {
67  name0 = layout0.language;
68  name1 = layout1.language;
69  }
70  }
71 
72  return QString::localeAwareCompare(name0, name1) < 0;
73 }
74 
75 void KeyboardLayoutsModel::buildModel()
76 {
77  GList *sources, *tmp;
78  const gchar *display_name;
79  const gchar *short_name;
80  const gchar *xkb_layout;
81  const gchar *xkb_variant;
82 
83  GnomeXkbInfo * xkbInfo(gnome_xkb_info_new());
84 
85  sources = gnome_xkb_info_get_all_layouts(xkbInfo);
86 
87  for (tmp = sources; tmp != NULL; tmp = tmp->next) {
88  gboolean result = gnome_xkb_info_get_layout_info(xkbInfo, (const gchar *)tmp->data,
89  &display_name, &short_name, &xkb_layout, &xkb_variant);
90  if (!result) {
91  qWarning() << "Skipping invalid layout";
92  continue;
93  }
94 
95  KeyboardLayoutInfo layout;
96  layout.id = QString::fromUtf8((const gchar *)tmp->data);
97  layout.language = QString::fromUtf8(short_name);
98  layout.displayName = QString::fromUtf8(display_name);
99 
100  m_db.append(layout);
101  }
102  g_list_free(sources);
103  g_object_unref(xkbInfo);
104 
105  std::sort(m_db.begin(), m_db.end(), compareLayouts);
106 }
107 
108 void KeyboardLayoutsModel::updateModel()
109 {
110  beginResetModel();
111  m_layouts.clear();
112 
113  const QString lang = m_language.section("_", 0, 0);
114  const QString country = m_language.section("_", 1, 1).toLower();
115 
116  Q_FOREACH(const KeyboardLayoutInfo & info, m_db) {
117  const QString kbdCountry = info.id.section("+", 0, 0);
118  if (info.language == lang && // filter by language
119  (kbdCountry.startsWith(country) || kbdCountry.length() > 2)) { // and by known country, also insert anything that doesn't match the country
120  m_layouts.append(info);
121  }
122  }
123 
124  std::sort(m_layouts.begin(), m_layouts.end(), compareLayouts);
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  const 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 LanguageRole:
152  return layout.language;
153  default: {
154  qWarning() << Q_FUNC_INFO << "unsupported data role";
155  return QVariant();
156  }
157  }
158 }
159 
160 QHash<int, QByteArray> KeyboardLayoutsModel::roleNames() const
161 {
162  return m_roleNames;
163 }