Merge lp:~flscogna/ubuntu-terminal-app/json-keyboard-profiles into lp:~ubuntu-terminal-dev/ubuntu-terminal-app/reboot

Proposed by Filippo Scognamiglio
Status: Merged
Approved by: Alan Pope 🍺🐧🐱 πŸ¦„
Approved revision: 50
Merged at revision: 55
Proposed branch: lp:~flscogna/ubuntu-terminal-app/json-keyboard-profiles
Merge into: lp:~ubuntu-terminal-dev/ubuntu-terminal-app/reboot
Diff against target: 1141 lines (+702/-244)
19 files modified
po/com.ubuntu.terminal.pot (+2/-18)
src/app/CMakeLists.txt (+2/-1)
src/app/fileio.cpp (+37/-0)
src/app/fileio.h (+21/-0)
src/app/main.cpp (+30/-0)
src/app/qml/KeyboardBar.qml (+113/-36)
src/app/qml/KeyboardRows/KeyboardLayout.qml (+103/-0)
src/app/qml/KeyboardRows/KeyboardRow.qml (+5/-1)
src/app/qml/KeyboardRows/Layouts/ControlKeys.json (+39/-0)
src/app/qml/KeyboardRows/Layouts/ControlKeysLayout.qml (+0/-26)
src/app/qml/KeyboardRows/Layouts/FunctionKeys.json (+98/-0)
src/app/qml/KeyboardRows/Layouts/FunctionKeysLayout.qml (+0/-62)
src/app/qml/KeyboardRows/Layouts/ScrollKeys.json (+77/-0)
src/app/qml/KeyboardRows/Layouts/ScrollKeysLayout.qml (+0/-50)
src/app/qml/KeyboardRows/Layouts/SimpleCommands.json (+87/-0)
src/app/qml/KeyboardRows/Layouts/SimpleCommandsLayout.qml (+0/-48)
src/app/qml/KeyboardRows/jsonParser.js (+78/-0)
src/plugin/qmltermwidget/lib/TerminalDisplay.cpp (+9/-1)
src/plugin/qmltermwidget/lib/TerminalDisplay.h (+1/-1)
To merge this branch: bzr merge lp:~flscogna/ubuntu-terminal-app/json-keyboard-profiles
Reviewer Review Type Date Requested Status
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Nicholas Skaggs (community) Needs Fixing
Alan Pope 🍺🐧🐱 πŸ¦„ (community) Approve
Stefano Verzegnassi Approve
Review via email: mp+249754@code.launchpad.net

Commit message

Enable layout customization through json profiles.

Description of the change

Enable layout customization through json profiles.

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Stefano Verzegnassi (verzegnassi-stefano) wrote :

LGTM, good work Filippo!

However, as discussed on Hangouts, the only thing that could be improved is the way you get the ConfigLocation from QStandardPaths.

Some of the returning paths are confined by the AppArmor policies, so it may be worth to use QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) which, according to Qt docs[1], "returns the directory where files of type should be written to".

Tested on utopic desktop.

[1]: http://doc.qt.io/qt-5/qstandardpaths.html#standardLocations

review: Approve
Revision history for this message
Filippo Scognamiglio (flscogna) wrote :

Thank you for the review Stefano. Your solution is good, and would save the need to look in directories where we are sure there won't be anything. I will add that in the next update, thank you.

Revision history for this message
Alan Pope 🍺🐧🐱 πŸ¦„ (popey) wrote :

Tested and works well on device! Thanks!

review: Approve
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Filippo, your target for this needs to be the reboot series :-)

Merge into: lp:ubuntu-terminal-app

review: Needs Fixing
Revision history for this message
Filippo Scognamiglio (flscogna) wrote :

