Merge lp:~jonas-drange/ubuntu-system-settings/hwKeyboardMinimal into lp:ubuntu-system-settings

Proposed by Jonas G. Drange
Status: Merged
Approved by: Jonas G. Drange
Approved revision: 1589
Merged at revision: 1623
Proposed branch: lp:~jonas-drange/ubuntu-system-settings/hwKeyboardMinimal
Merge into: lp:ubuntu-system-settings
Diff against target: 1707 lines (+1014/-345)
17 files modified
debian/control (+1/-0)
plugins/language/CMakeLists.txt (+7/-4)
plugins/language/KeyboardLayoutItem.qml (+65/-34)
plugins/language/KeyboardLayouts.qml (+114/-5)
plugins/language/PageComponent.qml (+34/-19)
plugins/language/PageHardwareKeyboard.qml (+75/-0)
plugins/language/hardwarekeyboard-plugin.cpp (+224/-0)
plugins/language/hardwarekeyboard-plugin.h (+75/-0)
plugins/language/keyboard-layout.cpp (+3/-1)
plugins/language/language-plugin.cpp (+13/-261)
plugins/language/language-plugin.h (+2/-20)
plugins/language/onscreenkeyboard-plugin.cpp (+294/-0)
plugins/language/onscreenkeyboard-plugin.h (+75/-0)
plugins/language/plugin.cpp (+4/-0)
plugins/language/subset-model.cpp (+24/-0)
plugins/language/subset-model.h (+3/-0)
tests/autopilot/ubuntu_system_settings/__init__.py (+1/-1)
To merge this branch: bzr merge lp:~jonas-drange/ubuntu-system-settings/hwKeyboardMinimal
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ken VanDine Approve
Review via email: mp+281213@code.launchpad.net

Commit message

Add External Keyboard panel which allows for configuration of the default keymap.

Description of the change

* Refactors some bits in the language plugin and keyboard layouts to make them work well with both OSK and external keyboards.
* Add a External Keyboard panel that allows for manipulation of input sources.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ken VanDine (ken-vandine) wrote :

See inline comments

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

Some inline comments

Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

Addressing comments from Lukáš and Ken. Thanks guys!

Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

See inline comments

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ken VanDine (ken-vandine) wrote :

I'm happy with the code, during testing it didn't appear to show the hardware keyboard stuff. I'm guessing UnityInputInfo.keyboards is not > 0 even with the bt keyboard connected, is that expected?

review: Needs Information
Revision history for this message
Ken VanDine (ken-vandine) wrote :

I got it working (I had install problems before) however I noticed the first time I open the lang panel it shows 0 layouts for the hardware keyboards. Shouldn't there be some default set?

review: Needs Information
Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

> I got it working (I had install problems before) however I noticed the first
> time I open the lang panel it shows 0 layouts for the hardware keyboards.
> Shouldn't there be some default set?

InputSources could be empty. If we want a default, then it needs to be set in the AccountsService schema.

Revision history for this message
Ken VanDine (ken-vandine) wrote :

OK, I think there should be a default, but that has to be set elsewhere.

review: Approve
Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

On 9 February 2016 at 15:26, Ken VanDine <email address hidden> wrote:

> Review: Approve
>
> OK, I think there should be a default, but that has to be set elsewhere.
>

​I agree and filed bug 1543617​. Thanks!

Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

I'm still not getting the panel on my laptop, only on the phone

Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

On 11 February 2016 at 22:01, Lukáš Tinkl <email address hidden> wrote:

> I'm still not getting the panel on my laptop, only on the phone
>

​It could be that the input detector is lacking. We'll then have to wait
for [1] to land. We can do that.

[1] https://requests.ci-train.ubuntu.com/#/ticket/763

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1589. By Jonas G. Drange