Nicholas lp:ubuntu-terminal-app is now alias for reboot, so I thought it was the same. Should I explicitly change it to /reboot?

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Filippo, so you are right. I swore this was different before.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) :
review: Approve (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :

FAILED: Autolanding.
Approved revid is not set in launchpad. This is most likely a launchpad issue and re-approve should fix it. There is also a chance (although a very small one) this is a permission problem of the ps-jenkins bot.
http://91.189.93.70:8080/job/ubuntu-terminal-app-autolanding/76/
Executed test runs:
    SUCCESS: http://91.189.93.70:8080/job/generic-mediumtests-utopic/2117
        deb: http://91.189.93.70:8080/job/generic-mediumtests-utopic/2117/artifact/work/output/*zip*/output.zip
    SUCCESS: http://91.189.93.70:8080/job/ubuntu-terminal-app-vivid-amd64-autolanding/8

review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'po/com.ubuntu.terminal.pot'
2--- po/com.ubuntu.terminal.pot 2015-01-17 02:02:04 +0000
3+++ po/com.ubuntu.terminal.pot 2015-02-14 14:24:37 +0000
4@@ -8,7 +8,7 @@
5 msgstr ""
6 "Project-Id-Version: \n"
7 "Report-Msgid-Bugs-To: \n"
8-"POT-Creation-Date: 2015-01-17 02:58+0100\n"
9+"POT-Creation-Date: 2015-02-14 13:41+0100\n"
10 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Language-Team: LANGUAGE <LL@li.org>\n"
13@@ -53,23 +53,7 @@
14 msgid "Authentication failed"
15 msgstr ""
16
17-#: ../src/app/qml/KeyboardBar.qml:41
18-msgid "Scroll Keys"
19-msgstr ""
20-
21-#: ../src/app/qml/KeyboardBar.qml:46
22-msgid "Functions Keys"
23-msgstr ""
24-
25-#: ../src/app/qml/KeyboardBar.qml:51
26-msgid "Command Keys"
27-msgstr ""
28-
29-#: ../src/app/qml/KeyboardBar.qml:56
30-msgid "Control Keys"
31-msgstr ""
32-
33-#: ../src/app/qml/KeyboardBar.qml:83
34+#: ../src/app/qml/KeyboardBar.qml:157
35 msgid "Change Keyboard"
36 msgstr ""
37
38
39=== modified file 'src/app/CMakeLists.txt'
40--- src/app/CMakeLists.txt 2014-10-25 04:42:31 +0000
41+++ src/app/CMakeLists.txt 2015-02-14 14:24:37 +0000
42@@ -1,7 +1,8 @@
43-file(GLOB_RECURSE QML_SRCS *.qml *.js)
44+file(GLOB_RECURSE QML_SRCS *.qml *.js *.json)
45
46 set(terminal_SRCS
47 main.cpp
48+ fileio.cpp
49 ${QML_SRCS}
50 )
51
52
53=== added file 'src/app/fileio.cpp'
54--- src/app/fileio.cpp 1970-01-01 00:00:00 +0000
55+++ src/app/fileio.cpp 2015-02-14 14:24:37 +0000
56@@ -0,0 +1,37 @@
57+#include "fileio.h"
58+
59+FileIO::FileIO()
60+{
61+}
62+
63+bool FileIO::write(const QString& sourceUrl, const QString& data) {
64+ if (sourceUrl.isEmpty())
65+ return false;
66+
67+ QUrl url(sourceUrl);
68+ QFile file(url.toLocalFile());
69+ if (!file.open(QFile::WriteOnly | QFile::Truncate))
70+ return false;
71+
72+ QTextStream out(&file);
73+ out << data;
74+ file.close();
75+ return true;
76+}
77+
78+QString FileIO::read(const QString& sourceUrl) {
79+ if (sourceUrl.isEmpty())
80+ return "";
81+
82+ QUrl url(sourceUrl);
83+ QFile file(url.toLocalFile());
84+ if (!file.open(QFile::ReadOnly))
85+ return "";
86+
87+ QTextStream in(&file);
88+ QString result = in.readAll();
89+
90+ file.close();
91+
92+ return result;
93+}
94
95=== added file 'src/app/fileio.h'
96--- src/app/fileio.h 1970-01-01 00:00:00 +0000
97+++ src/app/fileio.h 2015-02-14 14:24:37 +0000
98@@ -0,0 +1,21 @@
99+#ifndef FILEIO_H
100+#define FILEIO_H
101+
102+#include <QObject>
103+#include <QFile>
104+#include <QTextStream>
105+#include <QUrl>
106+
107+class FileIO : public QObject
108+{
109+ Q_OBJECT
110+
111+public:
112+ FileIO();
113+
114+public slots:
115+ bool write(const QString& sourceUrl, const QString& data);
116+ QString read(const QString& sourceUrl);
117+};
118+
119+#endif // FILEIO_H
120
121=== modified file 'src/app/main.cpp'
122--- src/app/main.cpp 2014-12-05 22:52:26 +0000
123+++ src/app/main.cpp 2015-02-14 14:24:37 +0000
124@@ -27,14 +27,32 @@
125 #include <QLibrary>
126 #include <QDir>
127
128+#include "fileio.h"
129+
130 #include <QDebug>
131
132+QStringList getProfileFromDir(const QString &path) {
133+ QDir layoutDir(path);
134+ layoutDir.setNameFilters(QStringList("*.json"));
135+
136+ QStringList jsonFiles = layoutDir.entryList();
137+
138+ QStringList result;
139+ foreach (QString s, jsonFiles) {
140+ result.append(s.prepend(path));
141+ }
142+ return result;
143+}
144+
145 int main(int argc, char *argv[])
146 {
147 QApplication a(argc, argv);
148 QQuickView view;
149 view.setResizeMode(QQuickView::SizeRootObjectToView);
150
151+ FileIO fileIO;
152+ view.engine()->rootContext()->setContextProperty("fileIO", &fileIO);
153+
154 // Set up import paths
155 QStringList importPathList = view.engine()->importPathList();
156 // Prepend the location of the plugin in the build dir,
157@@ -135,6 +153,7 @@
158
159 view.engine()->setImportPathList(importPathList);
160
161+ QStringList keyboardLayouts;
162 // load the qml file
163 if (qmlfile.isEmpty()) {
164 QStringList paths = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
165@@ -146,10 +165,21 @@
166 qDebug() << "Trying to load QML from:" << path + "/qml/ubuntu-terminal-app.qml";
167 if (fi.exists()) {
168 qmlfile = path + "/qml/ubuntu-terminal-app.qml";
169+ keyboardLayouts << getProfileFromDir(path + "/qml/KeyboardRows/Layouts/");
170 break;
171 }
172 }
173 }
174+
175+ QStringList configLocations = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation);
176+ foreach (const QString &path, configLocations) {
177+ QString fullPath = path + "/com.ubuntu.terminal/Layouts/";
178+ qDebug() << "Retrieving keyboard profiles from folder: " << fullPath;
179+ keyboardLayouts << getProfileFromDir(fullPath);
180+ }
181+
182+ view.engine()->rootContext()->setContextProperty("keyboardLayouts", keyboardLayouts);
183+
184 qDebug() << "using main qml file from:" << qmlfile;
185 view.setSource(QUrl::fromLocalFile(qmlfile));
186 view.show();
187
188=== modified file 'src/app/qml/KeyboardBar.qml'
189--- src/app/qml/KeyboardBar.qml 2015-01-16 23:04:14 +0000
190+++ src/app/qml/KeyboardBar.qml 2015-02-14 14:24:37 +0000
191@@ -1,10 +1,116 @@
192-import QtQuick 2.0
193+import QtQuick 2.2
194 import Ubuntu.Components 1.1
195 import "KeyboardRows"
196
197+import "KeyboardRows/jsonParser.js" as Parser
198+
199 Rectangle {
200+ id: rootItem
201 color: "black"
202
203+ property int selectedLayoutIndex: 0
204+
205+ signal simulateCommand(string command);
206+ signal simulateKey(int key, int mod);
207+
208+ ListModel {
209+ id: layoutsList
210+ }
211+
212+ Component {
213+ id: actionComponent
214+ Action {
215+ property int selectIndex
216+ onTriggered: rootItem.selectLayout(selectIndex);
217+ }
218+ }
219+
220+ Component {
221+ id: layoutComponent
222+ KeyboardLayout {
223+ anchors.fill: keyboardContainer
224+ enabled: false
225+ visible: false
226+ }
227+ }
228+
229+ function printLayouts() {
230+ for (var i = 0; i < layoutsList.count; i++) {
231+ console.log(layoutsList.get(i).layout.name);
232+ }
233+ }
234+
235+ function createLayoutObject(profileUrl) {
236+ var object = layoutComponent.createObject(keyboardContainer);
237+ object.loadProfile(fileIO.read(profileUrl));
238+ return object;
239+ }
240+
241+ function disableLayout(index) {
242+ var layoutObject = layoutsList.get(index).layout;
243+ layoutObject.visible = false;
244+ layoutObject.enabled = false;
245+ layoutObject.z = rootItem.z;
246+ layoutObject.simulateKey.disconnect(simulateKey);
247+ layoutObject.simulateCommand.disconnect(simulateCommand);
248+ }
249+
250+ function enableLayout(index) {
251+ var layoutObject = layoutsList.get(index).layout;
252+ layoutObject.visible = true;
253+ layoutObject.enabled = true;
254+ layoutObject.z = rootItem.z + 0.01;
255+ layoutObject.simulateKey.connect(simulateKey);
256+ layoutObject.simulateCommand.connect(simulateCommand);
257+ }
258+
259+ function selectLayout(index) {
260+ if (index < 0 || index >= layoutsList.count)
261+ return;
262+
263+ disableLayout(selectedLayoutIndex);
264+ enableLayout(index);
265+ selectedLayoutIndex = index;
266+ }
267+
268+ function dropProfiles() {
269+ for (var i = 0; i < layoutsList.count; i++) {
270+ layoutsList.get(i).layout.destroy();
271+ }
272+ layoutsList.clear();
273+ }
274+
275+ function updateSelector() {
276+ var result = [];
277+ for (var i = 0; i < layoutsList.count; i++) {
278+ var layoutObject = layoutsList.get(i).layout;
279+ var index = i;
280+ var actionObject = actionComponent.createObject(rootItem);
281+
282+ actionObject.text = layoutObject.short_name;
283+ actionObject.description = layoutObject.name;
284+ actionObject.selectIndex = i;
285+
286+ result.push(actionObject);
287+ }
288+ keyboardSelector.actions = result;
289+ }
290+
291+ function loadProfiles() {
292+ for (var i = 0; i < keyboardLayouts.length; i++) {
293+ try {
294+ console.log("Loading Layout:", Qt.resolvedUrl(keyboardLayouts[i]));
295+ var layoutObject = createLayoutObject(Qt.resolvedUrl(keyboardLayouts[i]));
296+ layoutsList.append({layout: layoutObject});
297+ } catch (e) {
298+ console.error("Error in profile " + keyboardLayouts[i]);
299+ console.error(e);
300+ }
301+ }
302+ updateSelector();
303+ selectLayout(0);
304+ }
305+
306 PressFeedback {
307 id: pressFeedbackEffect
308 }
309@@ -34,50 +140,18 @@
310 color: "black"
311 }
312 }
313-
314- actions: [
315- Action {
316- text: "SCR"
317- description: i18n.tr("Scroll Keys")
318- onTriggered: keyboardLoader.source = "KeyboardRows/Layouts/ScrollKeysLayout.qml"
319- },
320- Action {
321- text: "FN"
322- description: i18n.tr("Functions Keys")
323- onTriggered: keyboardLoader.source = "KeyboardRows/Layouts/FunctionKeysLayout.qml"
324- },
325- Action {
326- text: "CMD"
327- description: i18n.tr("Command Keys")
328- onTriggered: keyboardLoader.source = "KeyboardRows/Layouts/SimpleCommandsLayout.qml"
329- },
330- Action {
331- text: "CTRL"
332- description: i18n.tr("Control Keys")
333- onTriggered: keyboardLoader.source = "KeyboardRows/Layouts/ControlKeysLayout.qml"
334- }
335- ]
336 }
337
338- signal simulateCommand(string command);
339- signal simulateKey(int key, int mod);
340-
341 onSimulateKey: pressFeedbackEffect.start();
342 onSimulateCommand: pressFeedbackEffect.start();
343
344- Loader {
345- id: keyboardLoader
346+ Item {
347+ id: keyboardContainer
348 anchors.left: keyboardSelector.right
349 anchors.bottom: parent.bottom
350 anchors.right: parent.right
351 anchors.top: parent.top
352- source: "KeyboardRows/Layouts/ScrollKeysLayout.qml"
353-
354- onLoaded: {
355- item.keyHeight = parent.height
356- item.simulateKey.connect(simulateKey);
357- item.simulateCommand.connect(simulateCommand);
358- }
359+ height: units.gu(5)
360
361 Rectangle {
362 property string defaultString: i18n.tr("Change Keyboard");
363@@ -112,4 +186,7 @@
364 }
365 }
366 }
367+
368+ Component.onDestruction: dropProfiles();
369+ Component.onCompleted: loadProfiles();
370 }
371
372=== added file 'src/app/qml/KeyboardRows/KeyboardLayout.qml'
373--- src/app/qml/KeyboardRows/KeyboardLayout.qml 1970-01-01 00:00:00 +0000
374+++ src/app/qml/KeyboardRows/KeyboardLayout.qml 2015-02-14 14:24:37 +0000
375@@ -0,0 +1,103 @@
376+import QtQuick 2.0
377+import Ubuntu.Components 1.1
378+
379+import "jsonParser.js" as Parser
380+
381+KeyboardRow {
382+ id: keyboardRow
383+ keyWidth: units.gu(5)
384+
385+ // This label is used to compute the maximum width of all the controls.
386+ Label {
387+ id: hiddenLabel
388+ visible: false
389+ }
390+
391+ function dropProfile() {
392+ // TODO Check if this is enough and doesn't leak.
393+ for (var i = 0; i < model.length; i++)
394+ model[i].destroy();
395+ model = [];
396+ }
397+
398+ function createActionString(action) {
399+ switch(action.type){
400+ case "key":
401+ return createKeyActionString(action.key, action.mod, action.text);
402+ case "string":
403+ return createStringActionString(action.string, action.text);
404+ }
405+ }
406+
407+ function createOtherActionsString(actions) {
408+ var result = "[";
409+ for (var i = 0; i < actions.length; i++) {
410+ var action = actions[i];
411+ result += createActionString(action);
412+
413+ if (i < actions.length - 1)
414+ result += ",";
415+ }
416+
417+ return result + "]";
418+ }
419+
420+ function createKeyActionString(key, mod, text) {
421+ if (["Control", "Alt", "Shift"].indexOf(mod) === -1)
422+ mod = "No";
423+
424+ var textString = text ? "text: \"" + text + "\";" : "";
425+ return "Action { " + textString + " onTriggered: simulateKey(Qt.Key_"+ key + ", Qt." + mod + "Modifier); }";
426+ }
427+
428+ function createStringActionString(string, text) {
429+ var textString = text ? "text: \"" + text + "\";" : "";
430+ return "Action { " + textString + " onTriggered: simulateCommand(\"" + string + "\"); }";
431+ }
432+
433+ function createEntryString(text, actionString, otherActionsString) {
434+ var objectString = "
435+ import QtQuick 2.0
436+ import Ubuntu.Components 1.1
437+
438+ KeyModel {
439+ text: \"" + text + "\"
440+ mainAction: " + actionString + "\n
441+ actions:" + otherActionsString +
442+ "}"
443+ return objectString;
444+ }
445+
446+ function loadProfile(profileString) {
447+ dropProfile();
448+
449+ var maxWidth = 0;
450+
451+ // This function might raise exceptions which are handled in KeyboardBar.qml
452+ var profile = profile = Parser.parseJson(profileString);
453+
454+ name = profile.name;
455+ short_name = profile.short_name;
456+
457+ var layoutModel = []
458+ for (var i = 0; i < profile.buttons.length; i++) {
459+ var button = profile.buttons[i];
460+ var mainActionString = createActionString(button.main_action);
461+
462+ var otherActionsString = button.other_actions
463+ ? createOtherActionsString(button.other_actions)
464+ : "[]";
465+
466+ var entryString = createEntryString(button.main_action.text, mainActionString, otherActionsString);
467+
468+ layoutModel.push(Qt.createQmlObject(entryString, keyboardRow));
469+
470+ hiddenLabel.text = button.main_action.text;
471+ maxWidth = Math.max(hiddenLabel.width, maxWidth);
472+ }
473+
474+ keyWidth = maxWidth + units.gu(3);
475+ model = layoutModel;
476+ }
477+ Component.onDestruction: dropProfile();
478+}
479
480=== modified file 'src/app/qml/KeyboardRows/KeyboardRow.qml'
481--- src/app/qml/KeyboardRows/KeyboardRow.qml 2015-01-16 23:04:14 +0000
482+++ src/app/qml/KeyboardRows/KeyboardRow.qml 2015-02-14 14:24:37 +0000
483@@ -3,7 +3,11 @@
484
485 Rectangle {
486 id: container
487- property list<QtObject> model
488+
489+ property string name
490+ property string short_name
491+
492+ property var model: []
493 property real keyWidth: units.gu(5)
494 property real keyHeight: units.gu(5)
495
496
497=== added file 'src/app/qml/KeyboardRows/Layouts/ControlKeys.json'
498--- src/app/qml/KeyboardRows/Layouts/ControlKeys.json 1970-01-01 00:00:00 +0000
499+++ src/app/qml/KeyboardRows/Layouts/ControlKeys.json 2015-02-14 14:24:37 +0000
500@@ -0,0 +1,39 @@
501+{
502+ "name" : "Control Keys",
503+ "short_name" : "CTRL",
504+
505+ "buttons": [
506+ {
507+ "main_action" : {
508+ "type": "key",
509+ "text" : "CTRL+R",
510+ "key" : "R",
511+ "mod" : "Control"
512+ }
513+ },
514+ {
515+ "main_action" : {
516+ "type": "key",
517+ "text" : "CTRL+C",
518+ "key" : "C",
519+ "mod" : "Control"
520+ }
521+ },
522+ {
523+ "main_action" : {
524+ "type": "key",
525+ "text" : "CTRL+A",
526+ "key" : "A",
527+ "mod" : "Control"
528+ }
529+ },
530+ {
531+ "main_action" : {
532+ "type": "key",
533+ "text" : "CTRL+Z",
534+ "key" : "Z",
535+ "mod" : "Control"
536+ }
537+ }
538+ ]
539+}
540
541=== removed file 'src/app/qml/KeyboardRows/Layouts/ControlKeysLayout.qml'
542--- src/app/qml/KeyboardRows/Layouts/ControlKeysLayout.qml 2014-12-07 16:11:58 +0000
543+++ src/app/qml/KeyboardRows/Layouts/ControlKeysLayout.qml 1970-01-01 00:00:00 +0000
544@@ -1,26 +0,0 @@
545-import QtQuick 2.0
546-import Ubuntu.Components 1.1
547-
548-import ".."
549-
550-KeyboardRow {
551- keyWidth: units.gu(10)
552- model: [
553- KeyModel {
554- text: "CTRL+R"
555- mainAction: Action { onTriggered: simulateKey(Qt.Key_R, Qt.ControlModifier);}
556- },
557- KeyModel {
558- text: "CTRL+C"
559- mainAction: Action { onTriggered: simulateKey(Qt.Key_C, Qt.ControlModifier);}
560- },
561- KeyModel {
562- text: "CTRL+Z"
563- mainAction: Action { onTriggered: simulateKey(Qt.Key_Z, Qt.ControlModifier);}
564- },
565- KeyModel {
566- text: "CTRL+A"
567- mainAction: Action { onTriggered: simulateKey(Qt.Key_A, Qt.ControlModifier);}
568- }
569- ]
570-}
571
572=== added file 'src/app/qml/KeyboardRows/Layouts/FunctionKeys.json'
573--- src/app/qml/KeyboardRows/Layouts/FunctionKeys.json 1970-01-01 00:00:00 +0000
574+++ src/app/qml/KeyboardRows/Layouts/FunctionKeys.json 2015-02-14 14:24:37 +0000
575@@ -0,0 +1,98 @@
576+{
577+ "name" : "Function Keys",
578+ "short_name" : "FNS",
579+
580+ "buttons": [
581+ {
582+ "main_action" : {
583+ "type": "key",
584+ "text" : "ESC",
585+ "key" : "Escape"
586+ }
587+ },
588+ {
589+ "main_action" : {
590+ "type": "key",
591+ "text" : "F1",
592+ "key" : "F1"
593+ }
594+ },
595+ {
596+ "main_action" : {
597+ "type": "key",
598+ "text" : "F2",
599+ "key" : "F2"
600+ }
601+ },
602+ {
603+ "main_action" : {
604+ "type": "key",
605+ "text" : "F3",
606+ "key" : "F3"
607+ }
608+ },
609+ {
610+ "main_action" : {
611+ "type": "key",
612+ "text" : "F4",
613+ "key" : "F4"
614+ }
615+ },
616+ {
617+ "main_action" : {
618+ "type": "key",
619+ "text" : "F5",
620+ "key" : "F5"
621+ }
622+ },
623+ {
624+ "main_action" : {
625+ "type": "key",
626+ "text" : "F6",
627+ "key" : "F6"
628+ }
629+ },
630+ {
631+ "main_action" : {
632+ "type": "key",
633+ "text" : "F7",
634+ "key" : "F7"
635+ }
636+ },
637+ {
638+ "main_action" : {
639+ "type": "key",
640+ "text" : "F8",
641+ "key" : "F8"
642+ }
643+ },
644+ {
645+ "main_action" : {
646+ "type": "key",
647+ "text" : "F9",
648+ "key" : "F9"
649+ }
650+ },
651+ {
652+ "main_action" : {
653+ "type": "key",
654+ "text" : "F10",
655+ "key" : "F10"
656+ }
657+ },
658+ {
659+ "main_action" : {
660+ "type": "key",
661+ "text" : "F10",
662+ "key" : "F10"
663+ }
664+ },
665+ {
666+ "main_action" : {
667+ "type": "key",
668+ "text" : "F11",
669+ "key" : "F11"
670+ }
671+ }
672+ ]
673+}
674
675=== removed file 'src/app/qml/KeyboardRows/Layouts/FunctionKeysLayout.qml'
676--- src/app/qml/KeyboardRows/Layouts/FunctionKeysLayout.qml 2014-12-07 16:11:58 +0000
677+++ src/app/qml/KeyboardRows/Layouts/FunctionKeysLayout.qml 1970-01-01 00:00:00 +0000
678@@ -1,62 +0,0 @@
679-import QtQuick 2.0
680-import Ubuntu.Components 1.1
681-
682-import ".."
683-
684-KeyboardRow {
685- keyWidth: units.gu(6)
686- model: [
687- KeyModel {
688- text: "ESC"
689- mainAction: Action { onTriggered: simulateKey(Qt.Key_Escape, Qt.NoModifier); }
690- },
691- KeyModel {
692- text: "F1"
693- mainAction: Action { onTriggered: simulateKey(Qt.Key_F1, Qt.NoModifier);}
694- },
695- KeyModel {
696- text: "F2"
697- mainAction: Action { onTriggered: simulateKey(Qt.Key_F2, Qt.NoModifier);}
698- },
699- KeyModel {
700- text: "F3"
701- mainAction: Action { onTriggered: simulateKey(Qt.Key_F3, Qt.NoModifier);}
702- },
703- KeyModel {
704- text: "F4"
705- mainAction: Action { onTriggered: simulateKey(Qt.Key_F4, Qt.NoModifier);}
706- },
707- KeyModel {
708- text: "F5"
709- mainAction: Action { onTriggered: simulateKey(Qt.Key_F5, Qt.NoModifier);}
710- },
711- KeyModel {
712- text: "F6"
713- mainAction: Action { onTriggered: simulateKey(Qt.Key_F6, Qt.NoModifier);}
714- },
715- KeyModel {
716- text: "F7"
717- mainAction: Action { onTriggered: simulateKey(Qt.Key_F7, Qt.NoModifier);}
718- },
719- KeyModel {
720- text: "F8"
721- mainAction: Action { onTriggered: simulateKey(Qt.Key_F8, Qt.NoModifier);}
722- },
723- KeyModel {
724- text: "F9"
725- mainAction: Action { onTriggered: simulateKey(Qt.Key_F9, Qt.NoModifier);}
726- },
727- KeyModel {
728- text: "F10"
729- mainAction: Action { onTriggered: simulateKey(Qt.Key_F10, Qt.NoModifier);}
730- },
731- KeyModel {
732- text: "F11"
733- mainAction: Action { onTriggered: simulateKey(Qt.Key_F11, Qt.NoModifier);}
734- },
735- KeyModel {
736- text: "F12"
737- mainAction: Action { onTriggered: simulateKey(Qt.Key_F12, Qt.NoModifier);}
738- }
739- ]
740-}
741
742=== added file 'src/app/qml/KeyboardRows/Layouts/ScrollKeys.json'
743--- src/app/qml/KeyboardRows/Layouts/ScrollKeys.json 1970-01-01 00:00:00 +0000
744+++ src/app/qml/KeyboardRows/Layouts/ScrollKeys.json 2015-02-14 14:24:37 +0000
745@@ -0,0 +1,77 @@
746+{
747+ "name" : "Scroll Keys",
748+ "short_name" : "SCR",
749+
750+ "buttons": [
751+ {
752+ "main_action" : {
753+ "type": "key",
754+ "text" : "PG_UP",
755+ "key" : "PageUp"
756+ }
757+ },
758+ {
759+ "main_action" : {
760+ "type": "key",
761+ "text" : "PG_DN",
762+ "key" : "PageDown"
763+ }
764+ },
765+ {
766+ "main_action" : {
767+ "type": "key",
768+ "text" : "DEL",
769+ "key" : "Delete"
770+ }
771+ },
772+ {
773+ "main_action" : {
774+ "type": "key",
775+ "text" : "HOME",
776+ "key" : "Home"
777+ }
778+ },
779+ {
780+ "main_action" : {
781+ "type": "key",
782+ "text" : "END",
783+ "key" : "End"
784+ }
785+ },
786+ {
787+ "main_action" : {
788+ "type": "key",
789+ "text" : "TAB",
790+ "key" : "Tab"
791+ }
792+ },
793+ {
794+ "main_action" : {
795+ "type": "key",
796+ "text" : "\u2191",
797+ "key" : "Up"
798+ }
799+ },
800+ {
801+ "main_action" : {
802+ "type": "key",
803+ "text" : "\u2193",
804+ "key" : "Down"
805+ }
806+ },
807+ {
808+ "main_action" : {
809+ "type": "key",
810+ "text" : "\u2190",
811+ "key" : "Left"
812+ }
813+ },
814+ {
815+ "main_action" : {
816+ "type": "key",
817+ "text" : "\u2192",
818+ "key" : "Right"
819+ }
820+ }
821+ ]
822+}
823
824=== removed file 'src/app/qml/KeyboardRows/Layouts/ScrollKeysLayout.qml'
825--- src/app/qml/KeyboardRows/Layouts/ScrollKeysLayout.qml 2014-12-07 16:11:58 +0000
826+++ src/app/qml/KeyboardRows/Layouts/ScrollKeysLayout.qml 1970-01-01 00:00:00 +0000
827@@ -1,50 +0,0 @@
828-import QtQuick 2.0
829-import Ubuntu.Components 1.1
830-
831-import ".."
832-
833-KeyboardRow {
834- keyWidth: units.gu(8)
835- model: [
836- KeyModel {
837- text: "PG_UP"
838- mainAction: Action { onTriggered: simulateKey(Qt.Key_PageUp, Qt.NoModifier); }
839- },
840- KeyModel {
841- text: "PG_DN"
842- mainAction: Action { onTriggered: simulateKey(Qt.Key_PageDown, Qt.NoModifier); }
843- },
844- KeyModel {
845- text: "DEL"
846- mainAction: Action { onTriggered: simulateKey(Qt.Key_Delete, Qt.NoModifier); }
847- },
848- KeyModel {
849- text: "HOME"
850- mainAction: Action { onTriggered: simulateKey(Qt.Key_Home, Qt.NoModifier); }
851- },
852- KeyModel {
853- text: "END"
854- mainAction: Action { onTriggered: simulateKey(Qt.Key_End, Qt.NoModifier); }
855- },
856- KeyModel {
857- text: "TAB"
858- mainAction: Action { onTriggered: simulateKey(Qt.Key_Tab, Qt.NoModifier); }
859- },
860- KeyModel {
861- text: "\u2191"
862- mainAction: Action { onTriggered: simulateKey(Qt.Key_Up, Qt.NoModifier); }
863- },
864- KeyModel {
865- text: "\u2193"
866- mainAction: Action { onTriggered: simulateKey(Qt.Key_Down, Qt.NoModifier); }
867- },
868- KeyModel {
869- text: "\u2190"
870- mainAction: Action { onTriggered: simulateKey(Qt.Key_Left, Qt.NoModifier); }
871- },
872- KeyModel {
873- text: "\u2192"
874- mainAction: Action { onTriggered: simulateKey(Qt.Key_Right, Qt.NoModifier); }
875- }
876- ]
877-}
878
879=== added file 'src/app/qml/KeyboardRows/Layouts/SimpleCommands.json'
880--- src/app/qml/KeyboardRows/Layouts/SimpleCommands.json 1970-01-01 00:00:00 +0000
881+++ src/app/qml/KeyboardRows/Layouts/SimpleCommands.json 2015-02-14 14:24:37 +0000
882@@ -0,0 +1,87 @@
883+{
884+ "name" : "Comamnds Key",
885+ "short_name" : "CMD",
886+
887+ "buttons": [
888+ {
889+ "main_action" : {
890+ "type": "string",
891+ "text" : "top",
892+ "string" : "top\n"
893+ }
894+ },
895+ {
896+ "main_action" : {
897+ "type": "string",
898+ "text" : "clear",
899+ "string" : "clear\n"
900+ }
901+ },
902+ {
903+ "main_action" : {
904+ "type": "string",
905+ "text" : "ls",
906+ "string" : "ls\n"
907+ },
908+ "other_actions" : [
909+ {
910+ "type" : "string",
911+ "text" : "-l",
912+ "string" : "ls -l\n"
913+ },
914+ {
915+ "type": "string",
916+ "text": "-a",
917+ "string": "ls -a\n"
918+ }
919+ ]
920+ },
921+ {
922+ "main_action" : {
923+ "type": "string",
924+ "text" : "rm",
925+ "string" : "rm "
926+ },
927+ "other_actions" : [
928+ {
929+ "type" : "string",
930+ "text" : "-r",
931+ "string" : "rm -r "
932+ }
933+ ]
934+ },
935+ {
936+ "main_action" : {
937+ "type": "string",
938+ "text" : "find",
939+ "string" : "find "
940+ },
941+ "other_actions" : [
942+ {
943+ "type" : "string",
944+ "text" : "-name",
945+ "string" : "find -name "
946+ }
947+ ]
948+ },
949+ {
950+ "main_action" : {
951+ "type": "string",
952+ "text" : "chmod",
953+ "string" : "chmod "
954+ },
955+ "other_actions" : [
956+ {
957+ "type" : "string",
958+ "text" : "555",
959+ "string" : "chmod 555 "
960+ },
961+ {
962+ "type": "string",
963+ "text": "777",
964+ "string": "chmod 777 "
965+ }
966+ ]
967+ }
968+ ]
969+}
970
971=== removed file 'src/app/qml/KeyboardRows/Layouts/SimpleCommandsLayout.qml'
972--- src/app/qml/KeyboardRows/Layouts/SimpleCommandsLayout.qml 2014-12-07 16:46:56 +0000
973+++ src/app/qml/KeyboardRows/Layouts/SimpleCommandsLayout.qml 1970-01-01 00:00:00 +0000
974@@ -1,48 +0,0 @@
975-import QtQuick 2.0
976-import Ubuntu.Components 1.1
977-
978-import ".."
979-
980-KeyboardRow {
981- keyWidth: units.gu(8)
982- model: [
983- KeyModel {
984- text: "top"
985- mainAction: Action { onTriggered: simulateCommand("top\n"); }
986- },
987- KeyModel {
988- text: "clear"
989- mainAction: Action { onTriggered: simulateCommand("clear\n"); }
990- },
991- KeyModel {
992- text: "ls"
993- mainAction: Action { onTriggered: simulateCommand("ls\n"); }
994- actions: [
995- Action { text: "-a"; onTriggered: simulateCommand("ls -a\n"); },
996- Action { text: "-l"; onTriggered: simulateCommand("ls -l\n"); }
997- ]
998- },
999- KeyModel {
1000- text: "rm"
1001- mainAction: Action { onTriggered: simulateCommand("rm"); }
1002- actions: [
1003- Action { text: "-r"; onTriggered: simulateCommand("rm -r"); }
1004- ]
1005- },
1006- KeyModel {
1007- text: "find"
1008- mainAction: Action { onTriggered: simulateCommand("find"); }
1009- actions: [
1010- Action { text: "-name"; onTriggered: simulateCommand("find . -name "); }
1011- ]
1012- },
1013- KeyModel {
1014- text: "chmod"
1015- mainAction: Action { onTriggered: simulateCommand("chmod"); }
1016- actions: [
1017- Action { text: "555"; onTriggered: simulateCommand("chmod 555"); },
1018- Action { text: "777"; onTriggered: simulateCommand("chmod 777"); }
1019- ]
1020- }
1021- ]
1022-}
1023
1024=== added file 'src/app/qml/KeyboardRows/jsonParser.js'
1025--- src/app/qml/KeyboardRows/jsonParser.js 1970-01-01 00:00:00 +0000
1026+++ src/app/qml/KeyboardRows/jsonParser.js 2015-02-14 14:24:37 +0000
1027@@ -0,0 +1,78 @@
1028+.pragma library
1029+
1030+// This small library prints semantic errors of bad profiles.
1031+
1032+function isAllowed(value, allowed) {
1033+ return allowed.indexOf(value) !== -1;
1034+}
1035+
1036+function raiseException(msg, obj) {
1037+ throw msg + "\n" + JSON.stringify(obj, null, 2);
1038+}
1039+
1040+function validateKeyAction(keyObject) {
1041+ if (!keyObject.key)
1042+ return raiseException("key is missing");
1043+ if (keyObject.mod && !isAllowed(keyObject.mod, ["Control", "Shift", "Alt"]))
1044+ return raiseException("mod is invalid in", keyObject);
1045+}
1046+
1047+function validateStringAction(stringObject) {
1048+ if (!stringObject.string)
1049+ raiseException("string is missing in", stringObject);
1050+}
1051+
1052+function validateAction(actionObject) {
1053+ if (!actionObject.type)
1054+ raiseException("type is missing in", actionObject);
1055+ if (!isAllowed(actionObject.type, ["key", "string"]))
1056+ raiseException("type must be either key or string in", actionObject);
1057+ if (!actionObject.text)
1058+ raiseException("text is missing in", actionObject);
1059+
1060+ switch (actionObject.type) {
1061+ case "key":
1062+ validateKeyAction(actionObject);
1063+ break;
1064+ case "string":
1065+ validateStringAction(actionObject);
1066+ break;
1067+ }
1068+}
1069+
1070+function validateButton(buttonObject) {
1071+ if (!buttonObject.main_action)
1072+ raiseException("main_action is missing", buttonObject);
1073+
1074+ validateAction(buttonObject.main_action);
1075+
1076+ var other_actions = buttonObject.other_actions;
1077+ if (other_actions) {
1078+ if (!Array.isArray(other_actions))
1079+ raiseException("other_actions is not an array", other_actions);
1080+
1081+ for (var i = 0; i < other_actions.length; i++) {
1082+ validateAction(other_actions[i]);
1083+ }
1084+ }
1085+}
1086+
1087+function validateLayout(layoutObject) {
1088+ if (!layoutObject.name)
1089+ raiseException("name is missing in ", layoutObject);
1090+ if (!layoutObject.short_name)
1091+ raiseException("short_name is missing in", layoutObject);
1092+ if (!layoutObject.buttons)
1093+ raiseException("buttons is missing in", layoutObject);
1094+
1095+ for (var i = 0; i < layoutObject.buttons.length; i++) {
1096+ validateButton(layoutObject.buttons[i]);
1097+ }
1098+}
1099+
1100+function parseJson(jsonString) {
1101+ var jsonObject = JSON.parse(jsonString);
1102+ validateLayout(jsonObject);
1103+
1104+ return jsonObject;
1105+}
1106
1107=== modified file 'src/plugin/qmltermwidget/lib/TerminalDisplay.cpp'
1108--- src/plugin/qmltermwidget/lib/TerminalDisplay.cpp 2014-12-02 10:47:57 +0000
1109+++ src/plugin/qmltermwidget/lib/TerminalDisplay.cpp 2015-02-14 14:24:37 +0000
1110@@ -3248,9 +3248,17 @@
1111 setFillColor(cs->backgroundColor());
1112 }
1113
1114-void TerminalDisplay::simulateKeyPress(int key, int modifiers, bool pressed, quint32 nativeScanCode, const QString &text)
1115+void TerminalDisplay::simulateKeyPress(int key, int modifiers, bool pressed, quint32 nativeScanCode, QString text)
1116 {
1117 Q_UNUSED(nativeScanCode);
1118+
1119+ // TODO WORKAROUND: If text is not given we try to guess it from the unicode value.
1120+ // This solution is probably not complete on the domain, but works for the most common cases.
1121+ if (text.isEmpty()) {
1122+ bool shiftMod = modifiers & Qt::ShiftModifier;
1123+ text = QString(shiftMod ? QChar(key) : QChar(key).toLower());
1124+ }
1125+
1126 QEvent::Type type = pressed ? QEvent::KeyPress : QEvent::KeyRelease;
1127 QKeyEvent event = QKeyEvent(type, key, (Qt::KeyboardModifier) modifiers, text);
1128 keyPressedSignal(&event);
1129
1130=== modified file 'src/plugin/qmltermwidget/lib/TerminalDisplay.h'
1131--- src/plugin/qmltermwidget/lib/TerminalDisplay.h 2014-11-17 21:12:53 +0000
1132+++ src/plugin/qmltermwidget/lib/TerminalDisplay.h 2015-02-14 14:24:37 +0000
1133@@ -547,7 +547,7 @@
1134 void setColorScheme(const QString &name);
1135 QStringList availableColorSchemes();
1136
1137- void simulateKeyPress(int key, int modifiers, bool pressed, quint32 nativeScanCode, const QString &text);
1138+ void simulateKeyPress(int key, int modifiers, bool pressed, quint32 nativeScanCode, QString text);
1139 void simulateWheel(int x, int y, int buttons, int modifiers, QPointF angleDelta);
1140 void simulateMouseMove(int x, int y, int button, int buttons, int modifiers);
1141 void simulateMousePress(int x, int y, int button, int buttons, int modifiers);

Subscribers

People subscribed via source and target branches