sync with trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2016-03-12 03:41:03 +0000
3+++ debian/control 2016-03-14 14:37:14 +0000
4@@ -31,6 +31,7 @@
5 qtbase5-dev,
6 qtdeclarative5-dev,
7 libapt-pkg-dev,
8+ libgnome-desktop-3-dev,
9 # test-deps
10 qml-module-qttest,
11 qml-module-qtquick2,
12
13=== modified file 'plugins/language/CMakeLists.txt'
14--- plugins/language/CMakeLists.txt 2014-09-02 15:44:19 +0000
15+++ plugins/language/CMakeLists.txt 2016-03-14 14:37:14 +0000
16@@ -1,4 +1,5 @@
17-include_directories(${GLIB_INCLUDE_DIRS} ${ACCOUNTSSERVICE_INCLUDE_DIRS} ${ICU_INCLUDE_DIRS})
18+pkg_search_module(GD3 REQUIRED gnome-desktop-3.0)
19+include_directories(${GD3_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS} ${ACCOUNTSSERVICE_INCLUDE_DIRS} ${ICU_INCLUDE_DIRS})
20 add_definitions(-DQT_NO_KEYWORDS)
21
22 set(QML_SOURCES
23@@ -6,17 +7,19 @@
24 KeyboardLayoutItem.qml
25 KeyboardLayouts.qml
26 PageComponent.qml
27+ PageHardwareKeyboard.qml
28 RebootNecessary.qml
29 SpellChecking.qml
30 SubsetView.qml
31 )
32
33+
34 add_library(UbuntuLanguagePlugin MODULE
35- keyboard-layout.cpp language-plugin.cpp plugin.cpp subset-model.cpp
36- keyboard-layout.h language-plugin.h plugin.h subset-model.h
37+ keyboard-layout.cpp language-plugin.cpp plugin.cpp subset-model.cpp onscreenkeyboard-plugin.cpp hardwarekeyboard-plugin.cpp
38+ keyboard-layout.h language-plugin.h plugin.h subset-model.h onscreenkeyboard-plugin.h hardwarekeyboard-plugin.h
39 ${QML_SOURCES})
40 qt5_use_modules(UbuntuLanguagePlugin Qml Quick DBus)
41-target_link_libraries(UbuntuLanguagePlugin uss-sessionservice ${GLIB_LDFLAGS} ${GIO_LDFLAGS} ${ACCOUNTSSERVICE_LDFLAGS} ${ICU_LDFLAGS})
42+target_link_libraries(UbuntuLanguagePlugin uss-accountsservice uss-sessionservice ${GD3_LDFLAGS} ${GLIB_LDFLAGS} ${GIO_LDFLAGS} ${ACCOUNTSSERVICE_LDFLAGS} ${ICU_LDFLAGS})
43
44 set(PLUG_DIR ${PLUGIN_PRIVATE_MODULE_DIR}/Ubuntu/SystemSettings/LanguagePlugin)
45 install(TARGETS UbuntuLanguagePlugin DESTINATION ${PLUG_DIR})
46
47=== modified file 'plugins/language/KeyboardLayoutItem.qml'
48--- plugins/language/KeyboardLayoutItem.qml 2015-08-10 13:31:45 +0000
49+++ plugins/language/KeyboardLayoutItem.qml 2016-03-14 14:37:14 +0000
50@@ -22,49 +22,80 @@
51 import Ubuntu.Components 1.3
52 import Ubuntu.Components.ListItems 1.3 as ListItem
53
54-ListItem.Base {
55+ListItem.Empty {
56+ id: root
57 property alias name: name.text
58 property alias checked: checkBox.checked
59 property alias shortName: shortName.text
60-
61- Row {
62- anchors.top: parent.top
63- anchors.left: parent.left
64- anchors.bottom: parent.bottom
65- spacing: units.gu(1)
66-
67- Rectangle {
68- width: units.gu(3.0)
69- height: units.gu(3.0)
70- radius: units.gu(0.5)
71-
72- color: Theme.palette.normal.backgroundText
73-
74- anchors.verticalCenter: parent.verticalCenter
75-
76- Label {
77- id: shortName
78-
79- color: Theme.palette.normal.background
80- fontSize: "small"
81-
82- anchors.centerIn: parent
83- }
84+ property alias draggable: dragHandle.visible
85+ property alias dragger: dragArea.drag
86+
87+ signal dragStarted()
88+ signal dragFinished()
89+
90+ Rectangle {
91+ id: icon
92+ anchors {
93+ left: parent.left
94+ leftMargin: units.gu(2)
95 }
96+ width: units.gu(3.0)
97+ height: units.gu(3.0)
98+ radius: units.gu(0.5)
99+
100+ color: Theme.palette.normal.backgroundText
101+
102+ anchors.verticalCenter: parent.verticalCenter
103
104 Label {
105- id: name
106-
107- anchors.verticalCenter: parent.verticalCenter
108- }
109+ id: shortName
110+
111+ color: Theme.palette.normal.background
112+ fontSize: "small"
113+
114+ anchors.centerIn: parent
115+ }
116+ }
117+
118+ Label {
119+ id: name
120+ anchors {
121+ left: icon.right
122+ leftMargin: units.gu(2)
123+ right: dragHandle.visible ? dragHandle.left : checkBox.left
124+ rightMargin: units.gu(3)
125+ }
126+ elide: Text.ElideMiddle
127+ anchors.verticalCenter: parent.verticalCenter
128+ }
129+
130+ Icon {
131+ id: dragHandle
132+ width: units.gu(2.5)
133+ height: parent.height
134+ anchors {
135+ right: checkBox.left
136+ rightMargin: units.gu(3)
137+ verticalCenter: parent.verticalCenter
138+ }
139+
140+ MouseArea {
141+ id: dragArea
142+ anchors.fill: parent
143+
144+ onPressed: root.dragStarted()
145+ onReleased: root.dragFinished()
146+ }
147+
148+ name: "grip-large"
149 }
150
151 CheckBox {
152 id: checkBox
153-
154- anchors.right: parent.right
155- anchors.verticalCenter: parent.verticalCenter
156+ anchors {
157+ right: parent.right
158+ rightMargin: units.gu(2)
159+ verticalCenter: parent.verticalCenter
160+ }
161 }
162-
163- onClicked: checked = !checked
164 }
165
166=== modified file 'plugins/language/KeyboardLayouts.qml'
167--- plugins/language/KeyboardLayouts.qml 2015-08-06 18:59:59 +0000
168+++ plugins/language/KeyboardLayouts.qml 2016-03-14 14:37:14 +0000
169@@ -18,16 +18,23 @@
170 * with this program. If not, see <http://www.gnu.org/licenses/>.
171 */
172
173+import QtQuick 2.4
174 import SystemSettings 1.0
175+import Ubuntu.Components 1.3
176 import Ubuntu.Components.ListItems 1.3 as ListItem
177 import Ubuntu.SystemSettings.LanguagePlugin 1.0
178
179 ItemPage {
180+ id: root
181 title: i18n.tr("Keyboard layouts")
182
183- UbuntuLanguagePlugin {
184- id: plugin
185- }
186+ property var plugin
187+ property bool currentLayoutsDraggable: false
188+ property bool draggingCurrentLayouts: false
189+ property double originalContentY: 0
190+
191+ // Empty height + ThinDivider height
192+ readonly property double listItemHeight: units.gu(6) + units.dp(2)
193
194 SubsetView {
195 id: subsetView
196@@ -36,16 +43,36 @@
197
198 anchors.fill: parent
199
200- subsetLabel: i18n.tr("Current layouts:")
201- supersetLabel: i18n.tr("All layouts available:")
202+ section.property: "subset"
203+ section.delegate: ListItem.Standard {
204+ text: section == "true" ? i18n.tr("Current layouts:") : i18n.tr("All layouts available:")
205+
206+ // Fade out if it's “All layouts available” and we're draggingCurrentLayouts
207+ opacity: section != "true" && draggingCurrentLayouts ? 0 : 1
208+ }
209
210 model: plugin.keyboardLayoutsModel
211 delegate: KeyboardLayoutItem {
212+ id: item
213+ anchors {
214+ left: parent.left
215+ right: parent.right
216+ }
217+
218+ Behavior on height { enabled: visible; UbuntuNumberAnimation { } }
219+
220 name: model.language
221 shortName: model.icon
222 checked: model.checked
223 enabled: model.enabled
224
225+ draggable: (currentLayoutsDraggable &&
226+ model.subset &&
227+ subsetView.model.subset.length > 1)
228+
229+ visible: root.draggingCurrentLayouts ? model.subset : true
230+ opacity: root.draggingCurrentLayouts ? 0.75 : 1
231+
232 onCheckedChanged: {
233 var element = model.index < subsetView.model.subset.length ?
234 subsetView.model.subset[model.index] :
235@@ -55,6 +82,88 @@
236
237 checked = Qt.binding(function() { return model.checked })
238 }
239+
240+ onDragStarted: {
241+
242+ // If the element is not checked, refuse dragging.
243+ if (!model.checked) {
244+ return;
245+ }
246+ root.draggingCurrentLayouts = true;
247+
248+ // Force scroll to top
249+ subsetView.contentY = -listItemHeight
250+
251+ dragger.target = dragItem;
252+ dragger.maximumX = units.gu(1);
253+ dragger.minimumX = units.gu(1);
254+
255+ dragger.minimumY = listItemHeight + (listItemHeight / 2)
256+ dragger.maximumY = listItemHeight * (0.5 + subsetView.model.subset.length)
257+
258+ dragItem.name = name;
259+ dragItem.shortName = shortName;
260+ dragItem.checked = checked;
261+ dragItem.enabled = enabled;
262+ dragItem.originalY = mapToItem(root, 0, 0).y;
263+ dragItem.originalIndex = index;
264+ dragItem.y = dragItem.originalY;
265+ dragItem.x = units.gu(1);
266+ dragItem.visible = true;
267+ dragItem.elementToShrink = item;
268+ }
269+
270+ onDragFinished: {
271+ root.draggingCurrentLayouts = false;
272+ dragger.target = undefined;
273+ dragItem.visible = false;
274+ if (dragMarker.visible && dragMarker.index != index) {
275+ plugin.requestCurrentLayoutMove(dragItem.originalIndex, dragMarker.index);
276+ }
277+ dragMarker.visible = false;
278+ dragItem.elementToShrink.height = listItemHeight;
279+ dragItem.elementToShrink.clip = false;
280+ dragItem.elementToShrink = null;
281+ }
282+ }
283+ }
284+
285+ ListItem.ThinDivider {
286+ id: dragMarker
287+ visible: false
288+ property int index: {
289+ var midOfDragItem = dragItem.y - (dragItem.height / 2) - listItemHeight;
290+ var origi = Math.round(midOfDragItem / listItemHeight)
291+ var i = Math.round(midOfDragItem / listItemHeight)
292+ if (i < 0) i = 0;
293+ if (i >= subsetView.model.subset.length - 1) {
294+ i = subsetView.model.subset.length - 1;
295+ }
296+ return i;
297+ }
298+ y: ((index + 2) * listItemHeight) - height / 2
299+ height: units.gu(1)
300+ }
301+
302+ KeyboardLayoutItem {
303+ id: dragItem
304+
305+ property real originalY
306+ property int originalIndex
307+ property var elementToShrink: null
308+
309+ objectName: "dragItem"
310+ visible: false
311+ opacity: 0.9
312+ style: Rectangle {
313+ color: Theme.palette.selected.background
314+ }
315+ onYChanged: {
316+ if (!dragMarker.visible && Math.abs(y - originalY) >= height / 2) {
317+ dragMarker.visible = true;
318+ dragItem.elementToShrink.clip = true;
319+ elementToShrink.height = 0.01;
320+ }
321 }
322 }
323 }
324
325=== modified file 'plugins/language/PageComponent.qml'
326--- plugins/language/PageComponent.qml 2015-08-10 13:31:45 +0000
327+++ plugins/language/PageComponent.qml 2016-03-14 14:37:14 +0000
328@@ -19,6 +19,7 @@
329 */
330
331 import QtQuick 2.4
332+import QtSystemInfo 5.5
333 import GSettings 1.0
334 import SystemSettings 1.0
335 import Ubuntu.Components 1.3
336@@ -33,10 +34,21 @@
337
338 title: i18n.tr("Language & Text")
339
340+ InputDeviceManager {
341+ id: keyboardsModel
342+ filter: InputInfo.Keyboard
343+ }
344+
345+ property bool externalKeyboardPresent: keyboardsModel.count > 0
346+
347 UbuntuLanguagePlugin {
348 id: plugin
349 }
350
351+ OnScreenKeyboardPlugin {
352+ id: oskPlugin
353+ }
354+
355 Component {
356 id: displayLanguage
357
358@@ -50,12 +62,6 @@
359 }
360
361 Component {
362- id: keyboardLayouts
363-
364- KeyboardLayouts {}
365- }
366-
367- Component {
368 id: spellChecking
369
370 SpellChecking {}
371@@ -100,6 +106,7 @@
372 iconSource: "image://theme/language-chooser"
373 text: i18n.tr("Display language…")
374 objectName: "displayLanguage"
375+ showDivider: false
376 component: Label {
377 property int currentLanguage: plugin.currentLanguage
378 objectName: "currentLanguage"
379@@ -111,21 +118,29 @@
380 onClicked: PopupUtils.open(displayLanguage)
381 }
382
383- ListItem.Divider {
384- }
385+ ListItem.Divider {}
386
387 ListItem.SingleValue {
388- text: i18n.tr("Keyboard layouts")
389- value: plugin.keyboardLayoutsModel.subset.length == 1 ?
390- plugin.keyboardLayoutsModel.superset[plugin.keyboardLayoutsModel.subset[0]][0] :
391- plugin.keyboardLayoutsModel.subset.length
392- progression: true
393-
394- onClicked: pageStack.push(keyboardLayouts)
395- }
396-
397- ListItem.Divider {
398- }
399+ text: externalKeyboardPresent ? i18n.tr("On-screen keyboard") :
400+ i18n.tr("Keyboard layouts")
401+ progression: true
402+ value: oskPlugin.keyboardLayoutsModel.subset.length == 1 ?
403+ oskPlugin.keyboardLayoutsModel.superset[oskPlugin.keyboardLayoutsModel.subset[0]][0] :
404+ oskPlugin.keyboardLayoutsModel.subset.length
405+ onClicked: pageStack.push(Qt.resolvedUrl("KeyboardLayouts.qml"), {
406+ plugin: oskPlugin
407+ })
408+ }
409+
410+ ListItem.Standard {
411+ text: i18n.tr("External keyboard")
412+ progression: true
413+ showDivider: false
414+ onClicked: pageStack.push(Qt.resolvedUrl("PageHardwareKeyboard.qml"))
415+ visible: externalKeyboardPresent
416+ }
417+
418+ ListItem.Divider {}
419
420 ListItem.SingleValue {
421 visible: showAllUI
422
423=== added file 'plugins/language/PageHardwareKeyboard.qml'
424--- plugins/language/PageHardwareKeyboard.qml 1970-01-01 00:00:00 +0000
425+++ plugins/language/PageHardwareKeyboard.qml 2016-03-14 14:37:14 +0000
426@@ -0,0 +1,75 @@
427+/*
428+ * This file is part of system-settings
429+ *
430+ * Copyright (C) 2015 Canonical Ltd.
431+ *
432+ * Contact: William Hua <william.hua@canonical.com>
433+ * Jonas G. Drange <jonas.drange@canonical.com>
434+ *
435+ * This program is free software: you can redistribute it and/or modify it
436+ * under the terms of the GNU General Public License version 3, as published
437+ * by the Free Software Foundation.
438+ *
439+ * This program is distributed in the hope that it will be useful, but
440+ * WITHOUT ANY WARRANTY; without even the implied warranties of
441+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
442+ * PURPOSE. See the GNU General Public License for more details.
443+ *
444+ * You should have received a copy of the GNU General Public License along
445+ * with this program. If not, see <http://www.gnu.org/licenses/>.
446+ */
447+
448+import QtQuick 2.0
449+import SystemSettings 1.0
450+import Ubuntu.Components 1.3
451+import Ubuntu.Components.ListItems 1.3 as ListItem
452+import Ubuntu.Settings.Components 0.1 as USC
453+import Ubuntu.Settings.Menus 0.1 as Menus
454+import Ubuntu.SystemSettings.LanguagePlugin 1.0
455+
456+ItemPage {
457+ id: root
458+ objectName: "hwKbdPage"
459+
460+ title: i18n.tr("Hardware keyboard")
461+
462+ Component {
463+ id: keyboardLayouts
464+
465+ KeyboardLayouts {}
466+ }
467+
468+ HardwareKeyboardPlugin {
469+ id: plugin
470+ }
471+
472+ Flickable {
473+ anchors.fill: parent
474+ contentHeight: contentItem.childrenRect.height
475+ boundsBehavior: contentHeight > root.height ?
476+ Flickable.DragAndOvershootBounds :
477+ Flickable.StopAtBounds
478+ /* Set the direction to workaround https://bugreports.qt-project.org/browse/QTBUG-31905
479+ otherwise the UI might end up in a situation where scrolling doesn't work */
480+ flickableDirection: Flickable.VerticalFlick
481+
482+ Column {
483+ anchors.left: parent.left
484+ anchors.right: parent.right
485+
486+
487+ ListItem.SingleValue {
488+ text: i18n.tr("Layouts and other sources")
489+ value: plugin.keyboardLayoutsModel.subset.length == 1 ?
490+ plugin.keyboardLayoutsModel.superset[plugin.keyboardLayoutsModel.subset[0]][0] :
491+ plugin.keyboardLayoutsModel.subset.length
492+ progression: true
493+
494+ onClicked: pageStack.push(Qt.resolvedUrl("KeyboardLayouts.qml"), {
495+ plugin: plugin,
496+ currentLayoutsDraggable: true
497+ })
498+ }
499+ }
500+ }
501+}
502
503=== added file 'plugins/language/hardwarekeyboard-plugin.cpp'
504--- plugins/language/hardwarekeyboard-plugin.cpp 1970-01-01 00:00:00 +0000
505+++ plugins/language/hardwarekeyboard-plugin.cpp 2016-03-14 14:37:14 +0000
506@@ -0,0 +1,224 @@
507+/*
508+ * This file is part of system-settings
509+ *
510+ * Copyright (C) 2015 Canonical Ltd.
511+ *
512+ * Contact: William Hua <william.hua@canonical.com>
513+ * Jonas G. Drange <jonas.drange@canonical.com>
514+ *
515+ * This program is free software: you can redistribute it and/or modify it
516+ * under the terms of the GNU General Public License version 3, as published
517+ * by the Free Software Foundation.
518+ *
519+ * This program is distributed in the hope that it will be useful, but
520+ * WITHOUT ANY WARRANTY; without even the implied warranties of
521+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
522+ * PURPOSE. See the GNU General Public License for more details.
523+ *
524+ * You should have received a copy of the GNU General Public License along
525+ * with this program. If not, see <http://www.gnu.org/licenses/>.
526+ */
527+
528+#include <QDBusMetaType>
529+#include <QtAlgorithms>
530+#include <QtDebug>
531+#include "hardwarekeyboard-plugin.h"
532+
533+#define INPUT_SOURCE_TYPE_XKB "xkb"
534+
535+typedef QList<QMap<QString, QString>> StringMapList;
536+Q_DECLARE_METATYPE(StringMapList)
537+
538+HardwareKeyboardPlugin::HardwareKeyboardPlugin(QObject *parent) :
539+ QObject(parent)
540+{
541+ qDBusRegisterMetaType<StringMapList>();
542+ m_xkbInfo = gnome_xkb_info_new();
543+
544+ updateKeyboardLayouts();
545+ updateKeyboardLayoutsModel();
546+}
547+
548+
549+HardwareKeyboardPlugin::~HardwareKeyboardPlugin()
550+{
551+ if (m_xkbInfo != nullptr) {
552+ g_object_unref(m_xkbInfo);
553+ }
554+
555+ qDeleteAll(m_keyboardLayouts);
556+}
557+
558+SubsetModel *
559+HardwareKeyboardPlugin::keyboardLayoutsModel()
560+{
561+ return &m_keyboardLayoutsModel;
562+}
563+
564+void
565+HardwareKeyboardPlugin::keyboardLayoutsModelChanged()
566+{
567+ QVariant answer = m_accountsService.getUserProperty(
568+ "org.freedesktop.Accounts.User",
569+ "InputSources");
570+ StringMapList maps;
571+ if (answer.isValid()) {
572+ QDBusArgument arg = answer.value<QDBusArgument>();
573+ maps = qdbus_cast<StringMapList>(arg);
574+ } else {
575+ qCritical() << "failed to get input sources";
576+ return;
577+ }
578+
579+ StringMapList finalMaps = StringMapList();
580+ for (int i = 0; i < maps.size(); i++) {
581+ QMap<QString, QString> m = maps.at(i);
582+
583+ // Keep any maps not of xkb type (ibus e.g.)
584+ if (!m.contains(INPUT_SOURCE_TYPE_XKB)) {
585+ finalMaps.append(m);
586+ }
587+ }
588+
589+ // Update maps with what the user selected.
590+ QList<int> subset = m_keyboardLayoutsModel.subset();
591+
592+ // The first top item selected by the user will appear as the first map.
593+ QListIterator<int> it(subset);
594+ it.toBack();
595+ while (it.hasPrevious()) {
596+ QMap<QString, QString> m = QMap<QString, QString>();
597+ KeyboardLayout* layout = m_keyboardLayouts.at(it.previous());
598+ m.insert(INPUT_SOURCE_TYPE_XKB, layout->name());
599+ finalMaps.prepend(m);
600+ }
601+
602+ m_accountsService.customSetUserProperty(
603+ "SetInputSources", QVariant::fromValue(finalMaps));
604+}
605+
606+static bool
607+compareLayouts(const KeyboardLayout *layout0,
608+ const KeyboardLayout *layout1)
609+{
610+ QString name0(layout0->displayName());
611+ QString name1(layout1->displayName());
612+
613+ if (name0 == name1) {
614+ name0 = layout0->language();
615+ name1 = layout1->language();
616+
617+ if (name0 == name1) {
618+ name0 = layout0->name();
619+ name1 = layout1->name();
620+ }
621+ }
622+
623+ return QString::localeAwareCompare(name0, name1) < 0;
624+}
625+
626+void
627+HardwareKeyboardPlugin::updateKeyboardLayouts()
628+{
629+ GList *sources, *tmp;
630+ gchar *source_id = NULL;
631+ const gchar *display_name;
632+ const gchar *short_name;
633+ const gchar *xkb_layout;
634+ const gchar *xkb_variant;
635+ sources = gnome_xkb_info_get_all_layouts(m_xkbInfo);
636+
637+ m_keyboardLayouts.clear();
638+
639+ for (tmp = sources; tmp != NULL; tmp = tmp->next) {
640+ g_free (source_id);
641+ source_id = g_strconcat(INPUT_SOURCE_TYPE_XKB, tmp->data, NULL);
642+
643+ gnome_xkb_info_get_layout_info(m_xkbInfo, (const gchar *)tmp->data,
644+ &display_name, &short_name, &xkb_layout, &xkb_variant);
645+
646+ KeyboardLayout *layout(new KeyboardLayout((const gchar *)tmp->data,
647+ short_name,
648+ display_name,
649+ xkb_variant));
650+ if (!layout->language().isEmpty())
651+ m_keyboardLayouts += layout;
652+ else
653+ delete layout;
654+
655+ }
656+ g_free(source_id);
657+ g_list_free(sources);
658+ qSort(m_keyboardLayouts.begin(), m_keyboardLayouts.end(), compareLayouts);
659+}
660+
661+void
662+HardwareKeyboardPlugin::updateKeyboardLayoutsModel()
663+{
664+ QStringList customRoles;
665+ customRoles += "language";
666+ customRoles += "icon";
667+
668+ m_keyboardLayoutsModel.setCustomRoles(customRoles);
669+
670+ QVariantList superset;
671+
672+ for (QList<KeyboardLayout *>::const_iterator
673+ i(m_keyboardLayouts.begin()); i != m_keyboardLayouts.end(); ++i) {
674+ QVariantList element;
675+
676+ if (!(*i)->displayName().isEmpty())
677+ element += (*i)->displayName();
678+ else
679+ element += (*i)->name();
680+
681+ element += (*i)->shortName();
682+ superset += QVariant(element);
683+ }
684+
685+ m_keyboardLayoutsModel.setSuperset(superset);
686+
687+ enabledLayoutsChanged();
688+
689+ connect(&m_keyboardLayoutsModel,
690+ SIGNAL(subsetChanged()),
691+ SLOT(keyboardLayoutsModelChanged()));
692+}
693+
694+void
695+HardwareKeyboardPlugin::enabledLayoutsChanged()
696+{
697+ QList<int> subset;
698+
699+ QVariant answer = m_accountsService.getUserProperty(
700+ "org.freedesktop.Accounts.User",
701+ "InputSources");
702+
703+ if (answer.isValid()) {
704+ QDBusArgument arg = answer.value<QDBusArgument>();
705+ StringMapList list = qdbus_cast<StringMapList>(arg);
706+
707+ for (int i = 0; i < list.length(); ++i) {
708+ for (int j = 0; j < m_keyboardLayouts.length(); j++) {
709+ if (m_keyboardLayouts[j]->name() == list.at(i)[INPUT_SOURCE_TYPE_XKB]) {
710+ subset += j;
711+ break;
712+ }
713+ }
714+ }
715+ m_keyboardLayoutsModel.setSubset(subset);
716+ } else {
717+ qCritical() << "failed to get input sources";
718+ }
719+}
720+
721+void HardwareKeyboardPlugin::setCurrentLayout(const QString &code)
722+{
723+ Q_UNUSED(code);
724+ // TODO: Implement.
725+}
726+
727+void HardwareKeyboardPlugin::requestCurrentLayoutMove(const int from, const int to) {
728+ m_keyboardLayoutsModel.moveSubsetRow(from, to);
729+ keyboardLayoutsModelChanged();
730+}
731
732=== added file 'plugins/language/hardwarekeyboard-plugin.h'
733--- plugins/language/hardwarekeyboard-plugin.h 1970-01-01 00:00:00 +0000
734+++ plugins/language/hardwarekeyboard-plugin.h 2016-03-14 14:37:14 +0000
735@@ -0,0 +1,75 @@
736+/*
737+ * This file is part of system-settings
738+ *
739+ * Copyright (C) 2015 Canonical Ltd.
740+ *
741+ * Contact: William Hua <william.hua@canonical.com>
742+ * Jonas G. Drange <jonas.drange@canonical.com>
743+ *
744+ * This program is free software: you can redistribute it and/or modify it
745+ * under the terms of the GNU General Public License version 3, as published
746+ * by the Free Software Foundation.
747+ *
748+ * This program is distributed in the hope that it will be useful, but
749+ * WITHOUT ANY WARRANTY; without even the implied warranties of
750+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
751+ * PURPOSE. See the GNU General Public License for more details.
752+ *
753+ * You should have received a copy of the GNU General Public License along
754+ * with this program. If not, see <http://www.gnu.org/licenses/>.
755+ */
756+
757+#ifndef HWKBD_PLUGIN_H
758+#define HWKBD_PLUGIN_H
759+
760+#include <QtCore>
761+#include <gio/gio.h>
762+#include <QDBusArgument>
763+
764+#define GNOME_DESKTOP_USE_UNSTABLE_API
765+#include <libgnome-desktop/gnome-xkb-info.h>
766+
767+#include "accountsservice.h"
768+#include "subset-model.h"
769+#include "keyboard-layout.h"
770+
771+typedef void *gpointer;
772+typedef char gchar;
773+
774+class KeyboardLayout;
775+
776+class HardwareKeyboardPlugin : public QObject
777+{
778+private:
779+
780+ Q_OBJECT
781+
782+public:
783+
784+ Q_PROPERTY(SubsetModel *keyboardLayoutsModel
785+ READ keyboardLayoutsModel
786+ CONSTANT)
787+
788+ explicit HardwareKeyboardPlugin(QObject *parent = nullptr);
789+
790+ virtual ~HardwareKeyboardPlugin();
791+
792+ Q_INVOKABLE void setCurrentLayout(const QString &code);
793+ Q_INVOKABLE void requestCurrentLayoutMove(const int from, const int to);
794+
795+ SubsetModel *keyboardLayoutsModel();
796+ Q_SLOT void keyboardLayoutsModelChanged();
797+ Q_SLOT void enabledLayoutsChanged();
798+
799+private:
800+ void updateEnabledLayouts();
801+ void updateKeyboardLayouts();
802+ void updateKeyboardLayoutsModel();
803+
804+ GnomeXkbInfo *m_xkbInfo;
805+ QList<KeyboardLayout *> m_keyboardLayouts;
806+ SubsetModel m_keyboardLayoutsModel;
807+ AccountsService m_accountsService;
808+};
809+
810+#endif // HWKBD_PLUGIN_H
811
812=== modified file 'plugins/language/keyboard-layout.cpp'
813--- plugins/language/keyboard-layout.cpp 2014-07-23 13:44:31 +0000
814+++ plugins/language/keyboard-layout.cpp 2016-03-14 14:37:14 +0000
815@@ -30,8 +30,10 @@
816 m_name(name),
817 m_language(language),
818 m_displayName(displayName),
819- m_shortName(shortName)
820+ m_shortName(language)
821 {
822+ Q_UNUSED(shortName);
823+ m_shortName[0] = m_shortName[0].toUpper();
824 }
825
826 KeyboardLayout::KeyboardLayout(const QFileInfo &fileInfo,
827
828=== modified file 'plugins/language/language-plugin.cpp'
829--- plugins/language/language-plugin.cpp 2015-05-22 19:16:25 +0000
830+++ plugins/language/language-plugin.cpp 2016-03-14 14:37:14 +0000
831@@ -17,21 +17,14 @@
832 * You should have received a copy of the GNU General Public License along
833 * with this program. If not, see <http://www.gnu.org/licenses/>.
834 */
835-
836+#include <QtDebug>
837 #include "language-plugin.h"
838 #include "keyboard-layout.h"
839
840 #include <act/act.h>
841 #include <unicode/locid.h>
842
843-#define UBUNTU_KEYBOARD_SCHEMA_ID "com.canonical.keyboard.maliit"
844-
845-#define KEY_ENABLED_LAYOUTS "enabled-languages"
846-#define KEY_CURRENT_LAYOUT "active-language"
847-#define KEY_PLUGIN_PATHS "plugin-paths"
848-
849 #define LANGUAGE2LOCALE "/usr/share/language-tools/language2locale"
850-#define LAYOUTS_DIR "/usr/share/maliit/plugins/com/ubuntu/lib"
851
852 static const char * const LOCALE_BLACKLIST[] = {
853 "C",
854@@ -94,12 +87,8 @@
855 m_currentLanguage(-1),
856 m_nextCurrentLanguage(-1),
857 m_manager(act_user_manager_get_default()),
858- m_user(nullptr),
859- m_maliitSettings(g_settings_new(UBUNTU_KEYBOARD_SCHEMA_ID))
860+ m_user(nullptr)
861 {
862- GVariantIter *iter;
863- const gchar *path;
864-
865 if (m_manager != nullptr) {
866 g_object_ref(m_manager);
867
868@@ -113,16 +102,8 @@
869 G_CALLBACK(::managerLoaded), this);
870 }
871
872- m_layoutPaths.append(LAYOUTS_DIR);
873- g_settings_get(m_maliitSettings, KEY_PLUGIN_PATHS, "as", &iter);
874- for (int i(0); g_variant_iter_next(iter, "&s", &path); i++) {
875- m_layoutPaths.append(path);
876- }
877 updateLanguageNamesAndCodes();
878 updateCurrentLanguage();
879- updateEnabledLayouts();
880- updateKeyboardLayouts();
881- updateKeyboardLayoutsModel();
882 updateSpellCheckingModel();
883 }
884
885@@ -137,15 +118,6 @@
886 g_signal_handlers_disconnect_by_data(m_manager, this);
887 g_object_unref(m_manager);
888 }
889-
890- if (m_maliitSettings != nullptr) {
891- g_signal_handlers_disconnect_by_data(m_maliitSettings, this);
892- g_object_unref(m_maliitSettings);
893- }
894-
895- for (QList<KeyboardLayout *>::const_iterator
896- i(m_keyboardLayouts.begin()); i != m_keyboardLayouts.end(); ++i)
897- delete *i;
898 }
899
900 const QStringList &
901@@ -160,6 +132,17 @@
902 return m_languageCodes;
903 }
904
905+QString
906+LanguagePlugin::languageToLayout(const QString &lang)
907+{
908+ QString language(lang.left(lang.indexOf('.')));
909+ act_user_set_language(m_user, qPrintable(language));
910+ act_user_set_formats_locale(m_user, qPrintable(lang));
911+
912+ icu::Locale locale(qPrintable(lang));
913+ return locale.getLanguage();
914+}
915+
916 int
917 LanguagePlugin::currentLanguage() const
918 {
919@@ -177,72 +160,6 @@
920 }
921
922 SubsetModel *
923-LanguagePlugin::keyboardLayoutsModel()
924-{
925- return &m_keyboardLayoutsModel;
926-}
927-
928-void
929-LanguagePlugin::keyboardLayoutsModelChanged()
930-{
931- GVariantBuilder builder;
932- gchar *current;
933- bool removed(true);
934-
935- g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
936- g_settings_get(m_maliitSettings, KEY_CURRENT_LAYOUT, "s", &current);
937-
938- for (QList<int>::const_iterator
939- i(m_keyboardLayoutsModel.subset().begin());
940- i != m_keyboardLayoutsModel.subset().end(); ++i) {
941- g_variant_builder_add(&builder, "s",
942- qPrintable(m_keyboardLayouts[*i]->name()));
943-
944- if (m_keyboardLayouts[*i]->name() == current)
945- removed = false;
946- }
947-
948- if (removed && !m_keyboardLayoutsModel.subset().isEmpty()) {
949- GVariantIter *iter;
950- const gchar *layout;
951- bool found(false);
952-
953- g_settings_get(m_maliitSettings, KEY_ENABLED_LAYOUTS, "as", &iter);
954-
955- for (int i(0); g_variant_iter_next(iter, "&s", &layout); i++) {
956- found = g_strcmp0(layout, current) == 0;
957-
958- if (found) {
959- if (i >= m_keyboardLayoutsModel.subset().size())
960- i = m_keyboardLayoutsModel.subset().size() - 1;
961-
962- int index(m_keyboardLayoutsModel.subset()[i]);
963- const QString &name(m_keyboardLayouts[index]->name());
964-
965- g_settings_set_string(m_maliitSettings,
966- KEY_CURRENT_LAYOUT, qPrintable(name));
967-
968- break;
969- }
970- }
971-
972- if (!found) {
973- int index(m_keyboardLayoutsModel.subset().front());
974- const QString &name(m_keyboardLayouts[index]->name());
975-
976- g_settings_set_string(m_maliitSettings,
977- KEY_CURRENT_LAYOUT, qPrintable(name));
978- }
979-
980- g_variant_iter_free(iter);
981- }
982-
983- g_free(current);
984- g_settings_set_value(m_maliitSettings,
985- KEY_ENABLED_LAYOUTS, g_variant_builder_end(&builder));
986-}
987-
988-SubsetModel *
989 LanguagePlugin::spellCheckingModel()
990 {
991 return &m_spellCheckingModel;
992@@ -254,26 +171,6 @@
993 // TODO: update spell checking model
994 }
995
996-static bool
997-compareLayouts(const KeyboardLayout *layout0,
998- const KeyboardLayout *layout1)
999-{
1000- QString name0(layout0->displayName());
1001- QString name1(layout1->displayName());
1002-
1003- if (name0 == name1) {
1004- name0 = layout0->language();
1005- name1 = layout1->language();
1006-
1007- if (name0 == name1) {
1008- name0 = layout0->name();
1009- name1 = layout1->name();
1010- }
1011- }
1012-
1013- return QString::localeAwareCompare(name0, name1) < 0;
1014-}
1015-
1016 void
1017 LanguagePlugin::updateLanguageNamesAndCodes()
1018 {
1019@@ -359,19 +256,6 @@
1020 QString language(formatsLocale.left(formatsLocale.indexOf('.')));
1021 act_user_set_language(m_user, qPrintable(language));
1022 act_user_set_formats_locale(m_user, qPrintable(formatsLocale));
1023-
1024- icu::Locale locale(qPrintable(formatsLocale));
1025- const char *code(locale.getLanguage());
1026- for (int i = 0; i < m_layoutPaths.count(); i++) {
1027- QFileInfo fileInfo(QDir(m_layoutPaths.at(i)), code);
1028-
1029- if (fileInfo.exists() && fileInfo.isDir()) {
1030- g_settings_set_string(m_maliitSettings,
1031- KEY_CURRENT_LAYOUT, code);
1032-
1033- updateEnabledLayouts();
1034- }
1035- }
1036 } else {
1037 QString formatsLocale(act_user_get_formats_locale(m_user));
1038 m_currentLanguage = indexForLocale(formatsLocale);
1039@@ -390,103 +274,6 @@
1040 Q_EMIT currentLanguageChanged();
1041 }
1042
1043-void
1044-LanguagePlugin::updateEnabledLayouts()
1045-{
1046- GVariantBuilder builder;
1047- GVariantIter *iter;
1048- gchar *current;
1049- const gchar *layout;
1050- QSet<QString> added;
1051-
1052- g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1053- g_settings_get(m_maliitSettings, KEY_ENABLED_LAYOUTS, "as", &iter);
1054- g_settings_get(m_maliitSettings, KEY_CURRENT_LAYOUT, "s", &current);
1055-
1056- while (g_variant_iter_next(iter, "&s", &layout)) {
1057- if (!added.contains(layout)) {
1058- g_variant_builder_add(&builder, "s", layout);
1059- added.insert(layout);
1060- }
1061- }
1062-
1063- if (!added.contains(current)) {
1064- g_variant_builder_add(&builder, "s", current);
1065- added.insert(current);
1066- }
1067-
1068- g_free(current);
1069- g_variant_iter_free(iter);
1070- g_settings_set_value(m_maliitSettings,
1071- KEY_ENABLED_LAYOUTS, g_variant_builder_end(&builder));
1072-}
1073-
1074-void
1075-LanguagePlugin::updateKeyboardLayouts()
1076-{
1077- m_keyboardLayouts.clear();
1078-
1079- for (int i = 0; i < m_layoutPaths.count(); i++) {
1080- QDir layoutsDir(m_layoutPaths.at(i));
1081- layoutsDir.setFilter(QDir::Dirs);
1082- layoutsDir.setSorting(QDir::Name);
1083-
1084- QFileInfoList fileInfoList(layoutsDir.entryInfoList());
1085-
1086- for (QFileInfoList::const_iterator
1087- i(fileInfoList.begin()); i != fileInfoList.end(); ++i) {
1088- KeyboardLayout *layout(new KeyboardLayout(*i));
1089-
1090- if (!layout->language().isEmpty())
1091- m_keyboardLayouts += layout;
1092- else
1093- delete layout;
1094- }
1095- }
1096-
1097- qSort(m_keyboardLayouts.begin(), m_keyboardLayouts.end(), compareLayouts);
1098-}
1099-
1100-void enabledLayoutsChanged(GSettings *settings,
1101- gchar *key,
1102- gpointer user_data);
1103-
1104-void
1105-LanguagePlugin::updateKeyboardLayoutsModel()
1106-{
1107- QStringList customRoles;
1108- customRoles += "language";
1109- customRoles += "icon";
1110-
1111- m_keyboardLayoutsModel.setCustomRoles(customRoles);
1112-
1113- QVariantList superset;
1114-
1115- for (QList<KeyboardLayout *>::const_iterator
1116- i(m_keyboardLayouts.begin()); i != m_keyboardLayouts.end(); ++i) {
1117- QVariantList element;
1118-
1119- if (!(*i)->displayName().isEmpty())
1120- element += (*i)->displayName();
1121- else
1122- element += (*i)->name();
1123-
1124- element += (*i)->shortName();
1125- superset += QVariant(element);
1126- }
1127-
1128- m_keyboardLayoutsModel.setSuperset(superset);
1129-
1130- enabledLayoutsChanged();
1131-
1132- m_keyboardLayoutsModel.setAllowEmpty(false);
1133-
1134- connect(&m_keyboardLayoutsModel,
1135- SIGNAL(subsetChanged()), SLOT(keyboardLayoutsModelChanged()));
1136-
1137- g_signal_connect(m_maliitSettings, "changed::" KEY_ENABLED_LAYOUTS,
1138- G_CALLBACK(::enabledLayoutsChanged), this);
1139-}
1140
1141 void
1142 LanguagePlugin::updateSpellCheckingModel()
1143@@ -573,41 +360,6 @@
1144 plugin->managerLoaded();
1145 }
1146
1147-void
1148-LanguagePlugin::enabledLayoutsChanged()
1149-{
1150- GVariantIter *iter;
1151- const gchar *layout;
1152- QList<int> subset;
1153-
1154- g_settings_get(m_maliitSettings, KEY_ENABLED_LAYOUTS, "as", &iter);
1155-
1156- while (g_variant_iter_next(iter, "&s", &layout)) {
1157- for (int i(0); i < m_keyboardLayouts.length(); i++) {
1158- if (m_keyboardLayouts[i]->name() == layout) {
1159- subset += i;
1160- break;
1161- }
1162- }
1163- }
1164-
1165- g_variant_iter_free(iter);
1166-
1167- m_keyboardLayoutsModel.setSubset(subset);
1168-}
1169-
1170-void
1171-enabledLayoutsChanged(GSettings *settings,
1172- gchar *key,
1173- gpointer user_data)
1174-{
1175- Q_UNUSED(settings);
1176- Q_UNUSED(key);
1177-
1178- LanguagePlugin *plugin(static_cast<LanguagePlugin *>(user_data));
1179- plugin->enabledLayoutsChanged();
1180-}
1181-
1182 void LanguagePlugin::reboot()
1183 {
1184 m_sessionService.reboot();
1185
1186=== modified file 'plugins/language/language-plugin.h'
1187--- plugins/language/language-plugin.h 2015-04-10 11:22:10 +0000
1188+++ plugins/language/language-plugin.h 2016-03-14 14:37:14 +0000
1189@@ -56,10 +56,6 @@
1190 WRITE setCurrentLanguage
1191 NOTIFY currentLanguageChanged)
1192
1193- Q_PROPERTY(SubsetModel *keyboardLayoutsModel
1194- READ keyboardLayoutsModel
1195- CONSTANT)
1196-
1197 Q_PROPERTY(SubsetModel *spellCheckingModel
1198 READ spellCheckingModel
1199 CONSTANT)
1200@@ -77,19 +73,15 @@
1201 void setCurrentLanguage(int index);
1202 Q_SIGNAL void currentLanguageChanged() const;
1203
1204- SubsetModel *keyboardLayoutsModel();
1205- Q_SLOT void keyboardLayoutsModelChanged();
1206-
1207 SubsetModel *spellCheckingModel();
1208 Q_SLOT void spellCheckingModelChanged();
1209
1210+ Q_INVOKABLE QString languageToLayout(const QString &lang);
1211+
1212 private:
1213
1214 void updateLanguageNamesAndCodes();
1215 void updateCurrentLanguage();
1216- void updateEnabledLayouts();
1217- void updateKeyboardLayouts();
1218- void updateKeyboardLayoutsModel();
1219 void updateSpellCheckingModel();
1220
1221 int indexForLocale(const QString &name);
1222@@ -106,12 +98,6 @@
1223 GParamSpec *pspec,
1224 gpointer user_data);
1225
1226- void enabledLayoutsChanged();
1227-
1228- friend void enabledLayoutsChanged(GSettings *settings,
1229- gchar *key,
1230- gpointer user_data);
1231-
1232 QStringList m_languageNames;
1233 QStringList m_languageCodes;
1234 QHash<QString, unsigned int> m_indicesByLocale;
1235@@ -121,12 +107,8 @@
1236 ActUserManager *m_manager;
1237 ActUser *m_user;
1238
1239- GSettings *m_maliitSettings;
1240- QList<KeyboardLayout *> m_keyboardLayouts;
1241- SubsetModel m_keyboardLayoutsModel;
1242 SubsetModel m_spellCheckingModel;
1243 SessionService m_sessionService;
1244- QStringList m_layoutPaths;
1245 };
1246
1247 #endif // LANGUAGE_PLUGIN_H
1248
1249=== added file 'plugins/language/onscreenkeyboard-plugin.cpp'
1250--- plugins/language/onscreenkeyboard-plugin.cpp 1970-01-01 00:00:00 +0000
1251+++ plugins/language/onscreenkeyboard-plugin.cpp 2016-03-14 14:37:14 +0000
1252@@ -0,0 +1,294 @@
1253+/*
1254+ * This file is part of system-settings
1255+ *
1256+ * Copyright (C) 2015 Canonical Ltd.
1257+ *
1258+ * Contact: William Hua <william.hua@canonical.com>
1259+ * Jonas G. Drange <jonas.drange@canonical.com>
1260+ *
1261+ * This program is free software: you can redistribute it and/or modify it
1262+ * under the terms of the GNU General Public License version 3, as published
1263+ * by the Free Software Foundation.
1264+ *
1265+ * This program is distributed in the hope that it will be useful, but
1266+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1267+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1268+ * PURPOSE. See the GNU General Public License for more details.
1269+ *
1270+ * You should have received a copy of the GNU General Public License along
1271+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1272+ */
1273+
1274+#include <QtDebug>
1275+#include "onscreenkeyboard-plugin.h"
1276+
1277+#define UBUNTU_KEYBOARD_SCHEMA_ID "com.canonical.keyboard.maliit"
1278+
1279+#define KEY_ENABLED_LAYOUTS "enabled-languages"
1280+#define KEY_CURRENT_LAYOUT "active-language"
1281+#define KEY_PLUGIN_PATHS "plugin-paths"
1282+
1283+#define LAYOUTS_DIR "/usr/share/maliit/plugins/com/ubuntu/lib"
1284+
1285+OnScreenKeyboardPlugin::OnScreenKeyboardPlugin(QObject *parent) :
1286+ QObject(parent),
1287+ m_maliitSettings(g_settings_new(UBUNTU_KEYBOARD_SCHEMA_ID))
1288+{
1289+ GVariantIter *iter;
1290+ const gchar *path;
1291+
1292+ m_layoutPaths.append(LAYOUTS_DIR);
1293+ g_settings_get(m_maliitSettings, KEY_PLUGIN_PATHS, "as", &iter);
1294+ for (int i(0); g_variant_iter_next(iter, "&s", &path); i++) {
1295+ m_layoutPaths.append(path);
1296+ }
1297+ updateEnabledLayouts();
1298+ updateKeyboardLayouts();
1299+ updateKeyboardLayoutsModel();
1300+}
1301+
1302+
1303+OnScreenKeyboardPlugin::~OnScreenKeyboardPlugin()
1304+{
1305+ if (m_maliitSettings != nullptr) {
1306+ g_signal_handlers_disconnect_by_data(m_maliitSettings, this);
1307+ g_object_unref(m_maliitSettings);
1308+ }
1309+
1310+ for (QList<KeyboardLayout *>::const_iterator
1311+ i(m_keyboardLayouts.begin()); i != m_keyboardLayouts.end(); ++i)
1312+ delete *i;
1313+}
1314+
1315+SubsetModel *
1316+OnScreenKeyboardPlugin::keyboardLayoutsModel()
1317+{
1318+ return &m_keyboardLayoutsModel;
1319+}
1320+
1321+void
1322+OnScreenKeyboardPlugin::keyboardLayoutsModelChanged()
1323+{
1324+ GVariantBuilder builder;
1325+ gchar *current;
1326+ bool removed(true);
1327+
1328+ g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1329+ g_settings_get(m_maliitSettings, KEY_CURRENT_LAYOUT, "s", &current);
1330+
1331+ for (QList<int>::const_iterator
1332+ i(m_keyboardLayoutsModel.subset().begin());
1333+ i != m_keyboardLayoutsModel.subset().end(); ++i) {
1334+ g_variant_builder_add(&builder, "s",
1335+ qPrintable(m_keyboardLayouts[*i]->name()));
1336+
1337+ if (m_keyboardLayouts[*i]->name() == current)
1338+ removed = false;
1339+ }
1340+
1341+ if (removed && !m_keyboardLayoutsModel.subset().isEmpty()) {
1342+ GVariantIter *iter;
1343+ const gchar *layout;
1344+ bool found(false);
1345+
1346+ g_settings_get(m_maliitSettings, KEY_ENABLED_LAYOUTS, "as", &iter);
1347+
1348+ for (int i(0); g_variant_iter_next(iter, "&s", &layout); i++) {
1349+ found = g_strcmp0(layout, current) == 0;
1350+
1351+ if (found) {
1352+ if (i >= m_keyboardLayoutsModel.subset().size())
1353+ i = m_keyboardLayoutsModel.subset().size() - 1;
1354+
1355+ int index(m_keyboardLayoutsModel.subset()[i]);
1356+ const QString &name(m_keyboardLayouts[index]->name());
1357+
1358+ g_settings_set_string(m_maliitSettings,
1359+ KEY_CURRENT_LAYOUT, qPrintable(name));
1360+
1361+ break;
1362+ }
1363+ }
1364+
1365+ if (!found) {
1366+ int index(m_keyboardLayoutsModel.subset().front());
1367+ const QString &name(m_keyboardLayouts[index]->name());
1368+
1369+ g_settings_set_string(m_maliitSettings,
1370+ KEY_CURRENT_LAYOUT, qPrintable(name));
1371+ }
1372+
1373+ g_variant_iter_free(iter);
1374+ }
1375+
1376+ g_free(current);
1377+ g_settings_set_value(m_maliitSettings,
1378+ KEY_ENABLED_LAYOUTS, g_variant_builder_end(&builder));
1379+}
1380+
1381+static bool
1382+compareLayouts(const KeyboardLayout *layout0,
1383+ const KeyboardLayout *layout1)
1384+{
1385+ QString name0(layout0->displayName());
1386+ QString name1(layout1->displayName());
1387+
1388+ if (name0 == name1) {
1389+ name0 = layout0->language();
1390+ name1 = layout1->language();
1391+
1392+ if (name0 == name1) {
1393+ name0 = layout0->name();
1394+ name1 = layout1->name();
1395+ }
1396+ }
1397+
1398+ return QString::localeAwareCompare(name0, name1) < 0;
1399+}
1400+
1401+void
1402+OnScreenKeyboardPlugin::updateEnabledLayouts()
1403+{
1404+ GVariantBuilder builder;
1405+ GVariantIter *iter;
1406+ gchar *current;
1407+ const gchar *layout;
1408+ QSet<QString> added;
1409+
1410+ g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
1411+ g_settings_get(m_maliitSettings, KEY_ENABLED_LAYOUTS, "as", &iter);
1412+ g_settings_get(m_maliitSettings, KEY_CURRENT_LAYOUT, "s", &current);
1413+
1414+ while (g_variant_iter_next(iter, "&s", &layout)) {
1415+ if (!added.contains(layout)) {
1416+ g_variant_builder_add(&builder, "s", layout);
1417+ added.insert(layout);
1418+ }
1419+ }
1420+
1421+ if (!added.contains(current)) {
1422+ g_variant_builder_add(&builder, "s", current);
1423+ added.insert(current);
1424+ }
1425+
1426+ g_free(current);
1427+ g_variant_iter_free(iter);
1428+ g_settings_set_value(m_maliitSettings,
1429+ KEY_ENABLED_LAYOUTS, g_variant_builder_end(&builder));
1430+}
1431+
1432+void
1433+OnScreenKeyboardPlugin::updateKeyboardLayouts()
1434+{
1435+ m_keyboardLayouts.clear();
1436+
1437+ for (int i = 0; i < m_layoutPaths.count(); i++) {
1438+ QDir layoutsDir(m_layoutPaths.at(i));
1439+ layoutsDir.setFilter(QDir::Dirs);
1440+ layoutsDir.setSorting(QDir::Name);
1441+
1442+ QFileInfoList fileInfoList(layoutsDir.entryInfoList());
1443+
1444+ for (QFileInfoList::const_iterator
1445+ i(fileInfoList.begin()); i != fileInfoList.end(); ++i) {
1446+ KeyboardLayout *layout(new KeyboardLayout(*i));
1447+
1448+ if (!layout->language().isEmpty())
1449+ m_keyboardLayouts += layout;
1450+ else
1451+ delete layout;
1452+ }
1453+ }
1454+
1455+ qSort(m_keyboardLayouts.begin(), m_keyboardLayouts.end(), compareLayouts);
1456+}
1457+
1458+void enabledLayoutsChanged(GSettings *settings,
1459+ gchar *key,
1460+ gpointer user_data);
1461+
1462+void
1463+OnScreenKeyboardPlugin::updateKeyboardLayoutsModel()
1464+{
1465+ QStringList customRoles;
1466+ customRoles += "language";
1467+ customRoles += "icon";
1468+
1469+ m_keyboardLayoutsModel.setCustomRoles(customRoles);
1470+
1471+ QVariantList superset;
1472+
1473+ for (QList<KeyboardLayout *>::const_iterator
1474+ i(m_keyboardLayouts.begin()); i != m_keyboardLayouts.end(); ++i) {
1475+ QVariantList element;
1476+
1477+ if (!(*i)->displayName().isEmpty())
1478+ element += (*i)->displayName();
1479+ else
1480+ element += (*i)->name();
1481+
1482+ element += (*i)->shortName();
1483+ superset += QVariant(element);
1484+ }
1485+
1486+ m_keyboardLayoutsModel.setSuperset(superset);
1487+
1488+ enabledLayoutsChanged();
1489+
1490+ m_keyboardLayoutsModel.setAllowEmpty(false);
1491+
1492+ connect(&m_keyboardLayoutsModel,
1493+ SIGNAL(subsetChanged()), SLOT(keyboardLayoutsModelChanged()));
1494+
1495+ g_signal_connect(m_maliitSettings, "changed::" KEY_ENABLED_LAYOUTS,
1496+ G_CALLBACK(::enabledLayoutsChanged), this);
1497+}
1498+
1499+void
1500+OnScreenKeyboardPlugin::enabledLayoutsChanged()
1501+{
1502+ GVariantIter *iter;
1503+ const gchar *layout;
1504+ QList<int> subset;
1505+
1506+ g_settings_get(m_maliitSettings, KEY_ENABLED_LAYOUTS, "as", &iter);
1507+
1508+ while (g_variant_iter_next(iter, "&s", &layout)) {
1509+ for (int i(0); i < m_keyboardLayouts.length(); i++) {
1510+ if (m_keyboardLayouts[i]->name() == layout) {
1511+ subset += i;
1512+ break;
1513+ }
1514+ }
1515+ }
1516+
1517+ g_variant_iter_free(iter);
1518+
1519+ m_keyboardLayoutsModel.setSubset(subset);
1520+}
1521+
1522+void
1523+enabledLayoutsChanged(GSettings *settings,
1524+ gchar *key,
1525+ gpointer user_data)
1526+{
1527+ Q_UNUSED(settings);
1528+ Q_UNUSED(key);
1529+
1530+ OnScreenKeyboardPlugin *plugin(static_cast<OnScreenKeyboardPlugin *>(user_data));
1531+ plugin->enabledLayoutsChanged();
1532+}
1533+
1534+void OnScreenKeyboardPlugin::setCurrentLayout(const QString &code)
1535+{
1536+ for (int i = 0; i < m_layoutPaths.count(); i++) {
1537+ QFileInfo fileInfo(QDir(m_layoutPaths.at(i)), code);
1538+
1539+ if (fileInfo.exists() && fileInfo.isDir()) {
1540+ g_settings_set_string(m_maliitSettings,
1541+ KEY_CURRENT_LAYOUT, code.toStdString().c_str());
1542+
1543+ updateEnabledLayouts();
1544+ }
1545+ }
1546+}
1547
1548=== added file 'plugins/language/onscreenkeyboard-plugin.h'
1549--- plugins/language/onscreenkeyboard-plugin.h 1970-01-01 00:00:00 +0000
1550+++ plugins/language/onscreenkeyboard-plugin.h 2016-03-14 14:37:14 +0000
1551@@ -0,0 +1,75 @@
1552+/*
1553+ * This file is part of system-settings
1554+ *
1555+ * Copyright (C) 2015 Canonical Ltd.
1556+ *
1557+ * Contact: William Hua <william.hua@canonical.com>
1558+ * Jonas G. Drange <jonas.drange@canonical.com>
1559+ *
1560+ * This program is free software: you can redistribute it and/or modify it
1561+ * under the terms of the GNU General Public License version 3, as published
1562+ * by the Free Software Foundation.
1563+ *
1564+ * This program is distributed in the hope that it will be useful, but
1565+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1566+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1567+ * PURPOSE. See the GNU General Public License for more details.
1568+ *
1569+ * You should have received a copy of the GNU General Public License along
1570+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1571+ */
1572+
1573+#ifndef OSK_PLUGIN_H
1574+#define OSK_PLUGIN_H
1575+
1576+#include <QtCore>
1577+#include <gio/gio.h>
1578+#include "subset-model.h"
1579+#include "keyboard-layout.h"
1580+
1581+typedef struct _GSettings GSettings;
1582+typedef void *gpointer;
1583+typedef char gchar;
1584+
1585+class KeyboardLayout;
1586+
1587+class OnScreenKeyboardPlugin : public QObject
1588+{
1589+private:
1590+
1591+ Q_OBJECT
1592+
1593+public:
1594+
1595+ Q_PROPERTY(SubsetModel *keyboardLayoutsModel
1596+ READ keyboardLayoutsModel
1597+ CONSTANT)
1598+
1599+ explicit OnScreenKeyboardPlugin(QObject *parent = nullptr);
1600+
1601+ virtual ~OnScreenKeyboardPlugin();
1602+
1603+ Q_INVOKABLE void setCurrentLayout(const QString &code);
1604+
1605+ SubsetModel *keyboardLayoutsModel();
1606+ Q_SLOT void keyboardLayoutsModelChanged();
1607+
1608+private:
1609+
1610+ void updateEnabledLayouts();
1611+ void updateKeyboardLayouts();
1612+ void updateKeyboardLayoutsModel();
1613+
1614+ void enabledLayoutsChanged();
1615+
1616+ friend void enabledLayoutsChanged(GSettings *settings,
1617+ gchar *key,
1618+ gpointer user_data);
1619+
1620+ GSettings *m_maliitSettings;
1621+ QList<KeyboardLayout *> m_keyboardLayouts;
1622+ SubsetModel m_keyboardLayoutsModel;
1623+ QStringList m_layoutPaths;
1624+};
1625+
1626+#endif // OSK_PLUGIN_H
1627
1628=== modified file 'plugins/language/plugin.cpp'
1629--- plugins/language/plugin.cpp 2014-07-23 13:44:31 +0000
1630+++ plugins/language/plugin.cpp 2016-03-14 14:37:14 +0000
1631@@ -24,6 +24,8 @@
1632 #include <QtQml>
1633 #include "subset-model.h"
1634 #include "language-plugin.h"
1635+#include "onscreenkeyboard-plugin.h"
1636+#include "hardwarekeyboard-plugin.h"
1637
1638 void BackendPlugin::registerTypes(const char *uri)
1639 {
1640@@ -31,6 +33,8 @@
1641
1642 qmlRegisterType<SubsetModel>(uri, 1, 0, "SubsetModel");
1643 qmlRegisterType<LanguagePlugin>(uri, 1, 0, "UbuntuLanguagePlugin");
1644+ qmlRegisterType<OnScreenKeyboardPlugin>(uri, 1, 0, "OnScreenKeyboardPlugin");
1645+ qmlRegisterType<HardwareKeyboardPlugin>(uri, 1, 0, "HardwareKeyboardPlugin");
1646 }
1647
1648 void BackendPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
1649
1650=== modified file 'plugins/language/subset-model.cpp'
1651--- plugins/language/subset-model.cpp 2014-01-27 17:05:24 +0000
1652+++ plugins/language/subset-model.cpp 2016-03-14 14:37:14 +0000
1653@@ -401,3 +401,27 @@
1654 {
1655 return elementAtRow(index.row());
1656 }
1657+
1658+void
1659+SubsetModel::moveSubsetRow(int from, int to) {
1660+ // Make sure its not moved outside the lists
1661+ if (to < 0) {
1662+ to = 0;
1663+ }
1664+ if (to >= m_subset.count()) {
1665+ to = m_subset.count()-1;
1666+ }
1667+
1668+ // Nothing to do?
1669+ if (from == to) {
1670+ return;
1671+ }
1672+
1673+ // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
1674+ // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
1675+ // adjust the model's index by +1 in case we're moving upwards
1676+ int newModelIndex = to > from ? to+1 : to;
1677+ beginMoveRows(QModelIndex(), from, from, QModelIndex(), newModelIndex);
1678+ m_subset.move(from, to);
1679+ endMoveRows();
1680+}
1681
1682=== modified file 'plugins/language/subset-model.h'
1683--- plugins/language/subset-model.h 2014-07-23 13:44:31 +0000
1684+++ plugins/language/subset-model.h 2016-03-14 14:37:14 +0000
1685@@ -65,6 +65,9 @@
1686 virtual void setSubset(const QList<int> &subset);
1687 Q_SIGNAL virtual void subsetChanged() const;
1688
1689+ virtual void moveSubsetRow(int from, int to);
1690+
1691+
1692 virtual bool allowEmpty() const;
1693 virtual void setAllowEmpty(bool allowEmpty);
1694 Q_SIGNAL virtual void allowEmptyChanged() const;
1695
1696=== modified file 'tests/autopilot/ubuntu_system_settings/__init__.py'
1697--- tests/autopilot/ubuntu_system_settings/__init__.py 2015-10-20 14:46:24 +0000
1698+++ tests/autopilot/ubuntu_system_settings/__init__.py 2016-03-14 14:37:14 +0000
1699@@ -1394,7 +1394,7 @@
1700 @classmethod
1701 def validate_dbus_object(cls, path, state):
1702 name = introspection.get_classname_from_path(path)
1703- if name == b'ItemPage':
1704+ if name == b'PageComponent':
1705 if state['objectName'][1] == 'languagePage':
1706 return True
1707 return False

Subscribers

People subscribed via source and target branches