on xenial help sign in
Merge
da6049eAlbert Astals
Applications/Application.qml+1−1
@@ -16,7 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import "applications.js" as ApplicationsModel
18import "applications.js" as ApplicationsModel
19−import Ubuntu.Application 0.1
19+import Unity.Application 0.1
20
20
21/* This class is temporary. It is meant to be API compatible with qthybris' Application
21/* This class is temporary. It is meant to be API compatible with qthybris' Application
22 class.
22 class.
Bottombar/Bottombar.qml+5−3
@@ -18,7 +18,7 @@ import QtQuick 2.0
18import "../Components"
18import "../Components"
19import "../Components/Math.js" as MathLocal
19import "../Components/Math.js" as MathLocal
20import Unity 0.1
20import Unity 0.1
21−import Ubuntu.Application 0.1
21+import Unity.Application 0.1
22import Ubuntu.Gestures 0.1
22import Ubuntu.Gestures 0.1
23
23
24Item {
24Item {
@@ -107,7 +107,7 @@ Item {
107 height: distanceThreshold
107 height: distanceThreshold
108 anchors.bottom: parent.bottom
108 anchors.bottom: parent.bottom
109
109
110− distanceThreshold: units.gu(8)
110+ distanceThreshold: units.gu(1)
111 enabled: !theHud.shown && bottombar.enabled && applicationIsOnForeground
111 enabled: !theHud.shown && bottombar.enabled && applicationIsOnForeground
112 direction: Direction.Upwards
112 direction: Direction.Upwards
113
113
@@ -119,6 +119,8 @@ Item {
119 if (previousStatus == DirectionalDragArea.Recognized) {
119 if (previousStatus == DirectionalDragArea.Recognized) {
120 if (hudButton.mouseOver) {
120 if (hudButton.mouseOver) {
121 hudButton.clicked()
121 hudButton.clicked()
122+ } else {
123+ bottombar.state = "hidden"
122 }
124 }
123 }
125 }
124 } else if (status === DirectionalDragArea.Undecided) {
126 } else if (status === DirectionalDragArea.Undecided) {
@@ -142,7 +144,7 @@ Item {
142
144
143 InputFilterArea {
145 InputFilterArea {
144 anchors.fill: parent
146 anchors.fill: parent
145− blockInput: hudButton.visible
147+ blockInput: (hudButton.opacity == 1)
146 }
148 }
147
149
148 states: [
150 states: [
CMakeLists.txt+4
@@ -62,10 +62,13 @@ set(SHELL_PRIVATE_LIBDIR ${CMAKE_INSTALL_LIBDIR}/unity8)
62
62
63configure_file(paths.h.in ${CMAKE_CURRENT_BINARY_DIR}/paths.h @ONLY)
63configure_file(paths.h.in ${CMAKE_CURRENT_BINARY_DIR}/paths.h @ONLY)
64
64
65+pkg_check_modules(UNITY-MIR REQUIRED unity-mir)
66+
65include_directories(
67include_directories(
66 ${CMAKE_CURRENT_BINARY_DIR}
68 ${CMAKE_CURRENT_BINARY_DIR}
67 ${CMAKE_CURRENT_SOURCE_DIR}
69 ${CMAKE_CURRENT_SOURCE_DIR}
68 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
70 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
71+ ${UNITY-MIR_INCLUDE_DIRS}
69 )
72 )
70
73
71set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
74set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
@@ -118,6 +121,7 @@ target_link_libraries(${SHELL_APP}
118 ${Qt5Qml_LIBRARIES}
121 ${Qt5Qml_LIBRARIES}
119 ${Qt5Quick_LIBRARIES}
122 ${Qt5Quick_LIBRARIES}
120 ${XCB_LDFLAGS}
123 ${XCB_LDFLAGS}
124+ ${CMAKE_DL_LIBS}
121 )
125 )
122
126
123# install binaries
127# install binaries
Components/ApplicationManagerWrapper.qml+89−42
@@ -15,15 +15,15 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18−import Ubuntu.Application 0.1
18+import Unity.Application 0.1
19
19
20Item {
20Item {
21 id: applicationManager
21 id: applicationManager
22
22
23− property var mainStageApplications: ApplicationManager.mainStageApplications
23+ property alias mainStageApplications: mainStageModel
24− property var sideStageApplications: ApplicationManager.sideStageApplications
24+ property alias sideStageApplications: sideStageModel
25− property var mainStageFocusedApplication: ApplicationManager.mainStageFocusedApplication
25+ property variant mainStageFocusedApplication: null
26− property var sideStageFocusedApplication: ApplicationManager.sideStageFocusedApplication
26+ property variant sideStageFocusedApplication: null
27 property bool sideStageEnabled: true
27 property bool sideStageEnabled: true
28 signal focusRequested(string desktopFile)
28 signal focusRequested(string desktopFile)
29 property bool keyboardVisible: ApplicationManager.keyboardVisible
29 property bool keyboardVisible: ApplicationManager.keyboardVisible
@@ -31,32 +31,76 @@ Item {
31
31
32 property bool fake: ApplicationManager.fake ? ApplicationManager.fake : false
32 property bool fake: ApplicationManager.fake ? ApplicationManager.fake : false
33
33
34− Component.onCompleted: {
34+ ApplicationsModelStageFiltered {
35− // Start the watcher so that the ApplicationManager applications model can be populated.
35+ id: mainStageModel
36− ApplicationManager.startWatcher();
36+ stage: ApplicationInfo.MainStage
37+ }
38+
39+ ApplicationsModelStageFiltered {
40+ id: sideStageModel
41+ stage: ApplicationInfo.SideStage
42+ }
43+
44+ Connections {
45+ target: ApplicationManager
46+ onFocusedApplicationIdChanged: {
47+ var app = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
48+ if (!app) { //nothing at all focused, so clear all
49+ mainStageFocusedApplication = null;
50+ sideStageFocusedApplication = null;
51+ } else {
52+ if (app.stage == ApplicationInfo.MainStage) {
53+ mainStageFocusedApplication = app;
54+ // possible the side stage app being unfocused fired this signal, so check for it
55+ if (sideStageFocusedApplication && !sideStageFocusedApplication.focused)
56+ sideStageFocusedApplication = null;
57+ } else {
58+ sideStageFocusedApplication = app;
59+ // possible the main stage app being unfocused fired this signal, so check for it
60+ if (mainStageFocusedApplication && !mainStageFocusedApplication.focused)
61+ mainStageFocusedApplication = null;
62+ }
63+ }
64+ }
37 }
65 }
38
66
39 function activateApplication(desktopFile, argument) {
67 function activateApplication(desktopFile, argument) {
40− var application;
68+ var appId;
41− application = getApplicationFromDesktopFile(desktopFile, ApplicationInfo.MainStage);
69+
42− if (application == null) {
70+ // HACK: Applications identified sometimes with with appId, but mostly with desktopFile.
43− application = getApplicationFromDesktopFile(desktopFile, ApplicationInfo.SideStage);
71+ // TODO: convert entire shell to use appId only.
72+ if (desktopFile.indexOf(".desktop") >= 0) {
73+ appId = desktopFileToAppId(desktopFile);
74+ } else {
75+ appId = desktopFile;
44 }
76 }
77+
78+ var application = ApplicationManager.findApplication(appId);
45− if (application != null) {
79+ if (application !== null) {
46 return application;
80 return application;
47 }
81 }
48
82
49 var execFlags = sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
83 var execFlags = sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
50
84
51 if (argument) {
85 if (argument) {
52− return ApplicationManager.startProcess(desktopFile, execFlags, [argument]);
86+ return ApplicationManager.startApplication(appId, execFlags, [argument]);
53 } else {
87 } else {
54− return ApplicationManager.startProcess(desktopFile, execFlags);
88+ return ApplicationManager.startApplication(appId, execFlags);
55 }
89 }
56 }
90 }
57
91
58− function stopProcess(application) {
92+ function stopApplication(application) {
93+ var appId;
94+
95+ // HACK: Applications identified sometimes with with appId, but mostly with desktopFile.
96+ // TODO: convert entire shell to use appId only.
97+ if (typeof application == "string") {
98+ appId = application;
99+ } else {
100+ appId = desktopFileToAppId(application.desktopFile);
101+ }
102+
59− ApplicationManager.stopProcess(application)
103+ ApplicationManager.stopApplication(appId);
60 }
104 }
61
105
62 function focusApplication(application) {
106 function focusApplication(application) {
@@ -64,51 +108,54 @@ Item {
64 return;
108 return;
65 }
109 }
66
110
67− ApplicationManager.focusApplication(application.handle);
111+ ApplicationManager.focusApplication(application.appId);
68 }
112 }
69
113
70 function unfocusCurrentApplication() {
114 function unfocusCurrentApplication() {
71− ApplicationManager.unfocusCurrentApplication(ApplicationInfo.MainStage);
115+ ApplicationManager.unfocusCurrentApplication();
72 }
116 }
73
117
74 function moveRunningApplicationStackPosition(from, to, stage) {
118 function moveRunningApplicationStackPosition(from, to, stage) {
75− var applications;
119+ if (from == to || from < 0 || to < 0) return;
120+
76 if (stage == ApplicationInfo.SideStage) {
121 if (stage == ApplicationInfo.SideStage) {
77− applications = ApplicationManager.sideStageApplications;
122+ sideStageModel.move(from, to);
78 } else {
123 } else {
79− applications = ApplicationManager.mainStageApplications;
124+ mainStageModel.move(from, to);
80 }
125 }
81− // FIXME: applications.move(0, 0) crashes
82− if (from !== to && from >= 0 && to >= 0) applications.move(from, to);
83 }
126 }
84
127
85 function getApplicationFromDesktopFile(desktopFile, stage) {
128 function getApplicationFromDesktopFile(desktopFile, stage) {
86− var foundSideStageApp, foundMainStageApp;
129+ var appId;
87
130
88− foundMainStageApp = __find(desktopFile, ApplicationManager.mainStageApplications)
131+ // HACK: Applications identified sometimes with with appId, but mostly with desktopFile.
89− if (stage == ApplicationInfo.MainStage) {
132+ // TODO: convert entire shell to use appId only.
90− return foundMainStageApp;
133+ if (desktopFile.indexOf(".desktop") >= 0) {
91− }
134+ appId = desktopFileToAppId(desktopFile);
92− foundSideStageApp = __find(desktopFile, ApplicationManager.sideStageApplications)
135+ } else {
93− if (stage == ApplicationInfo.SideStage) {
136+ appId = desktopFile;
94− return foundSideStageApp;
95 }
137 }
96
138
97− // if stage not specified, return whichever app running on either stage
139+ for (var i = 0, len = ApplicationManager.count; i < len; i++ ) {
98− return (foundMainStageApp) ? foundMainStageApp : foundSideStageApp;
140+ var app = ApplicationManager.get(i);
99−
100
141
101− function __find(desktopFile, applications) {
142+ // if stage not specified, return whichever app running on either stage
102− for (var i = 0; i < applications.count; i++ ) {
143+ if (app.appId == appId && (stage == undefined || app.stage == stage)) {
103− var application = applications.get(i);
104− if (application.desktopFile == desktopFile) {
105− return application;
144+ return app;
106− }
107 }
145 }
108− return null;
109 }
146 }
110 }
147 }
111
148
149+ function desktopFileToAppId(desktopFile) {
150+ var right = desktopFile.lastIndexOf(".desktop");
151+ var left = desktopFile.lastIndexOf("/");
152+ if (left == -1 || right == -1 || left == right) {
153+ console.log("ApplicationManagerWrapper: unable to extract appId from '" + desktopFile + "'");
154+ return "";
155+ }
156+ return desktopFile.substring(left+1, right);
157+ }
158+
112 Connections {
159 Connections {
113 target: ApplicationManager
160 target: ApplicationManager
114 onFocusRequested: {
161 onFocusRequested: {
Components/ApplicationScreenshot.qml+2−2
@@ -16,7 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import Ubuntu.Application 0.1
19+import Unity.Application 0.1
20
20
21Item {
21Item {
22 id: applicationScreenshot
22 id: applicationScreenshot
@@ -56,6 +56,6 @@ Item {
56 width: applicationScreenshot.application ? parent.width : 0
56 width: applicationScreenshot.application ? parent.width : 0
57 height: applicationScreenshot.application ? parent.height : 0
57 height: applicationScreenshot.application ? parent.height : 0
58 visible: applicationScreenshot.application != null && ready
58 visible: applicationScreenshot.application != null && ready
59− source: applicationScreenshot.application
59+ source: ApplicationManager.findApplication((application) ? application.appId : "")
60 }
60 }
61}
61}
Components/ApplicationsModelStageFiltered.qmlnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Utils 0.1
19+import Unity.Application 0.1
20+
21+SortFilterProxyModel {
22+ id: root
23+ property int stage
24+
25+ function get(index) {
26+ return model.get(mapRowToSource(index));
27+ }
28+
29+ function move(from, to) {
30+ var realFrom = mapRowToSource(from);
31+ if (realFrom == -1) {
32+ console.log("ERROR; invalid from=" + from + " index in move operation");
33+ return;
34+ }
35+ var realTo = mapRowToSource(to);
36+ if (realTo == -1) {
37+ // assuming wanting to move to end of list
38+ realTo = model.count - 1;
39+ }
40+
41+ if (realFrom == realTo) return;
42+ model.move(realFrom, realTo);
43+ }
44+
45+ model: ApplicationManager
46+ dynamicSortFilter: true
47+ filterRole: ApplicationManager.RoleStage
48+ filterRegExp: RegExp(stage)
49+}
Components/EdgeDemo.qml+28−21
@@ -43,15 +43,12 @@ Item {
43 }
43 }
44
44
45 function hideEdgeDemoInShell() {
45 function hideEdgeDemoInShell() {
46− var user = LightDM.Users.data(greeter.currentIndex, LightDM.UserRoles.NameRole);
46+ AccountsService.demoEdges = false;
47− AccountsService.setUserProperty(user, "demo-edges", false);
48− d.showEdgeDemo = false;
49 stopDemo();
47 stopDemo();
50 }
48 }
51
49
52 function hideEdgeDemoInGreeter() {
50 function hideEdgeDemoInGreeter() {
53− // TODO: AccountsService.setUserProperty("lightdm", "demo-edges", false);
51+ // TODO: AccountsService.demoEdges = false as lightdm user
54− d.showEdgeDemoInGreeter = false;
55 }
52 }
56
53
57 function hideEdgeDemos() {
54 function hideEdgeDemos() {
@@ -71,6 +68,24 @@ Item {
71 if (d.finalEdgeDemo) d.finalEdgeDemo.destroy()
68 if (d.finalEdgeDemo) d.finalEdgeDemo.destroy()
72 }
69 }
73
70
71+ function startDemo() {
72+ if (!d.overlay) {
73+ d.overlay = Qt.createComponent("EdgeDemoOverlay.qml")
74+ }
75+
76+ launcherEnabled = false;
77+ dashEnabled = false;
78+ panelEnabled = false;
79+ panelContentEnabled = false;
80+
81+ // Begin with either greeter or dash, depending on which is visible
82+ if (greeter && greeter.shown) {
83+ startRightEdgeDemo()
84+ } else {
85+ startTopEdgeDemo()
86+ }
87+ }
88+
74 QtObject {
89 QtObject {
75 id: d
90 id: d
76 property Component overlay
91 property Component overlay
@@ -79,22 +94,18 @@ Item {
79 property QtObject bottomEdgeDemo
94 property QtObject bottomEdgeDemo
80 property QtObject leftEdgeDemo
95 property QtObject leftEdgeDemo
81 property QtObject finalEdgeDemo
96 property QtObject finalEdgeDemo
82− property bool showEdgeDemo: false
97+ property bool showEdgeDemo: AccountsService.demoEdges
83− property bool showEdgeDemoInGreeter: d.showEdgeDemo // TODO: AccountsService.getUserProperty("lightdm", "demo-edges")
98+ property bool showEdgeDemoInGreeter: AccountsService.demoEdges // TODO: AccountsService.demoEdges as lightdm user
84
99
85− onShowEdgeDemoInGreeterChanged: {
100+ onShowEdgeDemoChanged: {
86− if (!d.overlay && d.showEdgeDemoInGreeter) {
101+ stopDemo()
87− d.overlay = Qt.createComponent("EdgeDemoOverlay.qml")
102+ if (d.showEdgeDemo) {
88− startRightEdgeDemo()
103+ startDemo()
89 }
104 }
90 }
105 }
91 }
106 }
92
107
93 function startRightEdgeDemo() {
108 function startRightEdgeDemo() {
94− launcherEnabled = false;
95− demo.dashEnabled = false;
96− demo.panelEnabled = false;
97− demo.panelContentEnabled = false;
98 if (demo.greeter) {
109 if (demo.greeter) {
99 d.rightEdgeDemo = d.overlay.createObject(demo.greeter, {
110 d.rightEdgeDemo = d.overlay.createObject(demo.greeter, {
100 "edge": "right",
111 "edge": "right",
@@ -123,11 +134,6 @@ Item {
123
134
124 onUnlocked: hide()
135 onUnlocked: hide()
125 onShownChanged: if (!greeter.shown) hide()
136 onShownChanged: if (!greeter.shown) hide()
126−
127− onSelected: {
128− var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole)
129− d.showEdgeDemo = AccountsService.getUserProperty(user, "demo-edges")
130− }
131 }
137 }
132
138
133 function startTopEdgeDemo() {
139 function startTopEdgeDemo() {
@@ -218,8 +224,9 @@ Item {
218 d.finalEdgeDemo = d.overlay.createObject(demo.underlay, {
224 d.finalEdgeDemo = d.overlay.createObject(demo.underlay, {
219 "edge": "none",
225 "edge": "none",
220 "title": i18n.tr("Well done"),
226 "title": i18n.tr("Well done"),
221− "text": i18n.tr("You have now mastered the edge gestures and can start using the phone"),
227+ "text": i18n.tr("You have now mastered the edge gestures and can start using the phone<br><br>Tap anywhere to start"),
222 "anchors.fill": demo.dash,
228 "anchors.fill": demo.dash,
229+ "showSkip": false,
223 });
230 });
224 }
231 }
225 if (d.finalEdgeDemo) {
232 if (d.finalEdgeDemo) {
Components/EdgeDemoOverlay.qml+6−1
@@ -17,7 +17,7 @@
17import Powerd 0.1
17import Powerd 0.1
18import QtQuick 2.0
18import QtQuick 2.0
19import QtGraphicalEffects 1.0
19import QtGraphicalEffects 1.0
20−import Ubuntu.Application 0.1
20+import Unity.Application 0.1
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22
22
23Showable {
23Showable {
@@ -43,6 +43,11 @@ Showable {
43 */
43 */
44 property alias skipText: skipLabel.text
44 property alias skipText: skipLabel.text
45
45
46+ /*
47+ * This is the visible status of the skip button.
48+ */
49+ property alias showSkip: skipLabel.visible
50+
46 /*
51 /*
47 * Whether this demo is running currently.
52 * Whether this demo is running currently.
48 */
53 */
Components/IconUtil.jsdeleted−42
@@ -1,42 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−.pragma library
18−
19−// FIXME: a quick hack to get icons out of gicons and fallback to
20−// base icon for annotated icons. Doesn't correctly handle all icons.
21−// Proper global solution needed.
22−function from_gicon(name) {
23− var icon_name = name;
24− var annotated_re = /^. UnityProtocolAnnotatedIcon/;
25− if (annotated_re.test(name)) {
26− var base_icon_re = /'base-icon':.+?'(.+?)'/;
27− var base_icon = name.toString().match(base_icon_re);
28− icon_name = base_icon[1];
29− } else {
30− var themed_re = /^. GThemedIcon\s*([^\s]+)\s*/;
31− var themed = name.match(themed_re);
32− if (themed) {
33− return "image://gicon/" + themed[1];
34− }
35− }
36− var name_re = /^[a-z0-9\._-]+$/;
37− if (name_re.test(icon_name)) {
38− return "image://gicon/" + icon_name;
39− } else {
40− return icon_name;
41− }
42−}
Components/Stage.qml+1−1
@@ -15,7 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18−import Ubuntu.Application 0.1
18+import Unity.Application 0.1
19import Ubuntu.Components 0.1
19import Ubuntu.Components 0.1
20import Ubuntu.Gestures 0.1
20import Ubuntu.Gestures 0.1
21
21
Components/VolumeControl.qml+5−20
@@ -21,37 +21,22 @@ Item {
21 id: root
21 id: root
22 objectName: "volumeControl"
22 objectName: "volumeControl"
23 visible: false
23 visible: false
24− property int volume
25
24
26 QDBusActionGroup {
25 QDBusActionGroup {
27 id: actionGroup
26 id: actionGroup
28 busType: 1
27 busType: 1
29− busName: "com.canonical.settings.sound"
28+ busName: "com.canonical.indicator.sound"
30− objectPath: "/com/canonical/settings/sound"
29+ objectPath: "/com/canonical/indicator/sound"
31
30
32− property variant actionObject: action("volume")
31+ property variant actionObject: action("scroll")
33− property variant serverVolume: actionObject.valid ? actionObject.state: 0
34− }
35−
36− Binding {
37− target: root
38− property: "volume"
39− value: actionGroup.serverVolume * 100
40− }
41−
42− onVolumeChanged: {
43− if (actionGroup.serverVolume != volume) {
44− var targetVolume = Math.min(1, Math.max(0, volume / 100));
45− actionGroup.actionObject.updateState(targetVolume);
46− }
47 }
32 }
48
33
49 function volumeUp() {
34 function volumeUp() {
50− actionGroup.actionObject.updateState(Math.min(1, volume/100 + 0.10));
35+ actionGroup.actionObject.activate(1);
51 }
36 }
52
37
53 function volumeDown() {
38 function volumeDown() {
54− actionGroup.actionObject.updateState(Math.max(0, volume/100 - 0.10));
39+ actionGroup.actionObject.activate(-1);
55 }
40 }
56
41
57 Component.onCompleted: actionGroup.start()
42 Component.onCompleted: actionGroup.start()
Dash/Apps/AppPreview.qml+1−2
@@ -20,7 +20,6 @@ import Ubuntu.Components.ListItems 0.1 as ListItem
20import Ubuntu.DownloadDaemonListener 0.1
20import Ubuntu.DownloadDaemonListener 0.1
21import ".."
21import ".."
22import "../../Components"
22import "../../Components"
23−import "../../Components/IconUtil.js" as IconUtil
24
23
25DashPreview {
24DashPreview {
26 id: root
25 id: root
@@ -137,7 +136,7 @@ DashPreview {
137 anchors { left: parent.left; right: parent.right }
136 anchors { left: parent.left; right: parent.right }
138
137
139 appName: root.previewData.title
138 appName: root.previewData.title
140− icon: IconUtil.from_gicon(root.previewData.appIcon)
139+ icon: root.previewData.appIcon
141 rating: Math.round(root.previewData.rating * 10)
140 rating: Math.round(root.previewData.rating * 10)
142 reviews: root.previewData.numRatings
141 reviews: root.previewData.numRatings
143 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
142 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
Dash/Apps/ApplicationsFilterGrid.qml+1−2
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import "../../Components"
18import "../../Components"
19import "../../Applications"
19import "../../Applications"
20−import "../../Components/IconUtil.js" as IconUtil
21
20
22FilterGrid {
21FilterGrid {
23 id: filterGrid
22 id: filterGrid
@@ -50,7 +49,7 @@ FilterGrid {
50 text: model.title ? model.title : application.name // FIXME: this is temporary
49 text: model.title ? model.title : application.name // FIXME: this is temporary
51 imageWidth: units.gu(8)
50 imageWidth: units.gu(8)
52 imageHeight: units.gu(7.5)
51 imageHeight: units.gu(7.5)
53− source: IconUtil.from_gicon(icon)
52+ source: icon
54 onClicked: filterGrid.clicked(index, application.desktopFile);
53 onClicked: filterGrid.clicked(index, application.desktopFile);
55 }
54 }
56}
55}
Dash/Apps/RunningApplicationTile.qml+2−3
@@ -16,7 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import Ubuntu.Application 0.1
19+import Unity.Application 0.1
20import "../../Components/"
20import "../../Components/"
21import "../../Components/ListItems"
21import "../../Components/ListItems"
22
22
@@ -76,10 +76,9 @@ AbstractButton {
76
76
77 ApplicationImage {
77 ApplicationImage {
78 id: applicationImage
78 id: applicationImage
79− source: application
79+ source: ApplicationManager.findApplication((application) ? application.appId : "")
80 width: shapedApplicationImage.width
80 width: shapedApplicationImage.width
81 height: shapedApplicationImage.height
81 height: shapedApplicationImage.height
82− fillMode: ApplicationImage.PreserveAspectCrop
83 }
82 }
84
83
85 UbuntuShape {
84 UbuntuShape {
Dash/Apps/RunningApplicationsGrid.qml+4−4
@@ -52,22 +52,22 @@ ResponsiveFlowView {
52
52
53 RunningApplicationTile {
53 RunningApplicationTile {
54 id: runningAppTile
54 id: runningAppTile
55− objectName: "runningAppTile " + model.application.name
55+ objectName: "runningAppTile " + model.name
56 anchors {
56 anchors {
57 top: parent.top
57 top: parent.top
58 horizontalCenter: parent.horizontalCenter
58 horizontalCenter: parent.horizontalCenter
59 }
59 }
60− application: model.application
60+ application: model
61 onRequestedActivationMode: { root.terminationModeEnabled = false }
61 onRequestedActivationMode: { root.terminationModeEnabled = false }
62 onRequestedTerminationMode: {
62 onRequestedTerminationMode: {
63 if (canEnableTerminationMode)
63 if (canEnableTerminationMode)
64 root.terminationModeEnabled = true
64 root.terminationModeEnabled = true
65 }
65 }
66 onRequestedApplicationTermination: {
66 onRequestedApplicationTermination: {
67− shell.applicationManager.stopProcess(application)
67+ shell.applicationManager.stopApplication(model.appId)
68 }
68 }
69 onRequestedApplicationActivation: {
69 onRequestedApplicationActivation: {
70− shell.activateApplication(application.desktopFile)
70+ shell.activateApplication(model.appId)
71 }
71 }
72
72
73 terminationModeEnabled: root.terminationModeEnabled
73 terminationModeEnabled: root.terminationModeEnabled
Dash/DashBar.qml+2−3
@@ -83,9 +83,8 @@ Item {
83 }
83 }
84
84
85 delegate:
85 delegate:
86− Item {
86+ // FIXME: Use Panel's integrated trigger() once that works again.
87− signal clicked()
87+ MouseArea {
88−
89 width: itemSize
88 width: itemSize
90 height: dashBar.height
89 height: dashBar.height
91 anchors.top: parent.top
90 anchors.top: parent.top
Dash/Generic/GenericCarousel.qml+1−2
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import "../../Components"
19import "../../Components"
20−import "../../Components/IconUtil.js" as IconUtil
21
20
22Carousel {
21Carousel {
23 id: genericCarousel
22 id: genericCarousel
@@ -42,7 +41,7 @@ Carousel {
42 image: Image {
41 image: Image {
43 asynchronous: true
42 asynchronous: true
44 sourceSize { width: item.width; height: item.height }
43 sourceSize { width: item.width; height: item.height }
45− source: model ? IconUtil.from_gicon(model.icon) : ""
44+ source: model ? model.icon : ""
46 }
45 }
47 }
46 }
48
47
Dash/Generic/GenericFilterGrid.qml+1−2
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import "../../Components"
18import "../../Components"
19−import "../../Components/IconUtil.js" as IconUtil
20
19
21FilterGrid {
20FilterGrid {
22 id: filtergrid
21 id: filtergrid
@@ -41,7 +40,7 @@ FilterGrid {
41 imageWidth: filtergrid.iconWidth
40 imageWidth: filtergrid.iconWidth
42 imageHeight: filtergrid.iconHeight
41 imageHeight: filtergrid.iconHeight
43
42
44− source: IconUtil.from_gicon(model.icon)
43+ source: model.icon
45
44
46 fillMode: Image.PreserveAspectCrop
45 fillMode: Image.PreserveAspectCrop
47
46
Dash/Generic/GenericPreview.qml+1−2
@@ -18,7 +18,6 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import "../../Components"
19import "../../Components"
20import "../../Components/ListItems" as ListItems
20import "../../Components/ListItems" as ListItems
21−import "../../Components/IconUtil.js" as IconUtil
22import ".."
21import ".."
23
22
24DashPreview {
23DashPreview {
@@ -27,7 +26,7 @@ DashPreview {
27 title: previewData.title
26 title: previewData.title
28 previewWidthRatio: 0.6
27 previewWidthRatio: 0.6
29
28
30− property url url: IconUtil.from_gicon(previewData.image)
29+ property url url: previewData.image
31
30
32 header: UbuntuShape {
31 header: UbuntuShape {
33 id: urlLoader
32 id: urlLoader
Dash/GenericScopeView.qml+8−6
@@ -18,14 +18,14 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import "../Components"
19import "../Components"
20import "../Components/ListItems" as ListItems
20import "../Components/ListItems" as ListItems
21−import "../Components/IconUtil.js" as IconUtil
22
21
23ScopeView {
22ScopeView {
24 id: scopeView
23 id: scopeView
25− property alias previewShown: previewLoader.onScreen
24+ readonly property alias previewShown: previewLoader.onScreen
26
25
27 onIsCurrentChanged: {
26 onIsCurrentChanged: {
28 pageHeader.resetSearch();
27 pageHeader.resetSearch();
28+ previewLoader.open = false;
29 }
29 }
30
30
31 onMovementStarted: categoryView.showHeader()
31 onMovementStarted: categoryView.showHeader()
@@ -74,12 +74,12 @@ ScopeView {
74 onLoaded: {
74 onLoaded: {
75 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
75 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
76 // TODO: the running apps grid doesn't support standard scope results model yet
76 // TODO: the running apps grid doesn't support standard scope results model yet
77− item.firstModel = results.firstModel
77+ item.firstModel = Qt.binding(function() { return results.firstModel })
78− item.secondModel = results.secondModel
78+ item.secondModel = Qt.binding(function() { return results.secondModel })
79 } else {
79 } else {
80− item.model = results
80+ item.model = Qt.binding(function() { return results })
81 }
81 }
82− item.objectName = categoryId
82+ item.objectName = Qt.binding(function() { return categoryId })
83 if (item.expandable) {
83 if (item.expandable) {
84 var shouldFilter = index != categoryView.expandedIndex;
84 var shouldFilter = index != categoryView.expandedIndex;
85 if (shouldFilter != item.filter) {
85 if (shouldFilter != item.filter) {
@@ -180,6 +180,7 @@ ScopeView {
180 case "grid": {
180 case "grid": {
181 switch (contentType) {
181 switch (contentType) {
182 case "video": return "Generic/GenericFilterGridPotrait.qml";
182 case "video": return "Generic/GenericFilterGridPotrait.qml";
183+ case "music": return "Music/MusicFilterGrid.qml";
183 default: return "Generic/GenericFilterGrid.qml";
184 default: return "Generic/GenericFilterGrid.qml";
184 }
185 }
185 }
186 }
@@ -247,6 +248,7 @@ ScopeView {
247 }
248 }
248
249
249 Loader {
250 Loader {
251+ objectName: "previewLoader"
250 id: previewLoader
252 id: previewLoader
251 property var previewData
253 property var previewData
252 height: effect.bottomGapPx - effect.topGapPx
254 height: effect.bottomGapPx - effect.topGapPx
Dash/graphics/phone/home.pngdeleted

Binary file not shown.

Dash/graphics/phone/music.pngdeleted

Binary file not shown.

Dash/graphics/phone/videos.pngdeleted

Binary file not shown.

Dash/graphics/tablet/apps.pngdeleted

Binary file not shown.

Dash/graphics/tablet/home.pngdeleted

Binary file not shown.

Dash/graphics/tablet/music.pngdeleted

Binary file not shown.

Dash/graphics/tablet/people.pngdeleted

Binary file not shown.

Dash/graphics/tablet/videos.pngdeleted

Binary file not shown.

Greeter/Greeter.qml+2
@@ -26,6 +26,8 @@ Showable {
26 enabled: shown
26 enabled: shown
27 created: greeterContentLoader.status == Loader.Ready && greeterContentLoader.item.ready
27 created: greeterContentLoader.status == Loader.Ready && greeterContentLoader.item.ready
28
28
29+ property url defaultBackground
30+
29 // 1 when fully shown and 0 when fully hidden
31 // 1 when fully shown and 0 when fully hidden
30 property real showProgress: MathLocal.clamp((width + x) / width, 0, 1)
32 property real showProgress: MathLocal.clamp((width + x) / width, 0, 1)
31
33
Greeter/GreeterContent.qml+20−2
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import AccountsService 0.1
18import Ubuntu.Components 0.1
19import Ubuntu.Components 0.1
19import LightDM 0.1 as LightDM
20import LightDM 0.1 as LightDM
20import "../Components"
21import "../Components"
@@ -39,14 +40,31 @@ MouseArea {
39 color: "black"
40 color: "black"
40 }
41 }
41
42
43+ property url backgroundValue: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : greeter.defaultBackground
44+ onBackgroundValueChanged: wallpaper.source = backgroundValue
45+
42 CrossFadeImage {
46 CrossFadeImage {
43 id: wallpaper
47 id: wallpaper
44−
48+ objectName: "wallpaper"
45− source: shell.background
46 anchors.fill: parent
49 anchors.fill: parent
47 fadeInFirst: false
50 fadeInFirst: false
48 }
51 }
49
52
53+ // See Shell.qml's backgroundSettings treatment for why we need a separate
54+ // Image, but it boils down to avoiding binding loop detection.
55+ Image {
56+ source: wallpaper.source
57+ height: 0
58+ width: 0
59+ sourceSize.height: 0
60+ sourceSize.width: 0
61+ onStatusChanged: {
62+ if (status == Image.Error && source != greeter.defaultBackground) {
63+ wallpaper.source = greeter.defaultBackground
64+ }
65+ }
66+ }
67+
50 Rectangle {
68 Rectangle {
51 anchors.fill: parent
69 anchors.fill: parent
52 color: "black"
70 color: "black"
Greeter/Infographics.qml+1−2
@@ -286,9 +286,8 @@ Item {
286 objectName: "backgroundCircle"
286 objectName: "backgroundCircle"
287
287
288 anchors.fill: parent
288 anchors.fill: parent
289− opacity: 0.9
290
289
291− source: "graphics/infographic_circle.png"
290+ source: "graphics/infographic_circle_back.png"
292 }
291 }
293
292
294 Timer {
293 Timer {
Greeter/graphics/infographic_circle.pngdeleted

Binary file not shown.

Greeter/graphics/infographic_circle_back.pngnew file

Binary file not shown.

Launcher/Launcher.qml+4−4
@@ -35,7 +35,7 @@ Item {
35 readonly property bool shown: panel.x > -panel.width
35 readonly property bool shown: panel.x > -panel.width
36
36
37 // emitted when an application is selected
37 // emitted when an application is selected
38− signal launcherApplicationSelected(string desktopFile)
38+ signal launcherApplicationSelected(string appId)
39
39
40 // emitted when the apps dash should be shown because of a swipe gesture
40 // emitted when the apps dash should be shown because of a swipe gesture
41 signal dash()
41 signal dash()
@@ -159,7 +159,7 @@ Item {
159
159
160 onApplicationSelected: {
160 onApplicationSelected: {
161 root.state = ""
161 root.state = ""
162− launcherApplicationSelected(desktopFile)
162+ launcherApplicationSelected(appId)
163 }
163 }
164 onDashItemSelected: {
164 onDashItemSelected: {
165 root.state = ""
165 root.state = ""
@@ -192,12 +192,12 @@ Item {
192
192
193 direction: Direction.Rightwards
193 direction: Direction.Rightwards
194
194
195− enabled: root.available && root.state !== "visible"
195+ enabled: root.available
196 width: root.dragAreaWidth
196 width: root.dragAreaWidth
197 height: root.height
197 height: root.height
198
198
199 onTouchXChanged: {
199 onTouchXChanged: {
200− if (status !== DirectionalDragArea.Recognized)
200+ if (status !== DirectionalDragArea.Recognized || launcher.state == "visible")
201 return;
201 return;
202
202
203 // When the gesture finally gets recognized, the finger will likely be
203 // When the gesture finally gets recognized, the finger will likely be
Launcher/LauncherDelegate.qml+20−9
@@ -24,6 +24,7 @@ Item {
24 property int count: -1
24 property int count: -1
25 property int progress: -1
25 property int progress: -1
26 property bool highlighted: false
26 property bool highlighted: false
27+ property bool itemFocused: false
27 property real maxAngle: 0
28 property real maxAngle: 0
28 property bool inverted: false
29 property bool inverted: false
29
30
@@ -43,17 +44,18 @@ Item {
43 onCountChanged: shaderEffectSource.scheduleUpdate();
44 onCountChanged: shaderEffectSource.scheduleUpdate();
44 onProgressChanged: shaderEffectSource.scheduleUpdate();
45 onProgressChanged: shaderEffectSource.scheduleUpdate();
45 onHighlightedChanged: shaderEffectSource.scheduleUpdate();
46 onHighlightedChanged: shaderEffectSource.scheduleUpdate();
47+ onItemFocusedChanged: shaderEffectSource.scheduleUpdate();
46
48
47 Item {
49 Item {
48 id: iconItem
50 id: iconItem
49− width: parent.itemWidth
51+ width: parent.itemWidth + units.gu(1)
50− height: parent.itemHeight
52+ height: parent.itemHeight + units.gu(1)
51 anchors.centerIn: parent
53 anchors.centerIn: parent
52
54
53 UbuntuShape {
55 UbuntuShape {
54 id: iconShape
56 id: iconShape
55 anchors.fill: parent
57 anchors.fill: parent
56− anchors.margins: units.gu(0.5)
58+ anchors.margins: units.gu(1)
57 radius: "medium"
59 radius: "medium"
58
60
59 image: Image {
61 image: Image {
@@ -78,7 +80,7 @@ Item {
78 anchors {
80 anchors {
79 right: parent.right
81 right: parent.right
80 top: parent.top
82 top: parent.top
81− margins: -units.dp(1)
83+ margins: units.dp(3)
82 }
84 }
83 width: Math.min(root.itemWidth, Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
85 width: Math.min(root.itemWidth, Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
84 height: units.gu(3)
86 height: units.gu(3)
@@ -105,9 +107,9 @@ Item {
105 left: iconItem.left
107 left: iconItem.left
106 right: iconItem.right
108 right: iconItem.right
107 bottom: iconItem.bottom
109 bottom: iconItem.bottom
108− leftMargin: units.gu(0.5)
110+ leftMargin: units.gu(1)
109− rightMargin: units.gu(0.5)
111+ rightMargin: units.gu(1)
110− bottomMargin: units.gu(0.5)
112+ bottomMargin: units.gu(1)
111 }
113 }
112 height: units.gu(1.5)
114 height: units.gu(1.5)
113 visible: root.progress > -1
115 visible: root.progress > -1
@@ -136,14 +138,23 @@ Item {
136 }
138 }
137 }
139 }
138 }
140 }
141+ Image {
142+ objectName: "focusedHighlight"
143+ anchors {
144+ right: parent.right
145+ verticalCenter: parent.verticalCenter
146+ }
147+ visible: root.itemFocused
148+ source: "graphics/focused_app_arrow.png"
149+ }
139 }
150 }
140
151
141 ShaderEffect {
152 ShaderEffect {
142 id: transformEffect
153 id: transformEffect
143 anchors.centerIn: parent
154 anchors.centerIn: parent
144 anchors.verticalCenterOffset: root.offset
155 anchors.verticalCenterOffset: root.offset
145− width: parent.itemWidth
156+ width: iconItem.width
146− height: parent.itemHeight
157+ height: iconItem.height
147 property real itemOpacity: root.itemOpacity
158 property real itemOpacity: root.itemOpacity
148 property real brightness: Math.max(-1, root.brightness)
159 property real brightness: Math.max(-1, root.brightness)
149 property real angle: root.angle
160 property real angle: root.angle
Launcher/LauncherPanel.qml+12−5
@@ -34,7 +34,7 @@ Item {
34 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
34 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
35 property int highlightIndex: -1
35 property int highlightIndex: -1
36
36
37− signal applicationSelected(string desktopFile)
37+ signal applicationSelected(string appId)
38 signal dashItemSelected(int index)
38 signal dashItemSelected(int index)
39
39
40 BorderImage {
40 BorderImage {
@@ -50,8 +50,6 @@ Item {
50 id: mainColumn
50 id: mainColumn
51 anchors {
51 anchors {
52 fill: parent
52 fill: parent
53− leftMargin: units.gu(0.5)
54− rightMargin: units.gu(0.5)
55 }
53 }
56
54
57 MouseArea {
55 MouseArea {
@@ -94,6 +92,8 @@ Item {
94 fill: parent
92 fill: parent
95 topMargin: -extensionSize + units.gu(0.5)
93 topMargin: -extensionSize + units.gu(0.5)
96 bottomMargin: -extensionSize + units.gu(1)
94 bottomMargin: -extensionSize + units.gu(1)
95+ leftMargin: units.gu(0.5)
96+ rightMargin: units.gu(0.5)
97 }
97 }
98 topMargin: extensionSize
98 topMargin: extensionSize
99 bottomMargin: extensionSize
99 bottomMargin: extensionSize
@@ -156,6 +156,7 @@ Item {
156 iconName: model.icon
156 iconName: model.icon
157 count: model.count
157 count: model.count
158 progress: model.progress
158 progress: model.progress
159+ itemFocused: model.focused
159 inverted: root.inverted
160 inverted: root.inverted
160 z: -Math.abs(offset)
161 z: -Math.abs(offset)
161 maxAngle: 55
162 maxAngle: 55
@@ -299,7 +300,7 @@ Item {
299 } else if (clickedItem.angle < -12) {
300 } else if (clickedItem.angle < -12) {
300 launcherListView.flick(0, launcherListView.clickFlickSpeed);
301 launcherListView.flick(0, launcherListView.clickFlickSpeed);
301 } else {
302 } else {
302− root.applicationSelected(LauncherModel.get(index).desktopFile);
303+ root.applicationSelected(LauncherModel.get(index).appId);
303 }
304 }
304 return;
305 return;
305 }
306 }
@@ -310,7 +311,7 @@ Item {
310 } else if (clickedItem.angle < -30) {
311 } else if (clickedItem.angle < -30) {
311 launcherListView.flick(0, launcherListView.clickFlickSpeed);
312 launcherListView.flick(0, launcherListView.clickFlickSpeed);
312 } else {
313 } else {
313− root.applicationSelected(LauncherModel.get(index).desktopFile);
314+ root.applicationSelected(LauncherModel.get(index).appId);
314 }
315 }
315 }
316 }
316
317
@@ -512,6 +513,12 @@ Item {
512 objectName: "quickListEntry" + index
513 objectName: "quickListEntry" + index
513 text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
514 text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
514 highlightWhenPressed: model.clickable
515 highlightWhenPressed: model.clickable
516+
517+ // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
518+ // ListItems don't know that they are sitting in a themed Popover where the color
519+ // needs to be inverted.
520+ __foregroundColor: Theme.palette.selected.backgroundText
521+
515 onClicked: {
522 onClicked: {
516 if (!model.clickable) {
523 if (!model.clickable) {
517 return;
524 return;
Launcher/graphics/focused_app_arrow@30.pngnew file

Binary file not shown.

MouseTouchAdaptor.cpp+29−12
@@ -39,24 +39,16 @@ Qt::MouseButton translateMouseButton(xcb_button_t detail)
39} // end of anonymous namespace
39} // end of anonymous namespace
40
40
41MouseTouchAdaptor::MouseTouchAdaptor()
41MouseTouchAdaptor::MouseTouchAdaptor()
42− : m_targetWindow(0)
43− , m_leftButtonIsPressed(false)
42+ : m_leftButtonIsPressed(false)
44{
43{
45 m_touchDevice = new QTouchDevice;
44 m_touchDevice = new QTouchDevice;
46 m_touchDevice->setType(QTouchDevice::TouchScreen);
45 m_touchDevice->setType(QTouchDevice::TouchScreen);
47 QWindowSystemInterface::registerTouchDevice(m_touchDevice);
46 QWindowSystemInterface::registerTouchDevice(m_touchDevice);
48}
47}
49
48
50−void MouseTouchAdaptor::setTargetWindow(QWindow *window)
51−{
52− m_targetWindow = window;
53−}
54−
55bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
49bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
56 void * message, long * /*result*/)
50 void * message, long * /*result*/)
57{
51{
58− Q_ASSERT(m_targetWindow);
59−
60 if (eventType != "xcb_generic_event_t") {
52 if (eventType != "xcb_generic_event_t") {
61 // wrong backend.
53 // wrong backend.
62 qWarning("MouseTouchAdaptor: XCB backend not in use. Adaptor inoperative!");
54 qWarning("MouseTouchAdaptor: XCB backend not in use. Adaptor inoperative!");
@@ -91,7 +83,9 @@ bool MouseTouchAdaptor::handleButtonPress(xcb_button_press_event_t *pressEvent)
91
83
92 QPoint windowPos(pressEvent->event_x, pressEvent->event_y);
84 QPoint windowPos(pressEvent->event_x, pressEvent->event_y);
93
85
86+ QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(pressEvent->event));
87+
94− QTouchEventSequence touchEvent = QTest::touchEvent(m_targetWindow, m_touchDevice,
88+ QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
95 false /* autoCommit */);
89 false /* autoCommit */);
96 touchEvent.press(0 /* touchId */, windowPos);
90 touchEvent.press(0 /* touchId */, windowPos);
97 touchEvent.commit(false /* processEvents */);
91 touchEvent.commit(false /* processEvents */);
@@ -110,7 +104,9 @@ bool MouseTouchAdaptor::handleButtonRelease(xcb_button_release_event_t *releaseE
110
104
111 QPoint windowPos(releaseEvent->event_x, releaseEvent->event_y);
105 QPoint windowPos(releaseEvent->event_x, releaseEvent->event_y);
112
106
107+ QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(releaseEvent->event));
108+
113− QTouchEventSequence touchEvent = QTest::touchEvent(m_targetWindow, m_touchDevice,
109+ QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
114 false /* autoCommit */);
110 false /* autoCommit */);
115 touchEvent.release(0 /* touchId */, windowPos);
111 touchEvent.release(0 /* touchId */, windowPos);
116 touchEvent.commit(false /* processEvents */);
112 touchEvent.commit(false /* processEvents */);
@@ -127,10 +123,31 @@ bool MouseTouchAdaptor::handleMotionNotify(xcb_motion_notify_event_t *event)
127
123
128 QPoint windowPos(event->event_x, event->event_y);
124 QPoint windowPos(event->event_x, event->event_y);
129
125
126+ QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(event->event));
127+
130− QTouchEventSequence touchEvent = QTest::touchEvent(m_targetWindow, m_touchDevice,
128+ QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
131 false /* autoCommit */);
129 false /* autoCommit */);
132 touchEvent.move(0 /* touchId */, windowPos);
130 touchEvent.move(0 /* touchId */, windowPos);
133 touchEvent.commit(false /* processEvents */);
131 touchEvent.commit(false /* processEvents */);
134
132
135 return true;
133 return true;
136}
134}
135+
136+QWindow *MouseTouchAdaptor::findQWindowWithXWindowID(WId windowId)
137+{
138+ QWindowList windowList = QGuiApplication::topLevelWindows();
139+ QWindow *foundWindow = nullptr;
140+
141+ int i = 0;
142+ while (!foundWindow && i < windowList.count()) {
143+ QWindow *window = windowList[i];
144+ if (window->winId() == windowId) {
145+ foundWindow = window;
146+ } else {
147+ ++i;
148+ }
149+ }
150+
151+ Q_ASSERT(foundWindow);
152+ return foundWindow;
153+}
MouseTouchAdaptor.h+2−5
@@ -20,11 +20,11 @@
20#define MOUSE_TOUCH_ADAPTOR_H
20#define MOUSE_TOUCH_ADAPTOR_H
21
21
22#include <QtCore/QAbstractNativeEventFilter>
22#include <QtCore/QAbstractNativeEventFilter>
23+#include <QWindow>
23#include <xcb/xcb.h>
24#include <xcb/xcb.h>
24
25
25class QMouseEvent;
26class QMouseEvent;
26class QTouchDevice;
27class QTouchDevice;
27−class QWindow;
28
28
29// Transforms QMouseEvents into single-finger QTouchEvents.
29// Transforms QMouseEvents into single-finger QTouchEvents.
30class MouseTouchAdaptor : public QAbstractNativeEventFilter {
30class MouseTouchAdaptor : public QAbstractNativeEventFilter {
@@ -32,9 +32,6 @@ class MouseTouchAdaptor : public QAbstractNativeEventFilter {
32public:
32public:
33 MouseTouchAdaptor();
33 MouseTouchAdaptor();
34
34
35− // The window that will receive the generated QTouchEvents
36− void setTargetWindow(QWindow *window);
37−
38 // Filters mouse events and posts the equivalent QTouchEvents.
35 // Filters mouse events and posts the equivalent QTouchEvents.
39 virtual bool nativeEventFilter(const QByteArray & eventType, void *message, long *result);
36 virtual bool nativeEventFilter(const QByteArray & eventType, void *message, long *result);
40
37
@@ -43,8 +40,8 @@ private:
43 bool handleButtonPress(xcb_button_press_event_t *pressEvent);
40 bool handleButtonPress(xcb_button_press_event_t *pressEvent);
44 bool handleButtonRelease(xcb_button_release_event_t *releaseEvent);
41 bool handleButtonRelease(xcb_button_release_event_t *releaseEvent);
45 bool handleMotionNotify(xcb_motion_notify_event_t *event);
42 bool handleMotionNotify(xcb_motion_notify_event_t *event);
43+ QWindow *findQWindowWithXWindowID(WId windowId);
46
44
47− QWindow *m_targetWindow;
48 QTouchDevice *m_touchDevice;
45 QTouchDevice *m_touchDevice;
49 bool m_leftButtonIsPressed;
46 bool m_leftButtonIsPressed;
50};
47};
Panel/IndicatorItem.qml+2−3
@@ -26,13 +26,12 @@ Item {
26 property bool highlighted: false
26 property bool highlighted: false
27 property bool dimmed: false
27 property bool dimmed: false
28 property var indicatorProperties: undefined
28 property var indicatorProperties: undefined
29+ property bool indicatorVisible: loader.item ? loader.item.enabled : false
29
30
30 opacity: dimmed ? 0.4 : 1
31 opacity: dimmed ? 0.4 : 1
31 Behavior on opacity { StandardAnimation {} }
32 Behavior on opacity { StandardAnimation {} }
32
33
33− // only visible when non-empty
34− visible: loader.item != undefined && loader.status == Loader.Ready ? loader.item.enabled : false
35− width: visible ? loader.item.width : 0
34+ width: loader.item ? loader.item.width : 0
36
35
37 Loader {
36 Loader {
38 id: loader
37 id: loader
Panel/IndicatorRow.qml+19−1
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import Unity.Indicators 0.1 as Indicators
19import "../Components"
20import "../Components"
20
21
21Item {
22Item {
@@ -25,6 +26,7 @@ Item {
25 readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1
26 readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1
26 property alias row: row
27 property alias row: row
27 property QtObject indicatorsModel: null
28 property QtObject indicatorsModel: null
29+ property var visibleIndicators: defined
28
30
29 width: units.gu(40)
31 width: units.gu(40)
30 height: units.gu(3)
32 height: units.gu(3)
@@ -55,7 +57,8 @@ Item {
55 Item {
57 Item {
56 id: itemWrapper
58 id: itemWrapper
57 height: indicatorRow.height
59 height: indicatorRow.height
58− width: childrenRect.width
60+ width: indicatorItem.width
61+ visible: indicatorItem.indicatorVisible
59
62
60 property int ownIndex: index
63 property int ownIndex: index
61 property alias highlighted: indicatorItem.highlighted
64 property alias highlighted: indicatorItem.highlighted
@@ -70,6 +73,21 @@ Item {
70
73
71 widgetSource: model.widgetSource
74 widgetSource: model.widgetSource
72 indicatorProperties : model.indicatorProperties
75 indicatorProperties : model.indicatorProperties
76+
77+ Component.onCompleted: {
78+ if (visibleIndicators == undefined) {
79+ visibleIndicators = {}
80+ }
81+ indicatorRow.visibleIndicators[model.identifier] = indicatorVisible;
82+ indicatorRow.visibleIndicatorsChanged();
83+ }
84+ onIndicatorVisibleChanged: {
85+ if (visibleIndicators == undefined) {
86+ visibleIndicators = {}
87+ }
88+ indicatorRow.visibleIndicators[model.identifier] = indicatorVisible;
89+ indicatorRow.visibleIndicatorsChanged();
90+ }
73 }
91 }
74
92
75 opacity: {
93 opacity: {
Panel/Indicators.qml+17−2
@@ -180,11 +180,19 @@ Showable {
180 clip: !indicators.fullyOpened
180 clip: !indicators.fullyOpened
181 activeHeader: indicators.state == "hint" || indicators.state == "reveal"
181 activeHeader: indicators.state == "hint" || indicators.state == "reveal"
182 enabled: contentEnabled
182 enabled: contentEnabled
183+ visibleIndicators: indicatorRow.visibleIndicators
183
184
184 Connections {
185 Connections {
186+ property bool enableIndexChangeSignal: true
187+
185− target: indicatorRow
188+ target: enableIndexChangeSignal ? indicatorRow : null
186 onCurrentItemIndexChanged: {
189 onCurrentItemIndexChanged: {
190+ var oldActive = enableIndexChangeSignal;
191+ enableIndexChangeSignal = false;
192+
187 menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex);
193 menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex);
194+
195+ enableIndexChangeSignal = oldActive;
188 }
196 }
189 }
197 }
190
198
@@ -256,9 +264,16 @@ Showable {
256 state: indicators.state
264 state: indicators.state
257
265
258 Connections {
266 Connections {
267+ property bool enableIndexChangeSignal: true
268+
259− target: menuContent
269+ target: enableIndexChangeSignal ? menuContent : null
260 onCurrentMenuIndexChanged: {
270 onCurrentMenuIndexChanged: {
271+ var oldActive = enableIndexChangeSignal;
272+ enableIndexChangeSignal = false;
273+
261 indicatorRow.setCurrentItem(menuContent.currentMenuIndex);
274 indicatorRow.setCurrentItem(menuContent.currentMenuIndex);
275+
276+ enableIndexChangeSignal = oldActive;
262 }
277 }
263 }
278 }
264 }
279 }
Panel/Indicators/DatetimeIndicatorWidget.qml+4−2
@@ -25,6 +25,7 @@ Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
26
26
27 width: timeLabel.width + units.gu(1)
27 width: timeLabel.width + units.gu(1)
28+ rootMenuType: "com.canonical.indicator.root.time"
28
29
29 property alias label: timeLabel.text
30 property alias label: timeLabel.text
30
31
@@ -52,14 +53,15 @@ Indicators.IndicatorWidget {
52
53
53 onActionStateChanged: {
54 onActionStateChanged: {
54 if (action == undefined || !action.valid) {
55 if (action == undefined || !action.valid) {
56+ enabled = false;
55 return;
57 return;
56 }
58 }
57
59
58 if (action.state == undefined) {
60 if (action.state == undefined) {
59− visible = false;
61+ enabled = false;
60 return;
62 return;
61 }
63 }
62
64
63− indicatorWidget.visible = action.state[3];
65+ enabled = action.state[3];
64 }
66 }
65}
67}
Panel/Indicators/DefaultIndicatorWidget.qml+3−6
@@ -24,7 +24,8 @@ import Unity.IndicatorsLegacy 0.1 as Indicators
24Indicators.IndicatorWidget {
24Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
26
26
27+ enabled:true
27− width: itemRow.width + units.gu(1)
28+ width: itemRow.width + units.gu(0.5)
28
29
29 property alias label: itemLabel.text
30 property alias label: itemLabel.text
30 property alias iconSource: itemImage.source
31 property alias iconSource: itemImage.source
@@ -67,14 +68,10 @@ Indicators.IndicatorWidget {
67 }
68 }
68
69
69 if (action.state == undefined) {
70 if (action.state == undefined) {
70− label = "";
71− iconSource = "";
72− enabled = false;
73 return;
71 return;
74 }
72 }
75
73
76 label = action.state[Indicators.ActionState.Label];
74 label = action.state[Indicators.ActionState.Label];
77− iconSource = "image://gicon/" + action.state[Indicators.ActionState.IconSource];
75+ iconSource = "image://theme/" + action.state[Indicators.ActionState.IconSource];
78− enabled = action.state[Indicators.ActionState.Visible];
79 }
76 }
80}
77}
Panel/Indicators/DefaultIndicatorWidget2.qml+43−16
@@ -24,10 +24,11 @@ import Unity.Indicators 0.1 as Indicators
24Indicators.IndicatorWidget {
24Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
26
26
27− width: itemRow.width + units.gu(1)
27+ width: itemRow.width + units.gu(0.5)
28
28
29− property alias label: itemLabel.text
29+ property alias leftLabel: itemLeftLabel.text
30− property alias iconSource: itemImage.source
30+ property alias rightLabel: itemRightLabel.text
31+ property var icons: undefined
31
32
32 Row {
33 Row {
33 id: itemRow
34 id: itemRow
@@ -39,19 +40,43 @@ Indicators.IndicatorWidget {
39 }
40 }
40 spacing: units.gu(0.5)
41 spacing: units.gu(0.5)
41
42
42− // FIXME : Should us Ubuntu.Icon . results in low res images
43+ Label {
43− Image {
44+ id: itemLeftLabel
44− id: itemImage
45− objectName: "itemImage"
45+ objectName: "leftLabel"
46− visible: source != ""
46+ color: Theme.palette.selected.backgroundText
47− height: indicatorWidget.iconSize
47+ opacity: 0.8
48− width: indicatorWidget.iconSize
48+ font.family: "Ubuntu"
49+ fontSize: "medium"
49 anchors.verticalCenter: parent.verticalCenter
50 anchors.verticalCenter: parent.verticalCenter
51+ visible: text != ""
52+ }
53+
54+ Row {
55+ width: childrenRect.width
56+ anchors {
57+ top: parent.top
58+ bottom: parent.bottom
59+ }
60+ spacing: units.gu(0.5)
61+
62+ Repeater {
63+ model: indicatorWidget.icons
64+
65+ Image {
66+ id: itemImage
67+ objectName: "itemImage"
68+ visible: source != ""
69+ source: modelData
70+ height: indicatorWidget.iconSize
71+ width: indicatorWidget.iconSize
72+ anchors.verticalCenter: parent.verticalCenter
73+ }
74+ }
50 }
75 }
51
76
52 Label {
77 Label {
53− id: itemLabel
78+ id: itemRightLabel
54− objectName: "itemLabel"
79+ objectName: "rightLabel"
55 color: Theme.palette.selected.backgroundText
80 color: Theme.palette.selected.backgroundText
56 opacity: 0.8
81 opacity: 0.8
57 font.family: "Ubuntu"
82 font.family: "Ubuntu"
@@ -63,14 +88,16 @@ Indicators.IndicatorWidget {
63
88
64 onActionStateChanged: {
89 onActionStateChanged: {
65 if (actionState == undefined) {
90 if (actionState == undefined) {
66− label = "";
91+ leftLabel = "";
67− iconSource = "";
92+ rightLabel = "";
93+ icons = undefined;
68 enabled = false;
94 enabled = false;
69 return;
95 return;
70 }
96 }
71
97
72− label = actionState.label;
98+ leftLabel = actionState.leftLabel ? actionState.leftLabel : "";
99+ rightLabel = actionState.rightLabel ? actionState.rightLabel : "";
73− iconSource = actionState.icon;
100+ icons = actionState.icons;
74 enabled = actionState.visible;
101 enabled = actionState.visible;
75 }
102 }
76}
103}
Panel/Indicators/MessagingIndicatorWidget.qml+1−1
@@ -21,5 +21,5 @@ import QtQuick 2.0
21
21
22DefaultIndicatorWidget {
22DefaultIndicatorWidget {
23 // default the icon so we have something to show when we don't have the bus.
23 // default the icon so we have something to show when we don't have the bus.
24− iconSource: "image://gicon/indicator-messages"
24+ iconSource: "image://theme/indicator-messages"
25}
25}
Panel/Indicators/NetworkIndicatorWidget.qml+10−7
@@ -24,7 +24,7 @@ import Unity.IndicatorsLegacy 0.1 as Indicators
24Indicators.IndicatorWidget {
24Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
26
26
27− width: networkIcon.width + units.gu(1)
27+ width: networkIcon.width + units.gu(0.5)
28
28
29 property int signalStrength: 0
29 property int signalStrength: 0
30 property int connectionState: Indicators.NetworkConnection.Initial
30 property int connectionState: Indicators.NetworkConnection.Initial
@@ -43,14 +43,17 @@ Indicators.IndicatorWidget {
43
43
44 onActionStateChanged: {
44 onActionStateChanged: {
45 if (action == undefined || !action.valid) {
45 if (action == undefined || !action.valid) {
46+ enabled = false;
46 return;
47 return;
47 }
48 }
48
49
49 if (action.state == undefined) {
50 if (action.state == undefined) {
51+ enabled = false;
50 connectionState = 0;
52 connectionState = 0;
51 return;
53 return;
52 }
54 }
53
55
56+ enabled = true;
54 connectionState = action.state[Indicators.NetworkActionState.Connection];
57 connectionState = action.state[Indicators.NetworkActionState.Connection];
55 if (connectionState == Indicators.NetworkConnection.Activated) {
58 if (connectionState == Indicators.NetworkConnection.Activated) {
56 signalStrength = action.state[Indicators.NetworkActionState.SignalStrength];
59 signalStrength = action.state[Indicators.NetworkActionState.SignalStrength];
@@ -94,16 +97,16 @@ Indicators.IndicatorWidget {
94 function get_icon_for_signal(con_state, value) {
97 function get_icon_for_signal(con_state, value) {
95 if (con_state >= Indicators.NetworkConnection.Activating && con_state <= Indicators.NetworkConnection.Deactivating) {
98 if (con_state >= Indicators.NetworkConnection.Activating && con_state <= Indicators.NetworkConnection.Deactivating) {
96 if (value == 0) {
99 if (value == 0) {
97− return "image://gicon/nm-signal-00";
100+ return "image://theme/nm-signal-00";
98 } else if (value <= 25) {
101 } else if (value <= 25) {
99− return "image://gicon/nm-signal-25";
102+ return "image://theme/nm-signal-25";
100 } else if (value <= 50) {
103 } else if (value <= 50) {
101− return "image://gicon/nm-signal-50";
104+ return "image://theme/nm-signal-50";
102 } else if (value <= 75) {
105 } else if (value <= 75) {
103− return "image://gicon/nm-signal-75";
106+ return "image://theme/nm-signal-75";
104 }
107 }
105− return "image://gicon/nm-signal-100";
108+ return "image://theme/nm-signal-100";
106 }
109 }
107− return "image://gicon/wifi-none";
110+ return "image://theme/wifi-none";
108 }
111 }
109}
112}
Panel/Indicators/SoundIndicatorWidget.qml+2−1
@@ -21,5 +21,6 @@ import QtQuick 2.0
21
21
22DefaultIndicatorWidget {
22DefaultIndicatorWidget {
23 // default the icon so we have something to show when we don't have the bus.
23 // default the icon so we have something to show when we don't have the bus.
24− iconSource: "image://gicon/audio-volume-high"
24+ iconSource: "image://theme/audio-volume-high"
25+ enabled: true
25}
26}
Panel/MenuContent.qml+25−7
@@ -17,6 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import Unity.Indicators 0.1 as Indicators
19import Unity.Indicators 0.1 as Indicators
20+import Utils 0.1
20import "../Components"
21import "../Components"
21
22
22// FIXME : We dont want to use MainView.
23// FIXME : We dont want to use MainView.
@@ -27,20 +28,23 @@ MainView {
27
28
28 property QtObject indicatorsModel: null
29 property QtObject indicatorsModel: null
29 property bool __contentActive: false
30 property bool __contentActive: false
30− readonly property int currentMenuIndex : tabs.selectedTabIndex
31+ readonly property int currentMenuIndex : filteredIndicators.mapToSource(tabs.selectedTabIndex)
31 backgroundColor: "#221e1c" // FIXME not in palette yet
32 backgroundColor: "#221e1c" // FIXME not in palette yet
32 property int contentReleaseInterval: 20000
33 property int contentReleaseInterval: 20000
33 property bool activeHeader: false
34 property bool activeHeader: false
35+ property alias visibleIndicators: visibleIndicatorsModel.visible
34
36
35 width: units.gu(40)
37 width: units.gu(40)
36 height: units.gu(42)
38 height: units.gu(42)
37
39
38 function setCurrentMenuIndex(index) {
40 function setCurrentMenuIndex(index) {
39− if (currentMenuIndex !== index) {
41+ var filteredIndex = filteredIndicators.mapFromSource(index)
40− if (currentMenuIndex == -1) {
42+
43+ if (tabs.selectedTabIndex !== filteredIndex) {
44+ if (tabs.selectedTabIndex == -1) {
41 tabs.tabBar.animate = false;
45 tabs.tabBar.animate = false;
42 }
46 }
43− tabs.selectedTabIndex = index;
47+ tabs.selectedTabIndex = filteredIndex;
44 tabs.tabBar.animate = true;
48 tabs.tabBar.animate = true;
45 }
49 }
46 }
50 }
@@ -61,6 +65,20 @@ MainView {
61 tabs.tabBar.alwaysSelectionMode = activeHeader;
65 tabs.tabBar.alwaysSelectionMode = activeHeader;
62 }
66 }
63
67
68+ SortFilterProxyModel {
69+ id: filteredIndicators
70+ model: visibleIndicatorsModel
71+ dynamicSortFilter: true
72+
73+ filterRole: Indicators.IndicatorsModelRole.IsVisible
74+ filterRegExp: RegExp("^true$")
75+ }
76+
77+ Indicators.VisibleIndicatorsModel {
78+ id: visibleIndicatorsModel
79+ model: indicatorsModel
80+ }
81+
64 Tabs {
82 Tabs {
65 id: tabs
83 id: tabs
66 objectName: "tabs"
84 objectName: "tabs"
@@ -69,8 +87,8 @@ MainView {
69
87
70 Repeater {
88 Repeater {
71 id: repeater
89 id: repeater
72− model: indicatorsModel
90+ model: filteredIndicators
73− objectName: "menus"
91+ objectName: "tabsRepeater"
74
92
75 // FIXME: This is needed because tabs dont handle repeaters well.
93 // FIXME: This is needed because tabs dont handle repeaters well.
76 // Due to the child ordering happening after child insertion.
94 // Due to the child ordering happening after child insertion.
@@ -114,7 +132,7 @@ MainView {
114 item[pName] = indicatorProperties[pName]
132 item[pName] = indicatorProperties[pName]
115 }
133 }
116 }
134 }
117− if (contentActive && menus.visible) {
135+ if (contentActive && tabs.visible) {
118 item.start()
136 item.start()
119 }
137 }
120 }
138 }
Shell.qml+52−27
@@ -15,13 +15,15 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import AccountsService 0.1
18import GSettings 1.0
19import GSettings 1.0
19−import Ubuntu.Application 0.1
20+import Unity.Application 0.1
20import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
21import Ubuntu.Gestures 0.1
22import Ubuntu.Gestures 0.1
22import Unity.Launcher 0.1
23import Unity.Launcher 0.1
23import LightDM 0.1 as LightDM
24import LightDM 0.1 as LightDM
24import Powerd 0.1
25import Powerd 0.1
26+import SessionBroadcast 0.1
25import "Dash"
27import "Dash"
26import "Greeter"
28import "Greeter"
27import "Launcher"
29import "Launcher"
@@ -64,6 +66,12 @@ FocusScope {
64
66
65 property var applicationManager: ApplicationManagerWrapper {}
67 property var applicationManager: ApplicationManagerWrapper {}
66
68
69+ Binding {
70+ target: LauncherModel
71+ property: "applicationManager"
72+ value: ApplicationManager
73+ }
74+
67 Component.onCompleted: {
75 Component.onCompleted: {
68 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
76 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
69
77
@@ -74,6 +82,11 @@ FocusScope {
74 applicationManager.unfocusCurrentApplication();
82 applicationManager.unfocusCurrentApplication();
75 }
83 }
76
84
85+ readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
86+ || !!applicationManager.sideStageFocusedApplication
87+ // Used for autopilot testing.
88+ readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId
89+
77 readonly property bool fullscreenMode: {
90 readonly property bool fullscreenMode: {
78 if (greeter.shown || lockscreen.shown) {
91 if (greeter.shown || lockscreen.shown) {
79 return false;
92 return false;
@@ -147,6 +160,10 @@ FocusScope {
147 }
160 }
148 }
161 }
149
162
163+ OSKController {
164+ anchors.fill: parent // as needs to know the geometry of the shell
165+ }
166+
150 VolumeControl {
167 VolumeControl {
151 id: volumeControl
168 id: volumeControl
152 }
169 }
@@ -245,7 +262,7 @@ FocusScope {
245 property bool fullyShown: shown && x == 0 && parent.x == 0
262 property bool fullyShown: shown && x == 0 && parent.x == 0
246 property bool fullyHidden: !shown && x == width
263 property bool fullyHidden: !shown && x == width
247 available: !greeter.shown
264 available: !greeter.shown
248− hides: [launcher, panel.indicators]
265+ hides: [panel.indicators]
249 shown: false
266 shown: false
250 opacity: 1.0
267 opacity: 1.0
251 showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }
268 showAnimation: StandardAnimation { property: "x"; duration: 350; to: 0; easing.type: Easing.OutCubic }
@@ -446,6 +463,8 @@ FocusScope {
446 hides: [launcher, panel.indicators, hud]
463 hides: [launcher, panel.indicators, hud]
447 shown: true
464 shown: true
448
465
466+ defaultBackground: shell.background
467+
449 y: panel.panelHeight
468 y: panel.panelHeight
450 width: parent.width
469 width: parent.width
451 height: parent.height - panel.panelHeight
470 height: parent.height - panel.panelHeight
@@ -469,6 +488,7 @@ FocusScope {
469 onSelected: {
488 onSelected: {
470 // Update launcher items for new user
489 // Update launcher items for new user
471 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
490 var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
491+ AccountsService.user = user;
472 LauncherModel.setUser(user);
492 LauncherModel.setUser(user);
473 }
493 }
474
494
@@ -481,7 +501,8 @@ FocusScope {
481
501
482 InputFilterArea {
502 InputFilterArea {
483 anchors.fill: parent
503 anchors.fill: parent
484− blockInput: greeter.shown || lockscreen.shown
504+ blockInput: !applicationFocused || greeter.shown || lockscreen.shown || launcher.shown
505+ || panel.indicators.shown || hud.shown
485 }
506 }
486
507
487 Connections {
508 Connections {
@@ -493,10 +514,8 @@ FocusScope {
493
514
494 function setFocused(focused) {
515 function setFocused(focused) {
495 if (!focused) {
516 if (!focused) {
496− // FIXME: *FocusedApplication are not updated when unfocused, hence the need to check whether
497− // the stage was actually shown
498− if (mainStage.fullyShown) powerConnection.previousMainApp = applicationManager.mainStageFocusedApplication;
517+ powerConnection.previousMainApp = applicationManager.mainStageFocusedApplication;
499− if (sideStage.fullyShown) powerConnection.previousSideApp = applicationManager.sideStageFocusedApplication;
518+ powerConnection.previousSideApp = applicationManager.sideStageFocusedApplication;
500 applicationManager.unfocusCurrentApplication();
519 applicationManager.unfocusCurrentApplication();
501 } else {
520 } else {
502 if (powerConnection.previousMainApp) {
521 if (powerConnection.previousMainApp) {
@@ -529,6 +548,17 @@ FocusScope {
529 }
548 }
530 }
549 }
531
550
551+ function showHome() {
552+ greeter.hide()
553+ // Animate if moving between application and dash
554+ if (!stages.shown) {
555+ dash.setCurrentScope("home.scope", true, false)
556+ } else {
557+ dash.setCurrentScope("home.scope", false, false)
558+ }
559+ stages.hide()
560+ }
561+
532 Item {
562 Item {
533 id: overlay
563 id: overlay
534
564
@@ -547,8 +577,13 @@ FocusScope {
547 searchVisible: !greeter.shown && !lockscreen.shown
577 searchVisible: !greeter.shown && !lockscreen.shown
548
578
549 InputFilterArea {
579 InputFilterArea {
550− anchors.fill: parent
580+ anchors {
581+ top: parent.top
582+ left: parent.left
583+ right: parent.right
584+ }
585+ height: (panel.fullscreenMode) ? shell.edgeSize : panel.panelHeight
551− blockInput: panel.indicators.shown
586+ blockInput: true
552 }
587 }
553 }
588 }
554
589
@@ -568,11 +603,6 @@ FocusScope {
568 onMainStageFocusedApplicationChanged: hud.hide()
603 onMainStageFocusedApplicationChanged: hud.hide()
569 onSideStageFocusedApplicationChanged: hud.hide()
604 onSideStageFocusedApplicationChanged: hud.hide()
570 }
605 }
571−
572− InputFilterArea {
573− anchors.fill: parent
574− blockInput: hud.shown
575− }
576 }
606 }
577
607
578 Revealer {
608 Revealer {
@@ -595,8 +625,7 @@ FocusScope {
595 theHud: hud
625 theHud: hud
596 anchors.fill: parent
626 anchors.fill: parent
597 enabled: !panel.indicators.shown
627 enabled: !panel.indicators.shown
598− applicationIsOnForeground: applicationManager.mainStageFocusedApplication
628+ applicationIsOnForeground: applicationFocused
599− || applicationManager.sideStageFocusedApplication
600 }
629 }
601
630
602 InputFilterArea {
631 InputFilterArea {
@@ -617,16 +646,7 @@ FocusScope {
617 width: parent.width
646 width: parent.width
618 dragAreaWidth: shell.edgeSize
647 dragAreaWidth: shell.edgeSize
619 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
648 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
620− onDashItemSelected: {
649+ onDashItemSelected: showHome()
621− greeter.hide()
622− // Animate if moving between application and dash
623− if (!stages.shown) {
624− dash.setCurrentScope("home.scope", true, false)
625− } else {
626− dash.setCurrentScope("home.scope", false, false)
627− }
628− stages.hide();
629− }
630 onDash: {
650 onDash: {
631 if (stages.shown) {
651 if (stages.shown) {
632 dash.setCurrentScope("applications.scope", true, false)
652 dash.setCurrentScope("applications.scope", true, false)
@@ -636,7 +656,7 @@ FocusScope {
636 }
656 }
637 onLauncherApplicationSelected:{
657 onLauncherApplicationSelected:{
638 greeter.hide()
658 greeter.hide()
639− shell.activateApplication(desktopFile)
659+ shell.activateApplication(appId)
640 }
660 }
641 onShownChanged: {
661 onShownChanged: {
642 if (shown) {
662 if (shown) {
@@ -734,4 +754,9 @@ FocusScope {
734 indicators: panel.indicators
754 indicators: panel.indicators
735 underlay: underlay
755 underlay: underlay
736 }
756 }
757+
758+ Connections {
759+ target: SessionBroadcast
760+ onShowHome: showHome()
761+ }
737}
762}
SideStage/SideStage.qml+1−1
@@ -15,7 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18−import Ubuntu.Application 0.1
18+import Unity.Application 0.1
19import Ubuntu.Components 0.1
19import Ubuntu.Components 0.1
20import "../Components"
20import "../Components"
21
21
build+9
@@ -57,6 +57,15 @@ mk_build_deps() {
57 [ ! -f unity8-build-deps*deb -o $CODE_DIR/debian/control -nt unity8-build-deps*deb ] && mk-build-deps --install --root-cmd sudo $CODE_DIR/debian/control
57 [ ! -f unity8-build-deps*deb -o $CODE_DIR/debian/control -nt unity8-build-deps*deb ] && mk-build-deps --install --root-cmd sudo $CODE_DIR/debian/control
58}
58}
59
59
60+if [ -f "/usr/bin/ccache" ] ; then
61+ if [ "x$CC" = "x" ]; then
62+ export CC='ccache gcc'
63+ fi
64+ if [ "x$CXX" = "x" ] ; then
65+ export CXX='ccache g++'
66+ fi
67+fi
68+
60if [ -f "/usr/bin/ninja" ] ; then
69if [ -f "/usr/bin/ninja" ] ; then
61 GENERATOR="-G Ninja"
70 GENERATOR="-G Ninja"
62 # Ninja does not need -j, it parallelizes automatically.
71 # Ninja does not need -j, it parallelizes automatically.
debian/changelog+207
@@ -1,3 +1,210 @@
1+unity8 (7.81.3+13.10.20130919.3-0ubuntu1) saucy; urgency=low
2+
3+ [ Michal Hruby ]
4+ * Add support for music grid renderer to GenericScopeView.
5+
6+ [ Nick Dedekind ]
7+ * Fixed the removal of messaging widget due to incompatible action
8+ state. (LP: #1225017)
9+
10+ [ Christopher Lee ]
11+ * Addition of initial autopilot tests for the application lifecycle.
12+
13+ [ Lars Uebernickel ]
14+ * VolumeControl: use action of the new indicator indicator-sound
15+ recently gained an action to increase and decrease the volume. This
16+ patch makes use of that to get rid of a bus round trip (to get the
17+ current volume) and a race (when the volume gets set between
18+ fetching the current volume and setting the new volume). (LP:
19+ #1219057)
20+
21+ [ Michael Terry ]
22+ * When AccountsService.backgroundFile is unset/invalid, have the
23+ greeter fall back to whatever the shell background is.
24+ * Add a tiny SessionBroadcast plugin that listens to unity-greeter-
25+ session-broadcast for the ShowHome signal.
26+
27+ [ Daniel d'Andrada ]
28+ * Remove obsolete, unused graphics.
29+ * Make MouseTouchAdaptor work with multiple QWindows.
30+
31+ [ Michael Zanetti ]
32+ * change how icons are searched a) try to find it the Icon as is b)
33+ prepend with Path if a Path variable is given c) fall back to the
34+ image://theme/ with just the icon name . (LP: #1225186)
35+ * fix an issue with removing a running app from the launcher and
36+ always store pinning to the config.
37+ * collapse any open preview when programmatically switching current
38+ dash index. (LP: #1221137)
39+
40+ [ Ubuntu daily release ]
41+ * Automatic snapshot from revision 333
42+
43+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 19 Sep 2013 15:15:07 +0000
44+
45+unity8 (7.81.3+13.10.20130916-0ubuntu1) saucy; urgency=low
46+
47+ [ Michael Zanetti ]
48+ * allow left edge gesture to minimize apps even when launcher is
49+ already visible.
50+ * Don't hide the launcher on changes in the stages.
51+ * ssh is now installed per default, but it's set to manual in the
52+ ssh.override.
53+
54+ [ Ubuntu daily release ]
55+ * Automatic snapshot from revision 320
56+
57+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 16 Sep 2013 11:49:28 +0000
58+
59+unity8 (7.81.3+13.10.20130912-0ubuntu1) saucy; urgency=low
60+
61+ [ Michael Zanetti ]
62+ * add support for finding icons from click package apps in the
63+ launcher.
64+ * update to latest launcher API for better integration with the
65+ AppManager.
66+
67+ [ Ricardo Mendoza ]
68+ * Fixes problems related to image 20130912.0, amongst: * Fix autopilot
69+ tests by preventing blocking of input during HUD button animations,
70+ only when fully visible * Fix loading of unity-mir library, major
71+ version wasn't specified so unless the dev package was there it
72+ would fail.
73+
74+ [ Ubuntu daily release ]
75+ * Automatic snapshot from revision 316
76+
77+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 12 Sep 2013 13:47:59 +0000
78+
79+unity8 (7.81.3+13.10.20130911.1-0ubuntu1) saucy; urgency=low
80+
81+ [ Michael Terry ]
82+ * Switch from deprecated image://gicon/ to new image://theme/.
83+
84+ [ Gerry Boland ]
85+ * Add OSKController so shell can control OSK correctly on Mir.
86+ * Remove InputFilterArea for bottom edge swipes, as applications also
87+ listen for such swipes for Toolbar reveal.
88+
89+ [ Ricardo Mendoza ]
90+ * Select the backend to use dynamically on runtime according to the
91+ QPA selected by the system.
92+
93+ [ Ubuntu daily release ]
94+ * Automatic snapshot from revision 311
95+
96+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 11 Sep 2013 16:22:55 +0000
97+
98+unity8 (7.81.3+13.10.20130911-0ubuntu1) saucy; urgency=low
99+
100+ [ Michal Hruby ]
101+ * Hide all gicon strings from the shell and use the image://theme icon
102+ provider that was recently added to the SDK.
103+
104+ [ Gerry Boland ]
105+ * Convert to new ApplicationManager API.
106+
107+ [ Nick Dedekind ]
108+ * Updated access point design as per spec.
109+ * Indicator visibility based on connection with backend service.
110+
111+ [ Albert Astals ]
112+ * Dash: Make assignments bindings This way if the model changes the
113+ item value also changes.
114+
115+ [ Michael Zanetti ]
116+ * adds support for highlighting the currently focused application in
117+ the launcher, adds tests.
118+ * include ~/.local/share/applications in launcher's .desktop file
119+ search path.
120+ * shrink size of area for revealing the HUD button and make it
121+ disappear on release again. fixes 1219035. (LP: #1219035)
122+ * revert revision 304 as it makes the Shell crash.
123+
124+ [ Michael Terry ]
125+ * Allow testers to set custom password or pin in demo mode, rather
126+ than hardcoding them.
127+ * Have the greeter use AccountsService to determine its background.
128+ (LP: #1218402)
129+ * Listen to changes in the "show edge demo" AccountsService setting.
130+
131+ [ Daniel d'Andrada ]
132+ * Update fake/mock plugins so that "./run --fake" works well again -
133+ You can now see the thumbnails of the fake running applications once
134+ again. - You no longer get hundreds of warnings due to icons not
135+ found.
136+ * Reset apps scope when returning from app to dash (LP #1193419) If an
137+ app is on foreground and you perform a long left-edge swipe to
138+ minimize it, and therefore return to the dash, the dash should be in
139+ the Applications scope and showing the running/recents applications.
140+ (LP: #1193419)
141+
142+ [ Ubuntu daily release ]
143+ * Automatic snapshot from revision 306
144+
145+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 11 Sep 2013 00:54:31 +0000
146+
147+unity8 (7.81.3+13.10.20130905.2-0ubuntu1) saucy; urgency=low
148+
149+ [ Michael Zanetti ]
150+ * Integrate Launcher with AppManager.
151+
152+ [ Nick Dedekind ]
153+ * Removed FIXME from slider int->double conversion.
154+
155+ [ Ubuntu daily release ]
156+ * Automatic snapshot from revision 291
157+
158+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 05 Sep 2013 10:48:02 +0000
159+
160+unity8 (7.81.3+13.10.20130904.1-0ubuntu1) saucy; urgency=low
161+
162+ [ Nicolas d'Offay ]
163+ * Switched infographic background at design's request.
164+
165+ [ Ubuntu daily release ]
166+ * Automatic snapshot from revision 287
167+
168+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 04 Sep 2013 07:34:57 +0000
169+
170+unity8 (7.81.3+13.10.20130904-0ubuntu1) saucy; urgency=low
171+
172+ [ mhall119 ]
173+ * Add a little bit of text to the last step of the tour telling the
174+ user how to end it and get to their phone.
175+
176+ [ Jussi Pakkanen ]
177+ * Use CCache if it is installed.
178+
179+ [ Nick Dedekind ]
180+ * Multiple icon/label support for indicators.
181+
182+ [ Albert Astals ]
183+ * Remove unneeded role.
184+
185+ [ Lars Uebernickel ]
186+ * Fall back to "ubuntu-mobile" icon theme if $UBUNTU_ICON_THEME is
187+ unset.
188+
189+ [ Michael Zanetti ]
190+ * Use MouseAreas in DashBar to enable clicking again.
191+ * load launcher default config from existing dconf key.
192+
193+ [ Ubuntu daily release ]
194+ * Automatic snapshot from revision 285
195+
196+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 04 Sep 2013 03:02:57 +0000
197+
198+unity8 (7.81.3+13.10.20130903.1-0ubuntu1) saucy; urgency=low
199+
200+ [ Michael Zanetti ]
201+ * workaround quicklist text color.
202+
203+ [ Ubuntu daily release ]
204+ * Automatic snapshot from revision 277
205+
206+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 03 Sep 2013 06:09:36 +0000
207+
1unity8 (7.81.3+13.10.20130830-0ubuntu1) saucy; urgency=low
208unity8 (7.81.3+13.10.20130830-0ubuntu1) saucy; urgency=low
2
209
3 [ Pawel Stolowski ]
210 [ Pawel Stolowski ]
debian/control+10−7
@@ -9,6 +9,7 @@ Build-Depends: cmake,
9 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
9 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
10 libgl1-mesa-dri,
10 libgl1-mesa-dri,
11 libgles2-mesa-dev[armhf],
11 libgles2-mesa-dev[armhf],
12+ libgsettings-qt-dev,
12 libhud-client2-dev,
13 libhud-client2-dev,
13 libjson-perl,
14 libjson-perl,
14 libnih-dbus-dev,
15 libnih-dbus-dev,
@@ -22,6 +23,7 @@ Build-Depends: cmake,
22 libunity-api-dev (>= 7.80.3),
23 libunity-api-dev (>= 7.80.3),
23 libunity-core-6.0-dev (<< 7.80),
24 libunity-core-6.0-dev (<< 7.80),
24 libunity-dev (>= 7.0.12),
25 libunity-dev (>= 7.0.12),
26+ libunity-mir-dev,
25 libunity-scopes-json-def-phone,
27 libunity-scopes-json-def-phone,
26 libupstart-dev,
28 libupstart-dev,
27 libusermetricsoutput1-dev,
29 libusermetricsoutput1-dev,
@@ -53,7 +55,7 @@ Vcs-Browser: https://bazaar.launchpad.net/~unity-team/unity8/trunk
53Package: indicators-client
55Package: indicators-client
54Architecture: amd64 armhf i386
56Architecture: amd64 armhf i386
55Depends: qmenumodel-qml (>= 0.2.7),
57Depends: qmenumodel-qml (>= 0.2.7),
56− qtdeclarative5-ubuntu-ui-toolkit-plugin | qt-components-ubuntu,
58+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.46+13.10.20130829.1-0ubuntu1),
57 unity8 (= ${binary:Version}),
59 unity8 (= ${binary:Version}),
58 ${misc:Depends},
60 ${misc:Depends},
59 ${shlibs:Depends},
61 ${shlibs:Depends},
@@ -64,13 +66,14 @@ Package: unity8
64Architecture: any
66Architecture: any
65Provides: indicator-renderer,
67Provides: indicator-renderer,
66Depends: gsettings-desktop-schemas,
68Depends: gsettings-desktop-schemas,
69+ libunity-mir1,
67 qmenumodel-qml (>= 0.2.7),
70 qmenumodel-qml (>= 0.2.7),
68 qtdeclarative5-dee-plugin,
71 qtdeclarative5-dee-plugin,
69 qtdeclarative5-gsettings1.0,
72 qtdeclarative5-gsettings1.0,
70− qtdeclarative5-ubuntu-ui-toolkit-plugin,
73+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.46+13.10.20130829.1-0ubuntu1),
71 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
74 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
72 qtdeclarative5-xmllistmodel-plugin,
75 qtdeclarative5-xmllistmodel-plugin,
73− unity-launcher-impl-2,
76+ unity-launcher-impl-3,
74 unity-notifications-impl-1,
77 unity-notifications-impl-1,
75 unity8-fake-env | qtubuntu-shell,
78 unity8-fake-env | qtubuntu-shell,
76 unity8-private (= ${binary:Version}),
79 unity8-private (= ${binary:Version}),
@@ -86,18 +89,18 @@ Description: Unity 8 shell
86
89
87Package: unity8-autopilot
90Package: unity8-autopilot
88Architecture: all
91Architecture: all
92+Depends: gir1.2-glib-2.0,
89−Depends: indicators-client (>= ${source:Version}),
93+ indicators-client (>= ${source:Version}),
90 libautopilot-qt,
94 libautopilot-qt,
91 libqt5test5,
95 libqt5test5,
92 libqt5widgets5,
96 libqt5widgets5,
93 python-evdev,
97 python-evdev,
98+ python-gi,
94 unity8 (>= ${source:Version}),
99 unity8 (>= ${source:Version}),
95 unity8-fake-env (>= ${source:Version}),
100 unity8-fake-env (>= ${source:Version}),
96 ${misc:Depends},
101 ${misc:Depends},
97 ${python:Depends},
102 ${python:Depends},
98 ${shlibs:Depends},
103 ${shlibs:Depends},
99−Recommends: gir1.2-glib-2.0,
100− python-gi,
101Description: Test package for Unity 8 shell
104Description: Test package for Unity 8 shell
102 Autopilot tests for the unity8 package
105 Autopilot tests for the unity8 package
103
106
@@ -119,7 +122,7 @@ Pre-Depends: ${misc:Pre-Depends},
119Depends: ${misc:Depends},
122Depends: ${misc:Depends},
120 ${shlibs:Depends},
123 ${shlibs:Depends},
121Provides: unity-launcher-impl,
124Provides: unity-launcher-impl,
122− unity-launcher-impl-2,
125+ unity-launcher-impl-3,
123Description: Unity 8 private libs
126Description: Unity 8 private libs
124 The Unity 8 shell is the primary user interface for Ubuntu devices.
127 The Unity 8 shell is the primary user interface for Ubuntu devices.
125 .
128 .
debian/unity8-private.install+1
@@ -3,6 +3,7 @@ usr/lib/*/unity8/qml/HudClient
3usr/lib/*/unity8/qml/LightDM
3usr/lib/*/unity8/qml/LightDM
4usr/lib/*/unity8/qml/ListViewWithPageHeader
4usr/lib/*/unity8/qml/ListViewWithPageHeader
5usr/lib/*/unity8/qml/Powerd
5usr/lib/*/unity8/qml/Powerd
6+usr/lib/*/unity8/qml/SessionBroadcast
6usr/lib/*/unity8/qml/Ubuntu
7usr/lib/*/unity8/qml/Ubuntu
7usr/lib/*/unity8/qml/Unity
8usr/lib/*/unity8/qml/Unity
8usr/lib/*/unity8/qml/Utils
9usr/lib/*/unity8/qml/Utils
main.cpp+76−9
@@ -25,13 +25,17 @@
25#include <QtQml/QQmlContext>
25#include <QtQml/QQmlContext>
26#include <qpa/qplatformnativeinterface.h>
26#include <qpa/qplatformnativeinterface.h>
27#include <QLibrary>
27#include <QLibrary>
28+#include <QDebug>
28#include <libintl.h>
29#include <libintl.h>
30+#include <dlfcn.h>
29
31
30// local
32// local
31#include "paths.h"
33#include "paths.h"
32#include "MouseTouchAdaptor.h"
34#include "MouseTouchAdaptor.h"
33#include "ApplicationArguments.h"
35#include "ApplicationArguments.h"
34
36
37+#include <unity-mir/qmirserver.h>
38+
35namespace {
39namespace {
36
40
37void prependImportPaths(QQmlEngine *engine, const QStringList &paths)
41void prependImportPaths(QQmlEngine *engine, const QStringList &paths)
@@ -65,13 +69,14 @@ void appendImportPaths(QQmlEngine *engine, const QStringList &paths)
65
69
66void resolveIconTheme() {
70void resolveIconTheme() {
67 const char *ubuntuIconTheme = getenv("UBUNTU_ICON_THEME");
71 const char *ubuntuIconTheme = getenv("UBUNTU_ICON_THEME");
68− if (ubuntuIconTheme != NULL) {
72+ if (ubuntuIconTheme == NULL) {
69− QIcon::setThemeName(ubuntuIconTheme);
73+ ubuntuIconTheme = "ubuntu-mobile";
70 }
74 }
75+ QIcon::setThemeName(ubuntuIconTheme);
71}
76}
72} // namespace {
77} // namespace {
73
78
74−int main(int argc, char** argv)
79+int startShell(int argc, const char** argv, void* server)
75{
80{
76 /* Workaround Qt platform integration plugin not advertising itself
81 /* Workaround Qt platform integration plugin not advertising itself
77 as having the following capabilities:
82 as having the following capabilities:
@@ -81,12 +86,29 @@ int main(int argc, char** argv)
81 setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
86 setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
82 setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
87 setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
83
88
89+ const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
90+
84 QGuiApplication::setApplicationName("Unity 8");
91 QGuiApplication::setApplicationName("Unity 8");
85− QGuiApplication application(argc, argv);
92+ QGuiApplication *application;
93+ if (isUbuntuMirServer) {
94+ QLibrary unityMir("unity-mir", 1);
95+ unityMir.load();
96+
97+ typedef QGuiApplication* (*createServerApplication_t)(int&, const char **, void*);
98+ createServerApplication_t createQMirServerApplication = (createServerApplication_t) unityMir.resolve("createQMirServerApplication");
99+ if (!createQMirServerApplication) {
100+ qDebug() << "unable to resolve symbol: createQMirServerApplication";
101+ return 4;
102+ }
103+
104+ application = createQMirServerApplication(argc, argv, server);
105+ } else {
106+ application = new QGuiApplication(argc, (char**)argv);
107+ }
86
108
87 resolveIconTheme();
109 resolveIconTheme();
88
110
89− QStringList args = application.arguments();
111+ QStringList args = application->arguments();
90 ApplicationArguments qmlArgs(args);
112 ApplicationArguments qmlArgs(args);
91
113
92 // The testability driver is only loaded by QApplication but not by QGuiApplication.
114 // The testability driver is only loaded by QApplication but not by QGuiApplication.
@@ -122,8 +144,7 @@ int main(int argc, char** argv)
122 MouseTouchAdaptor *mouseTouchAdaptor = 0;
144 MouseTouchAdaptor *mouseTouchAdaptor = 0;
123 if (args.contains(QLatin1String("-mousetouch"))) {
145 if (args.contains(QLatin1String("-mousetouch"))) {
124 mouseTouchAdaptor = new MouseTouchAdaptor;
146 mouseTouchAdaptor = new MouseTouchAdaptor;
125− mouseTouchAdaptor->setTargetWindow(view);
126− application.installNativeEventFilter(mouseTouchAdaptor);
147+ application->installNativeEventFilter(mouseTouchAdaptor);
127 }
148 }
128
149
129 QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface();
150 QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface();
@@ -138,18 +159,64 @@ int main(int argc, char** argv)
138 QUrl source("Shell.qml");
159 QUrl source("Shell.qml");
139 prependImportPaths(view->engine(), ::overrideImportPaths());
160 prependImportPaths(view->engine(), ::overrideImportPaths());
140 appendImportPaths(view->engine(), ::fallbackImportPaths());
161 appendImportPaths(view->engine(), ::fallbackImportPaths());
162+
163+ QStringList importPath = view->engine()->importPathList().filter("qt5/imports");
164+ if (isUbuntuMirServer) {
165+ importPath.first().append("/Unity-Mir");
166+ view->engine()->addImportPath(importPath.first());
167+ }
168+
141 view->setSource(source);
169 view->setSource(source);
142 view->setColor("transparent");
170 view->setColor("transparent");
143
171
144− if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || args.contains(QLatin1String("-fullscreen"))) {
172+ if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || isUbuntuMirServer || args.contains(QLatin1String("-fullscreen"))) {
145 view->showFullScreen();
173 view->showFullScreen();
146 } else {
174 } else {
147 view->show();
175 view->show();
148 }
176 }
149
177
150− int result = application.exec();
178+ int result = application->exec();
151
179
152 delete mouseTouchAdaptor;
180 delete mouseTouchAdaptor;
181+ delete application;
153
182
154 return result;
183 return result;
155}
184}
185+
186+int main(int argc, const char *argv[])
187+{
188+ // For ubuntumirserver/ubuntumirclient
189+ if (qgetenv("QT_QPA_PLATFORM").startsWith("ubuntumir")) {
190+ setenv("QT_QPA_PLATFORM", "ubuntumirserver", 1);
191+
192+ // If we use unity-mir directly, we automatically link to the Mir-server
193+ // platform-api bindings, which result in unexpected behaviour when
194+ // running the non-Mir scenario.
195+ QLibrary unityMir("unity-mir", 1);
196+ unityMir.load();
197+ if (!unityMir.isLoaded()) {
198+ qDebug() << "Library unity-mir not found/loaded";
199+ return 1;
200+ }
201+
202+ typedef QMirServer* (*createServer_t)(int, const char **);
203+ createServer_t createQMirServer = (createServer_t) unityMir.resolve("createQMirServer");
204+ if (!createQMirServer) {
205+ qDebug() << "unable to resolve symbol: createQMirServer";
206+ return 2;
207+ }
208+
209+ QMirServer* mirServer = createQMirServer(argc, argv);
210+
211+ typedef int (*runWithClient_t)(QMirServer*, std::function<int(int, const char**, void*)>);
212+ runWithClient_t runWithClient = (runWithClient_t) unityMir.resolve("runQMirServerWithClient");
213+ if (!runWithClient) {
214+ qDebug() << "unable to resolve symbol: runWithClient";
215+ return 3;
216+ }
217+
218+ return runWithClient(mirServer, startShell);
219+ } else {
220+ return startShell(argc, argv, nullptr);
221+ }
222+}
plugins/AccountsService/AccountsService.cpp+67−37
@@ -17,58 +17,88 @@
17 */
17 */
18
18
19#include "AccountsService.h"
19#include "AccountsService.h"
20−#include <QDBusConnection>
20+#include "AccountsServiceDBusAdaptor.h"
21−#include <QDBusConnectionInterface>
21+
22−#include <QDBusMessage>
22+#include <QStringList>
23−#include <QDBusVariant>
24
23
25AccountsService::AccountsService(QObject* parent)
24AccountsService::AccountsService(QObject* parent)
26 : QObject(parent),
25 : QObject(parent),
27− accounts_manager(NULL),
26+ m_service(new AccountsServiceDBusAdaptor(this)),
28− users()
27+ m_user(qgetenv("USER"))
29{
28{
30− auto connection = QDBusConnection::SM_BUSNAME();
29+ connect(m_service, SIGNAL(propertiesChanged(const QString &, const QString &, const QStringList &)),
31− auto interface = connection.interface();
30+ this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
32− interface->startService("org.freedesktop.Accounts");
31+ connect(m_service, SIGNAL(maybeChanged(const QString &)),
33− accounts_manager = new QDBusInterface("org.freedesktop.Accounts",
32+ this, SLOT(maybeChanged(const QString &)));
34− "/org/freedesktop/Accounts",
35− "org.freedesktop.Accounts",
36− connection, this);
37}
33}
38
34
39−QVariant AccountsService::getUserProperty(const QString &user, const QString &property)
35+QString AccountsService::getUser()
40{
36{
41− auto iface = getUserInterface(user);
37+ return m_user;
42− if (iface != nullptr && iface->isValid()) {
38+}
43− auto answer = iface->call("Get", "com.canonical.unity.AccountsService", property);
39+
44− if (answer.type() == QDBusMessage::ReplyMessage) {
40+void AccountsService::setUser(const QString &user)
45− return answer.arguments()[0].value<QDBusVariant>().variant();
41+{
46− }
42+ m_user = user;
43+ Q_EMIT userChanged();
44+
45+ updateDemoEdges();
46+ updateBackgroundFile();
47+}
48+
49+bool AccountsService::getDemoEdges()
50+{
51+ return m_demoEdges;
52+}
53+
54+void AccountsService::setDemoEdges(bool demoEdges)
55+{
56+ m_demoEdges = demoEdges;
57+ m_service->setUserProperty(m_user, "com.canonical.unity.AccountsService", "demo-edges", demoEdges);
58+}
59+
60+QString AccountsService::getBackgroundFile()
61+{
62+ return m_backgroundFile;
63+}
64+
65+void AccountsService::updateDemoEdges()
66+{
67+ auto demoEdges = m_service->getUserProperty(m_user, "com.canonical.unity.AccountsService", "demo-edges").toBool();
68+ if (m_demoEdges != demoEdges) {
69+ m_demoEdges = demoEdges;
70+ Q_EMIT demoEdgesChanged();
47 }
71 }
48− return QVariant();
49}
72}
50
73
51−void AccountsService::setUserProperty(const QString &user, const QString &property, const QVariant &value)
74+void AccountsService::updateBackgroundFile()
52{
75{
53− auto iface = getUserInterface(user);
76+ auto backgroundFile = m_service->getUserProperty(m_user, "org.freedesktop.Accounts.User", "BackgroundFile").toString();
54− if (iface != nullptr && iface->isValid()) {
77+ if (m_backgroundFile != backgroundFile) {
55− // The value needs to be carefully wrapped
78+ m_backgroundFile = backgroundFile;
56− iface->call("Set", "com.canonical.unity.AccountsService", property, QVariant::fromValue(QDBusVariant(value)));
79+ Q_EMIT backgroundFileChanged();
57 }
80 }
58}
81}
59
82
60−QDBusInterface *AccountsService::getUserInterface(const QString &user)
83+void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
61{
84{
62− auto iface = users.value(user);
85+ if (m_user != user) {
63− if (iface == nullptr && accounts_manager->isValid()) {
86+ return;
64− auto answer = accounts_manager->call("FindUserByName", user);
87+ }
65− if (answer.type() == QDBusMessage::ReplyMessage) {
88+
66− iface = new QDBusInterface("org.freedesktop.Accounts",
89+ if (interface == "com.canonical.unity.AccountsService") {
67− answer.arguments()[0].value<QDBusObjectPath>().path(),
90+ if (changed.contains("demo-edges")) {
68− "org.freedesktop.DBus.Properties",
91+ updateDemoEdges();
69− accounts_manager->connection(), this);
70− users.insert(user, iface);
71 }
92 }
72 }
93 }
94+}
95+
96+void AccountsService::maybeChanged(const QString &user)
97+{
98+ if (m_user != user) {
73− return iface;
99+ return;
100+ }
101+
102+ // Standard properties might have changed
103+ updateBackgroundFile();
74}
104}
plugins/AccountsService/AccountsService.h+34−8
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2012,2013 Canonical, Ltd.
2+ * Copyright (C) 2013 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -19,26 +19,52 @@
19#ifndef UNITY_ACCOUNTSSERVICE_H
19#ifndef UNITY_ACCOUNTSSERVICE_H
20#define UNITY_ACCOUNTSSERVICE_H
20#define UNITY_ACCOUNTSSERVICE_H
21
21
22−#include <QDBusInterface>
23−#include <QMap>
24#include <QObject>
22#include <QObject>
25#include <QString>
23#include <QString>
26
24
25+class AccountsServiceDBusAdaptor;
26+
27class AccountsService: public QObject
27class AccountsService: public QObject
28{
28{
29 Q_OBJECT
29 Q_OBJECT
30+ Q_PROPERTY (QString user
31+ READ getUser
32+ WRITE setUser
33+ NOTIFY userChanged)
34+ Q_PROPERTY (bool demoEdges
35+ READ getDemoEdges
36+ WRITE setDemoEdges
37+ NOTIFY demoEdgesChanged)
38+ Q_PROPERTY (QString backgroundFile
39+ READ getBackgroundFile
40+ NOTIFY backgroundFileChanged)
30
41
31public:
42public:
32 explicit AccountsService(QObject *parent = 0);
43 explicit AccountsService(QObject *parent = 0);
33
44
34− Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &property);
45+ QString getUser();
46+ void setUser(const QString &user);
47+ bool getDemoEdges();
48+ void setDemoEdges(bool demoEdges);
49+ QString getBackgroundFile();
50+
51+Q_SIGNALS:
52+ void userChanged();
53+ void demoEdgesChanged();
54+ void backgroundFileChanged();
55+
56+private Q_SLOTS:
35− Q_INVOKABLE void setUserProperty(const QString &user, const QString &property, const QVariant &value);
57+ void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
58+ void maybeChanged(const QString &user);
36
59
37private:
60private:
38− QDBusInterface *getUserInterface(const QString &user);
61+ void updateDemoEdges();
62+ void updateBackgroundFile();
39
63
40− QDBusInterface *accounts_manager;
64+ AccountsServiceDBusAdaptor *m_service;
41− QMap<QString, QDBusInterface *> users;
65+ QString m_user;
66+ bool m_demoEdges;
67+ QString m_backgroundFile;
42};
68};
43
69
44#endif
70#endif
plugins/AccountsService/AccountsServiceDBusAdaptor.cppnew file+126
@@ -0,0 +1,126 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "AccountsServiceDBusAdaptor.h"
20+#include <QDBusConnection>
21+#include <QDBusConnectionInterface>
22+#include <QDBusMessage>
23+#include <QDBusVariant>
24+
25+AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
26+ : QObject(parent),
27+ accounts_manager(NULL),
28+ users()
29+{
30+ auto connection = QDBusConnection::SM_BUSNAME();
31+ auto interface = connection.interface();
32+ interface->startService("org.freedesktop.Accounts");
33+ accounts_manager = new QDBusInterface("org.freedesktop.Accounts",
34+ "/org/freedesktop/Accounts",
35+ "org.freedesktop.Accounts",
36+ connection, this);
37+}
38+
39+QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
40+{
41+ auto iface = getUserInterface(user);
42+ if (iface != nullptr && iface->isValid()) {
43+ auto answer = iface->call("Get", interface, property);
44+ if (answer.type() == QDBusMessage::ReplyMessage) {
45+ return answer.arguments()[0].value<QDBusVariant>().variant();
46+ }
47+ }
48+ return QVariant();
49+}
50+
51+void AccountsServiceDBusAdaptor::setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value)
52+{
53+ auto iface = getUserInterface(user);
54+ if (iface != nullptr && iface->isValid()) {
55+ // The value needs to be carefully wrapped
56+ iface->call("Set", interface, property, QVariant::fromValue(QDBusVariant(value)));
57+ }
58+}
59+
60+void AccountsServiceDBusAdaptor::propertiesChangedSlot(const QString &interface, const QVariantMap &changed, const QStringList &invalid)
61+{
62+ // Merge changed and invalidated together
63+ QStringList combined;
64+ combined << invalid;
65+ combined << changed.keys();
66+ combined.removeDuplicates();
67+
68+ Q_EMIT propertiesChanged(getUserForPath(message().path()), interface, combined);
69+}
70+
71+void AccountsServiceDBusAdaptor::maybeChangedSlot()
72+{
73+ Q_EMIT maybeChanged(getUserForPath(message().path()));
74+}
75+
76+QString AccountsServiceDBusAdaptor::getUserForPath(const QString &path)
77+{
78+ QMap<QString, QDBusInterface *>::const_iterator i;
79+ for (i = users.constBegin(); i != users.constEnd(); ++i) {
80+ if (i.value()->path() == path) {
81+ return i.key();
82+ }
83+ }
84+ return QString();
85+}
86+
87+QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user)
88+{
89+ auto iface = users.value(user);
90+ if (iface == nullptr && accounts_manager->isValid()) {
91+ auto answer = accounts_manager->call("FindUserByName", user);
92+ if (answer.type() == QDBusMessage::ReplyMessage) {
93+ auto path = answer.arguments()[0].value<QDBusObjectPath>().path();
94+
95+ iface = new QDBusInterface("org.freedesktop.Accounts",
96+ path,
97+ "org.freedesktop.DBus.Properties",
98+ accounts_manager->connection(), this);
99+
100+ // With its own pre-defined properties, AccountsService is oddly
101+ // close-lipped. It won't send out proper DBus.Properties notices,
102+ // but it does have one catch-all Changed() signal. So let's
103+ // listen to that.
104+ iface->connection().connect(
105+ iface->service(),
106+ path,
107+ "org.freedesktop.Accounts.User",
108+ "Changed",
109+ this,
110+ SLOT(maybeChangedSlot()));
111+
112+ // But custom properties do send out the right notifications, so
113+ // let's still listen there.
114+ iface->connection().connect(
115+ iface->service(),
116+ path,
117+ "org.freedesktop.DBus.Properties",
118+ "PropertiesChanged",
119+ this,
120+ SLOT(propertiesChangedSlot(QString, QVariantMap, QStringList)));
121+
122+ users.insert(user, iface);
123+ }
124+ }
125+ return iface;
126+}
plugins/AccountsService/AccountsServiceDBusAdaptor.hnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
20+#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
21+
22+#include <QDBusContext>
23+#include <QDBusInterface>
24+#include <QMap>
25+#include <QObject>
26+#include <QString>
27+
28+class AccountsServiceDBusAdaptor: public QObject, public QDBusContext
29+{
30+ Q_OBJECT
31+
32+public:
33+ explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
34+
35+ Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
36+ Q_INVOKABLE void setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value);
37+
38+Q_SIGNALS:
39+ void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
40+ void maybeChanged(const QString &user); // Standard properties might have changed
41+
42+private Q_SLOTS:
43+ void propertiesChangedSlot(const QString &interface, const QVariantMap &changed, const QStringList &invalid);
44+ void maybeChangedSlot();
45+
46+private:
47+ QDBusInterface *getUserInterface(const QString &user);
48+ QString getUserForPath(const QString &path);
49+
50+ QDBusInterface *accounts_manager;
51+ QMap<QString, QDBusInterface *> users;
52+};
53+
54+#endif
plugins/AccountsService/CMakeLists.txt+1
@@ -4,6 +4,7 @@ add_definitions(-DSM_BUSNAME=systemBus)
4
4
5add_library(AccountsService-qml MODULE
5add_library(AccountsService-qml MODULE
6 AccountsService.cpp
6 AccountsService.cpp
7+ AccountsServiceDBusAdaptor.cpp
7 plugin.cpp
8 plugin.cpp
8 )
9 )
9
10
plugins/CMakeLists.txt+1
@@ -3,6 +3,7 @@ add_subdirectory(HudClient)
3add_subdirectory(LightDM)
3add_subdirectory(LightDM)
4add_subdirectory(ListViewWithPageHeader)
4add_subdirectory(ListViewWithPageHeader)
5add_subdirectory(Powerd)
5add_subdirectory(Powerd)
6+add_subdirectory(SessionBroadcast)
6add_subdirectory(Ubuntu)
7add_subdirectory(Ubuntu)
7add_subdirectory(Unity)
8add_subdirectory(Unity)
8add_subdirectory(Utils)
9add_subdirectory(Utils)
plugins/SessionBroadcast/CMakeLists.txtnew file+13
@@ -0,0 +1,13 @@
1+include(Plugins)
2+
3+add_definitions(-DSM_BUSNAME=systemBus)
4+
5+add_library(SessionBroadcast-qml MODULE
6+ SessionBroadcast.cpp
7+ plugin.cpp
8+ )
9+
10+qt5_use_modules(SessionBroadcast-qml DBus Qml)
11+
12+export_qmlfiles(SessionBroadcast SessionBroadcast)
13+export_qmlplugin(SessionBroadcast 0.1 SessionBroadcast TARGETS SessionBroadcast-qml)
plugins/SessionBroadcast/SessionBroadcast.cppnew file+41
@@ -0,0 +1,41 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "SessionBroadcast.h"
20+#include <QDBusConnection>
21+
22+SessionBroadcast::SessionBroadcast(QObject* parent)
23+ : QObject(parent)
24+{
25+ auto connection = QDBusConnection::SM_BUSNAME();
26+
27+ connection.connect("com.canonical.Unity.Greeter.Broadcast",
28+ "/com/canonical/Unity/Greeter/Broadcast",
29+ "com.canonical.Unity.Greeter.Broadcast",
30+ "ShowHome",
31+ this,
32+ SLOT(onShowHome(const QString &)));
33+}
34+
35+void SessionBroadcast::onShowHome(const QString &username)
36+{
37+ // Only listen to requests meant for us
38+ if (username == qgetenv("USER")) {
39+ Q_EMIT showHome();
40+ }
41+}
plugins/SessionBroadcast/SessionBroadcast.hnew file+41
@@ -0,0 +1,41 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef UNITY_SESSIONBROADCAST_H
20+#define UNITY_SESSIONBROADCAST_H
21+
22+#include <QObject>
23+#include <QString>
24+
25+class QDBusInterface;
26+
27+class SessionBroadcast: public QObject
28+{
29+ Q_OBJECT
30+
31+public:
32+ explicit SessionBroadcast(QObject *parent = 0);
33+
34+Q_SIGNALS:
35+ void showHome();
36+
37+private Q_SLOTS:
38+ void onShowHome(const QString &username);
39+};
40+
41+#endif
plugins/SessionBroadcast/plugin.cppnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "plugin.h"
20+#include "SessionBroadcast.h"
21+
22+#include <QtQml>
23+
24+static QObject *broadcast_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25+{
26+ Q_UNUSED(engine)
27+ Q_UNUSED(scriptEngine)
28+ return new SessionBroadcast();
29+}
30+
31+void SessionBroadcastPlugin::registerTypes(const char *uri)
32+{
33+ Q_ASSERT(uri == QLatin1String("SessionBroadcast"));
34+ qmlRegisterSingletonType<SessionBroadcast>(uri, 0, 1, "SessionBroadcast", broadcast_provider);
35+}
plugins/SessionBroadcast/plugin.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef UNITY_SESSIONBROADCAST_PLUGIN_H
20+#define UNITY_SESSIONBROADCAST_PLUGIN_H
21+
22+#include <QQmlExtensionPlugin>
23+
24+class SessionBroadcastPlugin : public QQmlExtensionPlugin
25+{
26+ Q_OBJECT
27+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28+
29+public:
30+ void registerTypes(const char *uri);
31+};
32+
33+#endif
plugins/SessionBroadcast/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module SessionBroadcast
2+plugin SessionBroadcast-qml
3+typeinfo plugin.qmltypes
plugins/Unity/CMakeLists.txt+1
@@ -47,6 +47,7 @@ set(QMLPLUGIN_SRC
47 signalslist.cpp
47 signalslist.cpp
48 socialpreview.cpp
48 socialpreview.cpp
49 socialpreviewcomment.cpp
49 socialpreviewcomment.cpp
50+ iconutils.cpp
50 variantutils.cpp
51 variantutils.cpp
51)
52)
52
53
plugins/Unity/Indicators/CMakeLists.txt+9
@@ -34,11 +34,20 @@ set(IndicatorsQML_SOURCES
34 rootactionstate.cpp
34 rootactionstate.cpp
35 unitymenumodelcache.cpp
35 unitymenumodelcache.cpp
36 unitymenumodelstack.cpp
36 unitymenumodelstack.cpp
37+ visibleindicatorsmodel.cpp
37)
38)
38add_definitions(-DUNITYINDICATORS_LIBRARY)
39add_definitions(-DUNITYINDICATORS_LIBRARY)
39
40
41+set(IndicatorsQML_RESOURCES
42+ resources/indicators.qrc
43+)
44+
45+qt5_add_resources(IndicatorsQML_RESOURCES_RCC
46+ ${IndicatorsQML_RESOURCES}
47+)
40
48
41add_library(IndicatorsQml SHARED
49add_library(IndicatorsQml SHARED
50+ ${IndicatorsQML_RESOURCES_RCC}
42 ${IndicatorsQML_SOURCES}
51 ${IndicatorsQML_SOURCES}
43)
52)
44
53
plugins/Unity/Indicators/Network/qml/AccessPoint.qml+71−26
@@ -22,13 +22,15 @@ import Ubuntu.Components 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem
22import Ubuntu.Components.ListItems 0.1 as ListItem
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24
24
25−Indicators.FramedMenuItem {
25+Indicators.BaseMenuItem {
26− id: accessPoint
26+ id: menuItem
27+ implicitHeight: units.gu(5.5)
27
28
28 property bool checked: false
29 property bool checked: false
29 property bool secure: false
30 property bool secure: false
30 property bool adHoc: false
31 property bool adHoc: false
31 property int signalStrength: 0
32 property int signalStrength: 0
33+ property alias text: label.text
32
34
33 signal activate()
35 signal activate()
34
36
@@ -37,36 +39,79 @@ Indicators.FramedMenuItem {
37 checkBoxActive.checked = checked;
39 checkBoxActive.checked = checked;
38 }
40 }
39
41
40− icon: {
42+ onClicked: {
41− var imageName = "nm-signal-100"
43+ checkBoxActive.clicked();
42−
44+ }
43− if (adHoc) {
45+
44− imageName = "nm-adhoc";
46+ CheckBox {
45− } else if (signalStrength == 0) {
47+ id: checkBoxActive
46− imageName = "nm-signal-00";
48+ height: units.gu(3)
47− } else if (signalStrength <= 25) {
49+ width: units.gu(3)
48− imageName = "nm-signal-25";
50+
49− } else if (signalStrength <= 50) {
51+ anchors {
50− imageName = "nm-signal-50";
52+ left: parent.left
51− } else if (signalStrength <= 75) {
53+ leftMargin: menuItem.__contentsMargins
52− imageName = "nm-signal-75";
54+ verticalCenter: parent.verticalCenter
53 }
55 }
54
56
55− if (secure) {
57+ onClicked: {
56− imageName += "-secure";
58+ menuItem.activate();
57 }
59 }
58− return "image://gicon/" + imageName;
59 }
60 }
60
61
61− iconFrame: false
62+ Image {
62− control: CheckBox {
63+ id: iconSignal
63− id: checkBoxActive
64− height: units.gu(4)
65− width: units.gu(4)
66− anchors.centerIn: parent
67
64
68− onClicked: {
65+ width: height
69− accessPoint.activate();
66+ height: Math.min(units.gu(5), parent.height - units.gu(1))
67+ anchors {
68+ left: checkBoxActive.right
69+ leftMargin: units.gu(1)
70+ verticalCenter: parent.verticalCenter
71+ }
72+
73+ source: {
74+ var imageName = "nm-signal-100"
75+
76+ if (adHoc) {
77+ imageName = "nm-adhoc";
78+ } else if (signalStrength == 0) {
79+ imageName = "nm-signal-00";
80+ } else if (signalStrength <= 25) {
81+ imageName = "nm-signal-25";
82+ } else if (signalStrength <= 50) {
83+ imageName = "nm-signal-50";
84+ } else if (signalStrength <= 75) {
85+ imageName = "nm-signal-75";
86+ }
87+ return "image://theme/" + imageName;
88+ }
89+ }
90+
91+ Label {
92+ id: label
93+ anchors {
94+ left: iconSignal.right
95+ leftMargin: units.gu(1)
96+ verticalCenter: parent.verticalCenter
97+ right: iconSecure.visible ? iconSecure.left : parent.right
98+ rightMargin: menuItem.__contentsMargins
99+ }
100+ elide: Text.ElideRight
101+ opacity: label.enabled ? 1.0 : 0.5
102+ }
103+
104+ Image {
105+ id: iconSecure
106+ visible: secure
107+ source: "qrc:/indicators/artwork/network/secure.svg"
108+
109+ width: height
110+ height: Math.min(units.gu(4), parent.height - units.gu(1))
111+ anchors {
112+ right: parent.right
113+ rightMargin: units.gu(1)
114+ verticalCenter: parent.verticalCenter
70 }
115 }
71 }
116 }
72}
117}
plugins/Unity/Indicators/indicator.cpp−2
@@ -51,7 +51,6 @@ void Indicator::init(const QString& busName, const QSettings& settings)
51 }
51 }
52 }
52 }
53
53
54−
55 QVariantMap properties;
54 QVariantMap properties;
56 properties.clear();
55 properties.clear();
57 properties.insert("busType", 1);
56 properties.insert("busType", 1);
@@ -87,7 +86,6 @@ void Indicator::setPosition(int position)
87 }
86 }
88}
87}
89
88
90−
91QVariant Indicator::indicatorProperties() const
89QVariant Indicator::indicatorProperties() const
92{
90{
93 return m_properties;
91 return m_properties;
plugins/Unity/Indicators/indicator.h−4
@@ -29,10 +29,6 @@
29class UNITYINDICATORS_EXPORT Indicator : public QObject
29class UNITYINDICATORS_EXPORT Indicator : public QObject
30{
30{
31 Q_OBJECT
31 Q_OBJECT
32− Q_PROPERTY(QString identifier READ identifier NOTIFY identifierChanged)
33− Q_PROPERTY(int position READ position NOTIFY positionChanged)
34− Q_PROPERTY(QVariant indicatorProperties READ indicatorProperties NOTIFY indicatorPropertiesChanged)
35−
36public:
32public:
37 typedef QSharedPointer<Indicator> Ptr;
33 typedef QSharedPointer<Indicator> Ptr;
38
34
plugins/Unity/Indicators/indicators.h+1−1
@@ -79,7 +79,7 @@ public:
79 WidgetSource,
79 WidgetSource,
80 PageSource,
80 PageSource,
81 IndicatorProperties,
81 IndicatorProperties,
82− IsValid
82+ IsVisible
83 };
83 };
84
84
85 IndicatorsModelRole(QObject*parent=0):QObject(parent) {}
85 IndicatorsModelRole(QObject*parent=0):QObject(parent) {}
plugins/Unity/Indicators/indicatorsmodel.cpp−3
@@ -206,7 +206,6 @@ QHash<int, QByteArray> IndicatorsModel::roleNames() const
206 roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
206 roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
207 roles[IndicatorsModelRole::PageSource] = "pageSource";
207 roles[IndicatorsModelRole::PageSource] = "pageSource";
208 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
208 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
209− roles[IndicatorsModelRole::IsValid] = "isValid";
210 }
209 }
211 return roles;
210 return roles;
212}
211}
@@ -269,8 +268,6 @@ QVariant IndicatorsModel::data(const QModelIndex &index, int role) const
269 return QVariant(indicator->indicatorProperties());
268 return QVariant(indicator->indicatorProperties());
270 }
269 }
271 break;
270 break;
272− case IndicatorsModelRole::IsValid:
273− return (indicator ? true : false);
274 case IndicatorsModelRole::Title:
271 case IndicatorsModelRole::Title:
275 case IndicatorsModelRole::Description:
272 case IndicatorsModelRole::Description:
276 case IndicatorsModelRole::WidgetSource:
273 case IndicatorsModelRole::WidgetSource:
plugins/Unity/Indicators/indicatorsmodel.h+2−1
@@ -44,7 +44,7 @@ public:
44 Q_INVOKABLE void load();
44 Q_INVOKABLE void load();
45 Q_INVOKABLE void unload();
45 Q_INVOKABLE void unload();
46
46
47− Q_INVOKABLE QVariant data(int row, int role = Qt::DisplayRole) const;
47+ Q_INVOKABLE QVariant data(int row, int role) const;
48
48
49 /* QAbstractItemModel */
49 /* QAbstractItemModel */
50 QHash<int, QByteArray> roleNames() const;
50 QHash<int, QByteArray> roleNames() const;
@@ -56,6 +56,7 @@ public:
56 void setIndicatorData(const QVariant& data);
56 void setIndicatorData(const QVariant& data);
57 QVariant indicatorData() const;
57 QVariant indicatorData() const;
58
58
59+
59Q_SIGNALS:
60Q_SIGNALS:
60 void countChanged();
61 void countChanged();
61 void indicatorDataChanged(const QVariant& data);
62 void indicatorDataChanged(const QVariant& data);
plugins/Unity/Indicators/plugin.cpp+2
@@ -32,6 +32,7 @@
32#include "rootactionstate.h"
32#include "rootactionstate.h"
33#include "unitymenumodelcache.h"
33#include "unitymenumodelcache.h"
34#include "unitymenumodelstack.h"
34#include "unitymenumodelstack.h"
35+#include "visibleindicatorsmodel.h"
35
36
36static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
37static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
37 Q_UNUSED(engine);
38 Q_UNUSED(engine);
@@ -47,6 +48,7 @@ void Indicators2Plugin::registerTypes(const char *uri)
47 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
48 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
48 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
49 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
49 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
50 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
51+ qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
50
52
51 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
53 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
52
54
plugins/Unity/Indicators/qml/IndicatorBase.qml−5
@@ -24,8 +24,6 @@ import Unity.Indicators 0.1 as Indicators
24Item {
24Item {
25 id: indicatorItem
25 id: indicatorItem
26
26
27− // FIXME : should be disabled until bus available when we have desktop indicators
28− // for now, disable when we dont habe the correct profile.
29 enabled: menuObjectPaths.hasOwnProperty(device)
27 enabled: menuObjectPaths.hasOwnProperty(device)
30
28
31 //const
29 //const
@@ -39,9 +37,6 @@ Item {
39
37
40 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
38 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
41
39
42− signal actionGroupUpdated()
43− signal modelUpdated()
44−
45 property alias menuModel: cachedModel.model
40 property alias menuModel: cachedModel.model
46
41
47 CachedUnityMenuModel {
42 CachedUnityMenuModel {
plugins/Unity/Indicators/qml/IndicatorWidget.qml+1
@@ -24,6 +24,7 @@ IndicatorBase {
24 id: indicatorWidget
24 id: indicatorWidget
25
25
26 active: true
26 active: true
27+ enabled: false
27 property int iconSize: height - units.gu(0.5)
28 property int iconSize: height - units.gu(0.5)
28
29
29 property var actionState: Indicators.RootActionState {
30 property var actionState: Indicators.RootActionState {
plugins/Unity/Indicators/qml/MenuItemFactory.qml+6−21
@@ -73,23 +73,8 @@ Item {
73 'max-icon': 'icon'});
73 'max-icon': 'icon'});
74 }
74 }
75
75
76− // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
77− // reaching the boundaries (0.0 or 1.0), the value is converted
78− // to an integer when automatically wrapped in a variant when
79− // passed to QStateAction::updateState(…). The server chokes on
80− // those values, complaining that they’re not of the right type…
81 onChangeState: {
76 onChangeState: {
82− if (value == Math.round(value)) {
83− if (value >= maximumValue) {
84− model.changeState(modelIndex, maximumValue - 0.000001);
85− } else if (value <= minimumValue) {
86− model.changeState(modelIndex, minimumValue + 0.000001);
77+ model.changeState(modelIndex, value);
87− } else {
88− model.changeState(modelIndex, value * 1.000001);
89− }
90− } else {
91− model.changeState(modelIndex, value);
92− }
93 }
78 }
94 }
79 }
95 }
80 }
@@ -174,17 +159,17 @@ Item {
174 id: accessPoint;
159 id: accessPoint;
175 ICNetwork.AccessPoint {
160 ICNetwork.AccessPoint {
176 property QtObject menu: null
161 property QtObject menu: null
177−// property var strenthAction: QMenuModel.UnityMenuAction {
162+ property var strengthAction: QMenuModel.UnityMenuAction {
178−// model: menuFactory.model ? menuFactory.model : null
163+ model: menuFactory.model ? menuFactory.model : null
179−// name: menu ? menu.ext.xCanonicalWifiApStrengthAction : ""
164+ name: menu ? menu.ext.xCanonicalWifiApStrengthAction : ""
180−// }
165+ }
181
166
182 text: menu && menu.label ? menu.label : ""
167 text: menu && menu.label ? menu.label : ""
183 icon: menu ? menu.icon : ""
168 icon: menu ? menu.icon : ""
184 secure: menu ? menu.ext.xCanonicalWifiApIsSecure : false
169 secure: menu ? menu.ext.xCanonicalWifiApIsSecure : false
185 adHoc: menu ? menu.ext.xCanonicalWifiApIsAdhoc : false
170 adHoc: menu ? menu.ext.xCanonicalWifiApIsAdhoc : false
186 checked: menu ? menu.isToggled : false
171 checked: menu ? menu.isToggled : false
187−// signalStrength: strenthAction.valid ? strenthAction.state : 0
172+ signalStrength: strengthAction.valid ? strengthAction.state : 0
188 enabled: menu ? menu.sensitive : false
173 enabled: menu ? menu.sensitive : false
189
174
190 Component.onCompleted: {
175 Component.onCompleted: {
plugins/Unity/Indicators/resources/artwork/network/secure.svgnew file+71
@@ -0,0 +1,71 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3+
4+<svg
5+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6+ xmlns:cc="http://creativecommons.org/ns#"
7+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8+ xmlns:svg="http://www.w3.org/2000/svg"
9+ xmlns="http://www.w3.org/2000/svg"
10+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12+ width="64"
13+ height="64"
14+ id="svg2986"
15+ version="1.1"
16+ inkscape:version="0.48.4 r9939"
17+ sodipodi:docname="secure.svg">
18+ <defs
19+ id="defs2988" />
20+ <sodipodi:namedview
21+ id="base"
22+ pagecolor="#ffffff"
23+ bordercolor="#666666"
24+ borderopacity="1.0"
25+ inkscape:pageopacity="0.0"
26+ inkscape:pageshadow="2"
27+ inkscape:zoom="7.9180415"
28+ inkscape:cx="7.5048745"
29+ inkscape:cy="29.401628"
30+ inkscape:current-layer="layer1"
31+ showgrid="true"
32+ inkscape:grid-bbox="true"
33+ inkscape:document-units="px"
34+ inkscape:snap-grids="false"
35+ inkscape:window-width="1549"
36+ inkscape:window-height="876"
37+ inkscape:window-x="51"
38+ inkscape:window-y="24"
39+ inkscape:window-maximized="1">
40+ <inkscape:grid
41+ type="xygrid"
42+ id="grid3012"
43+ empspacing="5"
44+ visible="true"
45+ enabled="true"
46+ snapvisiblegridlinesonly="true" />
47+ </sodipodi:namedview>
48+ <metadata
49+ id="metadata2991">
50+ <rdf:RDF>
51+ <cc:Work
52+ rdf:about="">
53+ <dc:format>image/svg+xml</dc:format>
54+ <dc:type
55+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
56+ <dc:title />
57+ </cc:Work>
58+ </rdf:RDF>
59+ </metadata>
60+ <g
61+ id="layer1"
62+ inkscape:label="Layer 1"
63+ inkscape:groupmode="layer"
64+ transform="translate(0,32)">
65+ <path
66+ inkscape:connector-curvature="0"
67+ d="M 29.999999,-18 C 25.568001,-18 22,-14.432 22,-10 v 6 h -2 c -1.107999,0 -2,0.892 -2,2 v 18 c 0,1.108 0.892001,2 2,2 h 24 c 1.108,0 2,-0.892 2,-2 V -2 c 0,-1.108 -0.892,-2 -2,-2 h -2 v -6 c 0,-4.432 -3.568001,-8 -8.000001,-8 h -4 z m 0,4 h 4 c 2.216,0 4,1.784 4,4 v 6 H 26.000001 v -6 c 0,-2.216 1.784,-4 3.999998,-4 z"
68+ style="fill:#dfdbd2;fill-rule:evenodd"
69+ id="path8" />
70+ </g>
71+</svg>
plugins/Unity/Indicators/resources/indicators.qrcnew file+5
@@ -0,0 +1,5 @@
1+<RCC>
2+ <qresource prefix="/indicators">
3+ <file>artwork/network/secure.svg</file>
4+ </qresource>
5+</RCC>
plugins/Unity/Indicators/rootactionstate.cpp+52−22
@@ -108,25 +108,32 @@ bool RootActionState::isValid() const
108 return m_menu && m_menu->rowCount() > 0;
108 return m_menu && m_menu->rowCount() > 0;
109}
109}
110
110
111−QString RootActionState::label() const
111+QString RootActionState::leftLabel() const
112{
112{
113 if (!isValid()) return QString();
113 if (!isValid()) return QString();
114
114
115− return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
115+ return m_cachedState.value("pre-label", QVariant::fromValue(QString())).toString();
116}
116}
117
117
118−QString RootActionState::icon() const
118+QString RootActionState::rightLabel() const
119{
119{
120 if (!isValid()) return QString();
120 if (!isValid()) return QString();
121
121
122− return m_cachedState.value("icon", QVariant::fromValue(QString())).toString();
122+ return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
123+}
124+
125+QStringList RootActionState::icons() const
126+{
127+ if (!isValid()) return QStringList();
128+
129+ return m_cachedState.value("icons", QVariant::fromValue(QStringList())).toStringList();
123}
130}
124
131
125QString RootActionState::accessibleName() const
132QString RootActionState::accessibleName() const
126{
133{
127 if (!isValid()) return QString();
134 if (!isValid()) return QString();
128
135
129− return m_cachedState.value("accessible-name", QVariant::fromValue(QString())).toString();
136+ return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
130}
137}
131
138
132bool RootActionState::isVisible() const
139bool RootActionState::isVisible() const
@@ -141,15 +148,15 @@ static QString iconUri(GIcon *icon)
141 QString uri;
148 QString uri;
142
149
143 if (G_IS_THEMED_ICON (icon)) {
150 if (G_IS_THEMED_ICON (icon)) {
144−
145 const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon));
151 const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon));
146− guint index = 0;
152+
153+ QStringList iconNameList;
147− while(iconNames[index] != NULL) {
154+ for (uint index = 0; iconNames[index] != NULL; index++) {
148− if (QIcon::hasThemeIcon(iconNames[index])) {
155+ iconNameList << iconNames[index];
156+ }
157+
158+ if (!iconNameList.empty()) {
149− uri = QString("image://theme/%1").arg(iconNames[index]);
159+ uri = QString("image://theme/%1").arg(iconNameList.join(","));
150− break;
151− }
152− index++;
153 }
160 }
154 }
161 }
155 else if (G_IS_FILE_ICON (icon)) {
162 else if (G_IS_FILE_ICON (icon)) {
@@ -198,16 +205,39 @@ QVariant RootActionState::toQVariant(GVariant* state) const
198 while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
205 while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
199 {
206 {
200 QString str = QString::fromUtf8(key);
207 QString str = QString::fromUtf8(key);
201− if (str == "icon") {
208+ if (str == "icon" && !qmap.contains("icons")) {
209+ QStringList icons;
210+
202 // FIXME - should be sending a url.
211 // FIXME - should be sending a url.
203− GIcon *icon = g_icon_deserialize (vvalue);
212+ GIcon *gicon = g_icon_deserialize (vvalue);
204− if (icon) {
213+ if (gicon) {
205− qmap.insert(str, iconUri(icon));
214+ icons << iconUri(gicon);
206− g_object_unref (icon);
215+ g_object_unref (gicon);
207 }
216 }
208− else {
209− qmap.insert(str, QVariant(""));
217+ qmap.insert("icons", icons);
218+
219+ } else if (str == "icons") {
220+
221+ QStringList icons;
222+
223+ if (g_variant_type_is_array(g_variant_get_type(vvalue))) {
224+
225+
226+ for (int i = 0, iMax = g_variant_n_children(vvalue); i < iMax; i++) {
227+ GVariant *child = g_variant_get_child_value(vvalue, i);
228+
229+ // FIXME - should be sending a url.
230+ GIcon *gicon = g_icon_deserialize (child);
231+ if (gicon) {
232+ icons << iconUri(gicon);
233+ g_object_unref (gicon);
234+ }
235+ g_variant_unref(child);
236+ }
210 }
237 }
238+ // will overwrite icon.
239+ qmap.insert("icons", icons);
240+
211 } else {
241 } else {
212 qmap.insert(str, ActionStateParser::toQVariant(vvalue));
242 qmap.insert(str, ActionStateParser::toQVariant(vvalue));
213 }
243 }
@@ -230,12 +260,12 @@ QVariant RootActionState::toQVariant(GVariant* state) const
230 &visible);
260 &visible);
231
261
232 qmap["label"] = label ? QString::fromUtf8(label) : "";
262 qmap["label"] = label ? QString::fromUtf8(label) : "";
233− qmap["accessible-name"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
263+ qmap["accessible-desc"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
234 qmap["visible"] = visible;
264 qmap["visible"] = visible;
235
265
236 gicon = g_icon_new_for_string (icon, NULL);
266 gicon = g_icon_new_for_string (icon, NULL);
237 if (gicon) {
267 if (gicon) {
238− qmap["icon"] = iconUri(gicon);
268+ qmap["icons"] = QStringList() << iconUri(gicon);
239 g_object_unref (gicon);
269 g_object_unref (gicon);
240 }
270 }
241
271
plugins/Unity/Indicators/rootactionstate.h+9−6
@@ -32,8 +32,9 @@ class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
32 Q_PROPERTY(UnityMenuModel* menu READ menu WRITE setMenu NOTIFY menuChanged)
32 Q_PROPERTY(UnityMenuModel* menu READ menu WRITE setMenu NOTIFY menuChanged)
33
33
34 Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
34 Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
35− Q_PROPERTY(QString label READ label NOTIFY labelChanged)
35+ Q_PROPERTY(QString leftLabel READ leftLabel NOTIFY leftLabelChanged)
36− Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
36+ Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
37+ Q_PROPERTY(QStringList icons READ icons NOTIFY iconsChanged)
37 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
38 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
38 Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
39 Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
39public:
40public:
@@ -47,8 +48,9 @@ public:
47 void setIndex(int index);
48 void setIndex(int index);
48
49
49 bool isValid() const;
50 bool isValid() const;
50− QString label() const;
51+ QString leftLabel() const;
51− QString icon() const;
52+ QString rightLabel() const;
53+ QStringList icons() const;
52 QString accessibleName() const;
54 QString accessibleName() const;
53 bool isVisible() const;
55 bool isVisible() const;
54
56
@@ -62,8 +64,9 @@ Q_SIGNALS:
62 void indexChanged();
64 void indexChanged();
63
65
64 void validChanged();
66 void validChanged();
65− void labelChanged();
67+ void leftLabelChanged();
66− void iconChanged();
68+ void rightLabelChanged();
69+ void iconsChanged();
67 void accessibleNameChanged();
70 void accessibleNameChanged();
68 void visibleChanged();
71 void visibleChanged();
69
72
plugins/Unity/Indicators/visibleindicatorsmodel.cppnew file+94
@@ -0,0 +1,94 @@
1+/*
2+ * Copyright (C) 2012, 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+// self
18+#include "visibleindicatorsmodel.h"
19+#include "indicators.h"
20+
21+VisibleIndicatorsModel::VisibleIndicatorsModel(QObject *parent)
22+ : QIdentityProxyModel(parent),
23+ m_inserting(false)
24+{
25+ QObject::connect(this, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(onBeginRowInserted(QModelIndex, int, int)));
26+ QObject::connect(this, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(onRowInserted(QModelIndex, int, int)));
27+}
28+
29+QHash<int, QByteArray> VisibleIndicatorsModel::roleNames() const
30+{
31+ static QHash<int, QByteArray> roles;
32+ if (roles.isEmpty())
33+ {
34+ roles[IndicatorsModelRole::Identifier] = "identifier";
35+ roles[IndicatorsModelRole::Position] = "position";
36+ roles[IndicatorsModelRole::Title] = "title";
37+ roles[IndicatorsModelRole::Description] = "description";
38+ roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
39+ roles[IndicatorsModelRole::PageSource] = "pageSource";
40+ roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
41+ roles[IndicatorsModelRole::IsVisible] = "isVisible";
42+ }
43+ return roles;
44+}
45+
46+void VisibleIndicatorsModel::setSourceModel(QAbstractItemModel *model)
47+{
48+ if (sourceModel() != model) {
49+ QIdentityProxyModel::setSourceModel(model);
50+ Q_EMIT modelChanged();
51+ }
52+}
53+
54+QVariantMap VisibleIndicatorsModel::visible() const
55+{
56+ return m_visible;
57+}
58+
59+void VisibleIndicatorsModel::onBeginRowInserted(const QModelIndex&, int, int)
60+{
61+ m_inserting = true;
62+}
63+
64+void VisibleIndicatorsModel::onRowInserted(const QModelIndex&, int, int)
65+{
66+ m_inserting = false;
67+}
68+
69+void VisibleIndicatorsModel::setVisible(const QVariantMap& visible)
70+{
71+ if (m_visible != visible) {
72+ m_visible = visible;
73+ Q_EMIT visibleChanged();
74+
75+ // need to tell the view that the visible data has changed.
76+ if (!m_inserting && rowCount() > 0) {
77+ Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), QVector<int>() << IndicatorsModelRole::IsVisible);
78+ }
79+ }
80+}
81+
82+QVariant VisibleIndicatorsModel::data(const QModelIndex &index, int role) const
83+{
84+ if (role != IndicatorsModelRole::IsVisible) {
85+ return QIdentityProxyModel::data(index, role);
86+ }
87+
88+ if (!index.isValid()) {
89+ return false;
90+ }
91+
92+ QString ident = QIdentityProxyModel::data(index, IndicatorsModelRole::Identifier).toString();
93+ return m_visible.value(ident, false).toBool();
94+}
plugins/Unity/Indicators/visibleindicatorsmodel.hnew file+56
@@ -0,0 +1,56 @@
1+/*
2+ * Copyright (C) 2012, 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef VISIBLEINDICATORSMODEL_H
21+#define VISIBLEINDICATORSMODEL_H
22+
23+#include <QIdentityProxyModel>
24+
25+class VisibleIndicatorsModel : public QIdentityProxyModel
26+{
27+ Q_OBJECT
28+
29+ Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setSourceModel NOTIFY modelChanged)
30+ Q_PROPERTY(QVariantMap visible READ visible WRITE setVisible NOTIFY visibleChanged)
31+
32+public:
33+ explicit VisibleIndicatorsModel(QObject *parent = 0);
34+
35+ virtual void setSourceModel(QAbstractItemModel *model);
36+ virtual QHash<int, QByteArray> roleNames() const;
37+ virtual QVariant data(const QModelIndex &index, int role) const;
38+
39+ QVariantMap visible() const;
40+ void setVisible(const QVariantMap& visible);
41+
42+public Q_SLOTS:
43+ void onBeginRowInserted(const QModelIndex&, int start, int end);
44+ void onRowInserted(const QModelIndex&, int start, int end);
45+
46+Q_SIGNALS:
47+ void totalCountChanged();
48+ void modelChanged();
49+ void visibleChanged();
50+
51+private:
52+ QVariantMap m_visible;
53+ bool m_inserting;
54+};
55+
56+#endif // VISIBLEINDICATORSMODEL_H
plugins/Unity/IndicatorsLegacy/Messaging/qml/GroupedMessage.qml+1−1
@@ -44,7 +44,7 @@ Indicators.BasicMenuItem {
44 height: units.gu(6)
44 height: units.gu(6)
45 width: units.gu(6)
45 width: units.gu(6)
46 image: Image {
46 image: Image {
47− source: menu && (menu.extra.canonical_icon.length > 0) ? "image://gicon/" + encodeURI(menu.extra.canonical_icon) : "qrc:/indicators/artwork/messaging/default_app.svg"
47+ source: menu && (menu.extra.canonical_icon.length > 0) ? "image://theme/" + encodeURI(menu.extra.canonical_icon) : "qrc:/indicators/artwork/messaging/default_app.svg"
48 fillMode: Image.PreserveAspectFit
48 fillMode: Image.PreserveAspectFit
49 }
49 }
50 }
50 }
plugins/Unity/IndicatorsLegacy/Messaging/qml/HeroMessage.qml+2−2
@@ -48,8 +48,8 @@ Indicators.BasicMenuItem {
48 anchors.left: parent.left
48 anchors.left: parent.left
49 anchors.right: parent.right
49 anchors.right: parent.right
50
50
51− avatar: menu && (menu.extra.canonical_icon.length > 0) ? "image://gicon/" + encodeURI(menu.extra.canonical_icon) : "qrc:/indicators/artwork/messaging/default_contact.png"
51+ avatar: menu && (menu.extra.canonical_icon.length > 0) ? "image://theme/" + encodeURI(menu.extra.canonical_icon) : "qrc:/indicators/artwork/messaging/default_contact.png"
52− icon: menu && (menu.extra.canonical_app_icon.length > 0) ? "image://gicon/" + encodeURI(menu.extra.canonical_app_icon) : ""
52+ icon: menu && (menu.extra.canonical_app_icon.length > 0) ? "image://theme/" + encodeURI(menu.extra.canonical_app_icon) : ""
53 appIcon: icon
53 appIcon: icon
54
54
55 state: heroMessage.state
55 state: heroMessage.state
plugins/Unity/IndicatorsLegacy/Network/qml/AccessPoint.qml+2−2
@@ -47,10 +47,10 @@ Indicators.MenuItem {
47 imageName += "-secure";
47 imageName += "-secure";
48 }
48 }
49
49
50− return "image://gicon/" + imageName;
50+ return "image://theme/" + imageName;
51 }
51 }
52
52
53− icon: menu && wifiApStrength ? getNetworkIcon(menu) : "image://gicon/wifi-none"
53+ icon: menu && wifiApStrength ? getNetworkIcon(menu) : "image://theme/wifi-none"
54 iconFrame: false
54 iconFrame: false
55 control: CheckBox {
55 control: CheckBox {
56 id: checkBoxActive
56 id: checkBoxActive
plugins/Unity/IndicatorsLegacy/qml/IndicatorWidget.qml+2−2
@@ -23,7 +23,7 @@ import Unity.IndicatorsLegacy 0.1 as Indicators
23IndicatorBase {
23IndicatorBase {
24 id: indicatorWidget
24 id: indicatorWidget
25
25
26−// enabled: false // FIXME : should be disabled until bus available when we have desktop indicators
26+ enabled: false
27 property var action: undefined
27 property var action: undefined
28 property bool started: (busType != 0) && (busName != "") && (deviceMenuObjectPath != "")
28 property bool started: (busType != 0) && (busName != "") && (deviceMenuObjectPath != "")
29 property int iconSize: height - units.gu(0.5)
29 property int iconSize: height - units.gu(0.5)
@@ -40,7 +40,6 @@ IndicatorBase {
40 actionGroup.stop();
40 actionGroup.stop();
41 }
41 }
42 }
42 }
43− onActionChanged: actionStateChanged()
44 onActionGroupUpdated: update_state()
43 onActionGroupUpdated: update_state()
45 onModelUpdated: update_state()
44 onModelUpdated: update_state()
46
45
@@ -57,6 +56,7 @@ IndicatorBase {
57
56
58 if (extra.canonical_type === rootMenuType) {
57 if (extra.canonical_type === rootMenuType) {
59 action = actionGroup.action(proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Action));
58 action = actionGroup.action(proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Action));
59+ actionStateChanged();
60 }
60 }
61 }
61 }
62
62
plugins/Unity/Launcher/CMakeLists.txt+7−2
@@ -1,11 +1,14 @@
1include(FindPkgConfig)
1include(FindPkgConfig)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
3+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=1)
4+pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3
5
4add_definitions(-DSM_BUSNAME=systemBus)
6add_definitions(-DSM_BUSNAME=systemBus)
5
7
6include_directories(
8include_directories(
7 ${CMAKE_CURRENT_SOURCE_DIR}
9 ${CMAKE_CURRENT_SOURCE_DIR}
8 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
10 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
11+ ${GSETTINGS_QT_INCLUDE_DIRS}
9)
12)
10
13
11set(QMLLAUNCHERPLUGIN_SRC
14set(QMLLAUNCHERPLUGIN_SRC
@@ -15,7 +18,7 @@ set(QMLLAUNCHERPLUGIN_SRC
15 quicklistmodel.cpp
18 quicklistmodel.cpp
16 common/quicklistentry.cpp
19 common/quicklistentry.cpp
17 backend/launcherbackend.cpp
20 backend/launcherbackend.cpp
18− ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
19 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
22 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
20 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
23 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
21 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
24 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
@@ -25,6 +28,8 @@ add_library(UnityLauncher-qml MODULE
25 ${QMLLAUNCHERPLUGIN_SRC}
28 ${QMLLAUNCHERPLUGIN_SRC}
26 )
29 )
27
30
31+target_link_libraries(UnityLauncher-qml ${GSETTINGS_QT_LDFLAGS})
32+
28qt5_use_modules(UnityLauncher-qml DBus Qml)
33qt5_use_modules(UnityLauncher-qml DBus Qml)
29
34
30# export the qmldir and qmltypes files
35# export the qmldir and qmltypes files
plugins/Unity/Launcher/backend/launcherbackend.cpp+192−100
@@ -18,88 +18,117 @@
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
19 */
20
20
21−#include "AccountsService.h"
21+#include "AccountsServiceDBusAdaptor.h"
22#include "launcherbackend.h"
22#include "launcherbackend.h"
23
23
24#include <QDir>
24#include <QDir>
25#include <QDBusArgument>
25#include <QDBusArgument>
26#include <QFileInfo>
26#include <QFileInfo>
27+#include <QGSettings>
28+#include <QDebug>
29+
30+class LauncherBackendItem
31+{
32+public:
33+ LauncherBackendItem(): pinned(false) {}
34+
35+ QString desktopFile;
36+ QString displayName;
37+ QString icon;
38+ bool pinned;
39+};
27
40
28LauncherBackend::LauncherBackend(bool useStorage, QObject *parent):
41LauncherBackend::LauncherBackend(bool useStorage, QObject *parent):
29 QObject(parent),
42 QObject(parent),
30 m_accounts(nullptr)
43 m_accounts(nullptr)
31{
44{
32 if (useStorage) {
45 if (useStorage) {
33− m_accounts = new AccountsService(this);
46+ m_accounts = new AccountsServiceDBusAdaptor(this);
34 }
47 }
35− setUser(qgetenv("USER"));
48+ m_user = qgetenv("USER");
49+ syncFromAccounts();
36}
50}
37
51
38LauncherBackend::~LauncherBackend()
52LauncherBackend::~LauncherBackend()
39{
53{
40− clearItems();
54+ m_storedApps.clear();
41−}
42
55
43−void LauncherBackend::clearItems()
44−{
45− for (LauncherBackendItem &app: m_storedApps) {
56+ Q_FOREACH(LauncherBackendItem *item, m_itemCache) {
46− delete app.settings;
57+ delete item;
47 }
58 }
48− m_storedApps.clear();
59+ m_itemCache.clear();
49}
60}
50
61
51QStringList LauncherBackend::storedApplications() const
62QStringList LauncherBackend::storedApplications() const
52{
63{
53− auto files = QStringList();
54− for (const LauncherBackendItem &app: m_storedApps) {
55− files << app.settings->fileName();
56− }
57− return files;
64+ return m_storedApps;
58}
65}
59
66
60void LauncherBackend::setStoredApplications(const QStringList &appIds)
67void LauncherBackend::setStoredApplications(const QStringList &appIds)
61{
68{
62− // Record all existing pinned apps, so we can notice them in new list
69+ if (appIds.count() < m_storedApps.count()) {
63− auto pinnedItems = QStringList();
70+ Q_FOREACH(const QString &appId, m_storedApps) {
64− for (LauncherBackendItem &app: m_storedApps) {
71+ if (!appIds.contains(appId)) {
65− if (app.pinned) {
72+ delete m_itemCache.take(appId);
66− pinnedItems.append(app.settings->fileName());
73+ }
67 }
74 }
68 }
75 }
69−
76+ m_storedApps = appIds;
70− clearItems();
71−
72− for (const QString &appId: appIds) {
77+ Q_FOREACH(const QString &appId, appIds) {
73− loadApp(makeAppDetails(appId, pinnedItems.contains(desktopFile(appId))));
78+ if (!m_itemCache.contains(appId)) {
79+ QString df = findDesktopFile(appId);
80+ if (!df.isEmpty()) {
81+ LauncherBackendItem *item = parseDesktopFile(df);
82+ m_itemCache.insert(appId, item);
83+ } else {
84+ // Cannot find any data for that app... ignoring it.
85+ qWarning() << "cannot find desktop file for" << appId << ". discarding app.";
86+ m_storedApps.removeAll(appId);
87+ }
88+ }
74 }
89 }
75−
76 syncToAccounts();
90 syncToAccounts();
77}
91}
78
92
79QString LauncherBackend::desktopFile(const QString &appId) const
93QString LauncherBackend::desktopFile(const QString &appId) const
80{
94{
81− QFileInfo fileInfo(QDir("/usr/share/applications"), appId);
95+ LauncherBackendItem *item = m_itemCache.value(appId);
82− return fileInfo.absoluteFilePath();
96+ if (item) {
97+ return item->desktopFile;
98+ }
99+
100+ return findDesktopFile(appId);
83}
101}
84
102
85QString LauncherBackend::displayName(const QString &appId) const
103QString LauncherBackend::displayName(const QString &appId) const
86{
104{
87− auto desktopFile = parseDesktopFile(appId);
105+ LauncherBackendItem *item = m_itemCache.value(appId);
88− auto displayName = desktopFile->value("Desktop Entry/Name").toString();
106+ if (item) {
89− delete desktopFile;
107+ return item->displayName;
108+ }
109+
110+ QString df = findDesktopFile(appId);
111+ if (!df.isEmpty()) {
112+ LauncherBackendItem *item = parseDesktopFile(df);
113+ m_itemCache.insert(appId, item);
114+ return item->displayName;
115+ }
116+
90− return displayName;
117+ return QString();
91}
118}
92
119
93QString LauncherBackend::icon(const QString &appId) const
120QString LauncherBackend::icon(const QString &appId) const
94{
121{
95− auto desktopFile = parseDesktopFile(appId);
122+ QString iconName;
96− auto iconName = desktopFile->value("Desktop Entry/Icon").toString();
123+ LauncherBackendItem *item = m_itemCache.value(appId);
97− delete desktopFile;
124+ if (item) {
98−
125+ iconName = item->icon;
126+ } else {
127+ QString df = findDesktopFile(appId);
99− if (!iconName.isEmpty()) {
128+ if (!df.isEmpty()) {
100− QFileInfo iconFileInfo(iconName);
129+ LauncherBackendItem *item = parseDesktopFile(df);
101− if (iconFileInfo.isRelative()) {
130+ m_itemCache.insert(appId, item);
102− iconName = "image://gicon/" + iconName;
131+ iconName = item->icon;
103 }
132 }
104 }
133 }
105
134
@@ -108,19 +137,26 @@ QString LauncherBackend::icon(const QString &appId) const
108
137
109bool LauncherBackend::isPinned(const QString &appId) const
138bool LauncherBackend::isPinned(const QString &appId) const
110{
139{
111− auto index = findItem(appId);
140+ LauncherBackendItem *item = m_itemCache.value(appId);
112− if (index < 0) {
141+ if (item) {
113− return false;
142+ return item->pinned;
114− } else {
115− return m_storedApps[index].pinned;
116 }
143 }
144+ return false;
117}
145}
118
146
119void LauncherBackend::setPinned(const QString &appId, bool pinned)
147void LauncherBackend::setPinned(const QString &appId, bool pinned)
120{
148{
121− auto index = findItem(appId);
149+ if (!m_storedApps.contains(appId)) {
122− if (index >= 0 && !m_storedApps[index].pinned) {
150+ return;
151+ }
152+
153+ LauncherBackendItem *item = m_itemCache.value(appId);
154+ if (!item) {
155+ item = new LauncherBackendItem();
156+ m_itemCache.insert(appId, item);
157+ }
158+ if (item->pinned != pinned) {
123− m_storedApps[index].pinned = pinned;
159+ item->pinned = pinned;
124 syncToAccounts();
160 syncToAccounts();
125 }
161 }
126}
162}
@@ -153,8 +189,10 @@ int LauncherBackend::count(const QString &appId) const
153
189
154void LauncherBackend::setUser(const QString &username)
190void LauncherBackend::setUser(const QString &username)
155{
191{
192+ if (qgetenv("USER") == "lightdm" && m_user != username) {
156− m_user = username;
193+ m_user = username;
157− syncFromAccounts();
194+ syncFromAccounts();
195+ }
158}
196}
159
197
160void LauncherBackend::triggerQuickListAction(const QString &appId, const QString &quickListId)
198void LauncherBackend::triggerQuickListAction(const QString &appId, const QString &quickListId)
@@ -169,75 +207,140 @@ void LauncherBackend::syncFromAccounts()
169 QList<QVariantMap> apps;
207 QList<QVariantMap> apps;
170 bool defaults = true;
208 bool defaults = true;
171
209
172− clearItems();
210+ m_storedApps.clear();
173
211
174− if (m_user != "" && m_accounts != nullptr) {
212+ if (m_accounts && !m_user.isEmpty()) {
175− auto variant = m_accounts->getUserProperty(m_user, "launcher-items");
213+ QVariant variant = m_accounts->getUserProperty(m_user, "com.canonical.unity.AccountsService", "launcher-items");
176 apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
214 apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
177 defaults = isDefaultsItem(apps);
215 defaults = isDefaultsItem(apps);
178 }
216 }
179
217
180− // TODO: load default pinned ones from default config, instead of hardcoding here...
218+ if (m_accounts && defaults) { // Checking accounts as it'll be null when !useStorage
181− if (defaults) {
219+ QGSettings gSettings("com.canonical.Unity.Launcher", "/com/canonical/unity/launcher/");
182− apps <<
220+ Q_FOREACH(const QString &entry, gSettings.get("favorites").toStringList()) {
183− makeAppDetails("dialer-app.desktop", true) <<
221+ if (entry.startsWith("application://")) {
184− makeAppDetails("messaging-app.desktop", true) <<
222+ QString appId = entry;
185− makeAppDetails("address-book-app.desktop", true) <<
223+ // Transform "application://foobar.desktop" to "foobar"
186− makeAppDetails("camera-app.desktop", true) <<
224+ appId.remove("application://");
187− makeAppDetails("gallery-app.desktop", true) <<
225+ if (appId.endsWith(".desktop")) {
188− makeAppDetails("facebook-webapp.desktop", true) <<
226+ appId.chop(8);
189− makeAppDetails("webbrowser-app.desktop", true) <<
227+ }
190− makeAppDetails("twitter-webapp.desktop", true) <<
228+ QString df = findDesktopFile(appId);
191− makeAppDetails("gmail-webapp.desktop", true) <<
229+
192− makeAppDetails("ubuntu-weather-app.desktop", true) <<
230+ if (!df.isEmpty()) {
193− makeAppDetails("notes-app.desktop", true) <<
231+ m_storedApps << appId;
194− makeAppDetails("calendar-app.desktop", true);
232+
195− }
233+ if (!m_itemCache.contains(appId)) {
196−
234+ m_itemCache.insert(appId, parseDesktopFile(df));
235+ }
236+ m_itemCache.value(appId)->pinned = true;
237+ }
238+ }
239+ }
240+ } else {
197− for (const QVariant &app: apps) {
241+ for (const QVariant &app: apps) {
198− loadApp(app.toMap());
242+ loadFromVariant(app.toMap());
243+ }
199 }
244 }
200}
245}
201
246
202void LauncherBackend::syncToAccounts()
247void LauncherBackend::syncToAccounts()
203{
248{
204− if (m_user != "" && m_accounts != nullptr) {
249+ if (m_accounts && !m_user.isEmpty()) {
205 QList<QVariantMap> items;
250 QList<QVariantMap> items;
206
251
207− for (LauncherBackendItem &app: m_storedApps) {
252+ Q_FOREACH(const QString &appId, m_storedApps) {
208− items << makeAppDetails(app.settings->fileName(), app.pinned);
253+ items << itemToVariant(appId);
209 }
254 }
210
255
211− m_accounts->setUserProperty(m_user, "launcher-items", QVariant::fromValue(items));
256+ m_accounts->setUserProperty(m_user, "com.canonical.unity.AccountsService", "launcher-items", QVariant::fromValue(items));
212 }
257 }
213}
258}
214
259
215−QSettings *LauncherBackend::parseDesktopFile(const QString &appId) const
260+QString LauncherBackend::findDesktopFile(const QString &appId) const
216{
261{
217− auto fullAppId = desktopFile(appId);
262+ int dashPos = -1;
218− return new QSettings(fullAppId, QSettings::IniFormat);
263+ QString helper = appId;
264+
265+ QStringList searchDirs;
266+ searchDirs << QDir::homePath() + "/.local/share/applications";
267+ searchDirs << "/usr/share/applications";
268+
269+#ifdef LAUNCHER_TESTING
270+ searchDirs << "";
271+#endif
272+
273+ do {
274+ if (dashPos != -1) {
275+ helper = helper.replace(dashPos, 1, '/');
276+ }
277+
278+ Q_FOREACH(const QString &searchDir, searchDirs) {
279+ QFileInfo fileInfo(QDir(searchDir), helper + ".desktop");
280+ if (fileInfo.exists()) {
281+ return fileInfo.absoluteFilePath();
282+ }
283+ }
284+
285+ dashPos = helper.indexOf("-");
286+ } while (dashPos != -1);
287+
288+ return QString();
219}
289}
220
290
221−void LauncherBackend::loadApp(const QVariantMap &details)
291+LauncherBackendItem* LauncherBackend::parseDesktopFile(const QString &desktopFile) const
222{
292{
223− auto appId = details.value("id").toString();
293+ QSettings settings(desktopFile, QSettings::IniFormat);
224− auto isPinned = details.value("is-pinned").toBool();
294+
295+ LauncherBackendItem* item = new LauncherBackendItem();
296+ item->desktopFile = desktopFile;
297+ item->displayName = settings.value("Desktop Entry/Name").toString();
298+
299+ QString iconString = settings.value("Desktop Entry/Icon").toString();
300+ QString pathString = settings.value("Desktop Entry/Path").toString();
301+ if (QFileInfo(iconString).exists()) {
302+ item->icon = QFileInfo(iconString).absoluteFilePath();
303+ } else if (QFileInfo(pathString + '/' + iconString).exists()) {
304+ item->icon = pathString + '/' + iconString;
305+ } else {
306+ item->icon = "image://theme/" + iconString;
307+ }
308+ item->pinned = false;
309+ return item;
310+}
225
311
226− if (appId.isEmpty()) {
312+void LauncherBackend::loadFromVariant(const QVariantMap &details)
313+{
314+ if (!details.contains("id")) {
227 return;
315 return;
228 }
316 }
317+ QString appId = details.value("id").toString();
318+
319+ LauncherBackendItem *item = m_itemCache.value(appId);
320+ if (item) {
321+ delete item;
322+ }
323+
324+ item = new LauncherBackendItem();
325+
326+ item->desktopFile = details.value("desktopFile").toString();
327+ item->displayName = details.value("name").toString();
328+ item->icon = details.value("icon").toString();
329+ item->pinned = details.value("is-pinned").toBool();
229
330
230− LauncherBackendItem item;
331+ m_itemCache.insert(appId, item);
231− item.settings = parseDesktopFile(appId);
232− item.pinned = isPinned;
233− m_storedApps.append(item);
332+ m_storedApps.append(appId);
234}
333}
235
334
236−QVariantMap LauncherBackend::makeAppDetails(const QString &appId, bool pinned) const
335+QVariantMap LauncherBackend::itemToVariant(const QString &appId) const
237{
336{
337+ LauncherBackendItem *item = m_itemCache.value(appId);
238 QVariantMap details;
338 QVariantMap details;
239 details.insert("id", appId);
339 details.insert("id", appId);
340+ details.insert("name", item->displayName);
341+ details.insert("icon", item->icon);
342+ details.insert("desktopFile", item->desktopFile);
240− details.insert("is-pinned", pinned);
343+ details.insert("is-pinned", item->pinned);
241 return details;
344 return details;
242}
345}
243
346
@@ -248,14 +351,3 @@ bool LauncherBackend::isDefaultsItem(const QList<QVariantMap> &apps) const
248 // list of one item with the 'defaults' field set to true.
351 // list of one item with the 'defaults' field set to true.
249 return (apps.size() == 1 && apps[0].value("defaults").toBool());
352 return (apps.size() == 1 && apps[0].value("defaults").toBool());
250}
353}
251−
252−int LauncherBackend::findItem(const QString &appId) const
253−{
254− auto fullAppId = desktopFile(appId);
255− for (int i = 0; i < m_storedApps.size(); ++i) {
256− if (m_storedApps[i].settings->fileName() == fullAppId) {
257− return i;
258− }
259− }
260− return -1;
261−}
plugins/Unity/Launcher/backend/launcherbackend.h+12−14
@@ -25,12 +25,14 @@
25#include <QSettings>
25#include <QSettings>
26#include <QStringList>
26#include <QStringList>
27
27
28−class AccountsService;
28+class AccountsServiceDBusAdaptor;
29
29
30/**
30/**
31 * @brief An interface that provides all the data needed by the launcher.
31 * @brief An interface that provides all the data needed by the launcher.
32 */
32 */
33
33
34+class LauncherBackendItem;
35+
34class LauncherBackend : public QObject
36class LauncherBackend : public QObject
35{
37{
36 Q_OBJECT
38 Q_OBJECT
@@ -141,24 +143,20 @@ Q_SIGNALS:
141 void countChanged(const QString &appId, int count);
143 void countChanged(const QString &appId, int count);
142
144
143private:
145private:
146+ QString findDesktopFile(const QString &appId) const;
144− QSettings *parseDesktopFile(const QString &appId) const;
147+ LauncherBackendItem* parseDesktopFile(const QString &desktopFile) const;
148+
145− QVariantMap makeAppDetails(const QString &appId, bool pinned) const;
149+ QVariantMap itemToVariant(const QString &appId) const;
146− void loadApp(const QVariantMap &details);
150+ void loadFromVariant(const QVariantMap &details);
147− int findItem(const QString &appId) const;
151+
148 bool isDefaultsItem(const QList<QVariantMap> &apps) const;
152 bool isDefaultsItem(const QList<QVariantMap> &apps) const;
149 void syncFromAccounts();
153 void syncFromAccounts();
150 void syncToAccounts();
154 void syncToAccounts();
151− void clearItems();
152
155
156+ QList<QString> m_storedApps;
153− class LauncherBackendItem
157+ mutable QHash<QString, LauncherBackendItem*> m_itemCache;
154− {
155− public:
156− QSettings *settings;
157− bool pinned;
158− };
159
158
160− QList<LauncherBackendItem> m_storedApps;
159+ AccountsServiceDBusAdaptor *m_accounts;
161− AccountsService *m_accounts;
162 QString m_user;
160 QString m_user;
163};
161};
164
162
plugins/Unity/Launcher/launcheritem.cpp+15−7
@@ -20,10 +20,9 @@
20#include "launcheritem.h"
20#include "launcheritem.h"
21#include "quicklistmodel.h"
21#include "quicklistmodel.h"
22
22
23−LauncherItem::LauncherItem(const QString &appId, const QString &desktopFile, const QString &name, const QString &icon, QObject *parent) :
23+LauncherItem::LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent) :
24 LauncherItemInterface(parent),
24 LauncherItemInterface(parent),
25 m_appId(appId),
25 m_appId(appId),
26− m_desktopFile(desktopFile),
27 m_name(name),
26 m_name(name),
28 m_icon(icon),
27 m_icon(icon),
29 m_pinned(false),
28 m_pinned(false),
@@ -31,6 +30,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &desktopFile, con
31 m_recent(false),
30 m_recent(false),
32 m_progress(-1),
31 m_progress(-1),
33 m_count(0),
32 m_count(0),
33+ m_focused(false),
34 m_quickList(new QuickListModel(this))
34 m_quickList(new QuickListModel(this))
35{
35{
36 QuickListEntry nameAction;
36 QuickListEntry nameAction;
@@ -47,11 +47,6 @@ QString LauncherItem::appId() const
47 return m_appId;
47 return m_appId;
48}
48}
49
49
50−QString LauncherItem::desktopFile() const
51−{
52− return m_desktopFile;
53−}
54−
55QString LauncherItem::name() const
50QString LauncherItem::name() const
56{
51{
57 return m_name;
52 return m_name;
@@ -131,6 +126,19 @@ void LauncherItem::setCount(int count)
131 }
126 }
132}
127}
133
128
129+bool LauncherItem::focused() const
130+{
131+ return m_focused;
132+}
133+
134+void LauncherItem::setFocused(bool focused)
135+{
136+ if (m_focused != focused) {
137+ m_focused = focused;
138+ Q_EMIT focusedChanged(focused);
139+ }
140+}
141+
134unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
142unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
135{
143{
136 return m_quickList;
144 return m_quickList;
plugins/Unity/Launcher/launcheritem.h+5−3
@@ -32,10 +32,9 @@ class LauncherItem: public LauncherItemInterface
32{
32{
33 Q_OBJECT
33 Q_OBJECT
34public:
34public:
35− LauncherItem(const QString &appId, const QString &desktopFile, const QString &name, const QString &icon, QObject *parent = 0);
35+ LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent = 0);
36
36
37 QString appId() const;
37 QString appId() const;
38− QString desktopFile() const;
39 QString name() const;
38 QString name() const;
40 QString icon() const;
39 QString icon() const;
41
40
@@ -54,6 +53,9 @@ public:
54 int count() const;
53 int count() const;
55 void setCount(int count);
54 void setCount(int count);
56
55
56+ bool focused() const;
57+ void setFocused(bool focused);
58+
57 unity::shell::launcher::QuickListModelInterface *quickList() const;
59 unity::shell::launcher::QuickListModelInterface *quickList() const;
58
60
59Q_SIGNALS:
61Q_SIGNALS:
@@ -62,7 +64,6 @@ Q_SIGNALS:
62
64
63private:
65private:
64 QString m_appId;
66 QString m_appId;
65− QString m_desktopFile;
66 QString m_name;
67 QString m_name;
67 QString m_icon;
68 QString m_icon;
68 bool m_pinned;
69 bool m_pinned;
@@ -70,6 +71,7 @@ private:
70 bool m_recent;
71 bool m_recent;
71 int m_progress;
72 int m_progress;
72 int m_count;
73 int m_count;
74+ bool m_focused;
73 QuickListModel *m_quickList;
75 QuickListModel *m_quickList;
74};
76};
75
77
plugins/Unity/Launcher/launchermodel.cpp+126−10
@@ -21,16 +21,20 @@
21#include "launcheritem.h"
21#include "launcheritem.h"
22#include "backend/launcherbackend.h"
22#include "backend/launcherbackend.h"
23
23
24+#include <unity/shell/application/ApplicationInfoInterface.h>
25+
26+using namespace unity::shell::application;
27+
24LauncherModel::LauncherModel(QObject *parent):
28LauncherModel::LauncherModel(QObject *parent):
25 LauncherModelInterface(parent),
29 LauncherModelInterface(parent),
26− m_backend(new LauncherBackend(true, this))
30+ m_backend(new LauncherBackend(true, this)),
31+ m_appManager(0)
27{
32{
28 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
33 connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
29 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
34 connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
30
35
31 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
36 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
32 LauncherItem *item = new LauncherItem(entry,
37 LauncherItem *item = new LauncherItem(entry,
33− m_backend->desktopFile(entry),
34 m_backend->displayName(entry),
38 m_backend->displayName(entry),
35 m_backend->icon(entry),
39 m_backend->icon(entry),
36 this);
40 this);
@@ -61,9 +65,7 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
61 LauncherItem *item = m_list.at(index.row());
65 LauncherItem *item = m_list.at(index.row());
62 switch(role) {
66 switch(role) {
63 case RoleAppId:
67 case RoleAppId:
64− return item->desktopFile();
68+ return item->appId();
65− case RoleDesktopFile:
66− return item->desktopFile();
67 case RoleName:
69 case RoleName:
68 return item->name();
70 return item->name();
69 case RoleIcon:
71 case RoleIcon:
@@ -74,6 +76,8 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
74 return item->count();
76 return item->count();
75 case RoleProgress:
77 case RoleProgress:
76 return item->progress();
78 return item->progress();
79+ case RoleFocused:
80+ return item->focused();
77 }
81 }
78
82
79 return QVariant();
83 return QVariant();
@@ -111,9 +115,11 @@ void LauncherModel::move(int oldIndex, int newIndex)
111 m_list.move(oldIndex, newIndex);
115 m_list.move(oldIndex, newIndex);
112 endMoveRows();
116 endMoveRows();
113
117
114− storeAppList();
118+ if (!m_list.at(newIndex)->pinned()) {
115−
116− pin(m_list.at(newIndex)->appId());
119+ pin(m_list.at(newIndex)->appId());
120+ } else {
121+ storeAppList();
122+ }
117}
123}
118
124
119void LauncherModel::pin(const QString &appId, int index)
125void LauncherModel::pin(const QString &appId, int index)
@@ -135,7 +141,6 @@ void LauncherModel::pin(const QString &appId, int index)
135 }
141 }
136 beginInsertRows(QModelIndex(), index, index);
142 beginInsertRows(QModelIndex(), index, index);
137 LauncherItem *item = new LauncherItem(appId,
143 LauncherItem *item = new LauncherItem(appId,
138− m_backend->desktopFile(appId),
139 m_backend->displayName(appId),
144 m_backend->displayName(appId),
140 m_backend->icon(appId));
145 m_backend->icon(appId));
141 item->setPinned(true);
146 item->setPinned(true);
@@ -143,6 +148,8 @@ void LauncherModel::pin(const QString &appId, int index)
143 m_list.insert(index, item);
148 m_list.insert(index, item);
144 endInsertRows();
149 endInsertRows();
145 }
150 }
151+
152+ storeAppList();
146}
153}
147
154
148void LauncherModel::requestRemove(const QString &appId)
155void LauncherModel::requestRemove(const QString &appId)
@@ -152,6 +159,11 @@ void LauncherModel::requestRemove(const QString &appId)
152 return;
159 return;
153 }
160 }
154
161
162+ if (m_appManager->findApplication(appId)) {
163+ m_list.at(index)->setPinned(false);
164+ return;
165+ }
166+
155 beginRemoveRows(QModelIndex(), index, index);
167 beginRemoveRows(QModelIndex(), index, index);
156 m_list.takeAt(index)->deleteLater();
168 m_list.takeAt(index)->deleteLater();
157 endRemoveRows();
169 endRemoveRows();
@@ -191,11 +203,55 @@ void LauncherModel::setUser(const QString &username)
191 m_backend->setUser(username);
203 m_backend->setUser(username);
192}
204}
193
205
206+ApplicationManagerInterface *LauncherModel::applicationManager() const
207+{
208+ return m_appManager;
209+}
210+
211+void LauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager)
212+{
213+ // Is there already another appmanager set?
214+ if (m_appManager) {
215+ // Disconnect any signals
216+ disconnect(this, SLOT(applicationAdded(QModelIndex,int)));
217+ disconnect(this, SLOT(applicationRemoved(QModelIndex,int)));
218+ disconnect(this, SLOT(focusedAppIdChanged()));
219+
220+ // remove any recent/running apps from the launcher
221+ QList<int> recentAppIndices;
222+ for (int i = 0; i < m_list.count(); ++i) {
223+ if (m_list.at(i)->recent()) {
224+ recentAppIndices << i;
225+ }
226+ }
227+ int run = 0;
228+ while (recentAppIndices.count() > 0) {
229+ beginRemoveRows(QModelIndex(), recentAppIndices.first() - run, recentAppIndices.first() - run);
230+ m_list.takeAt(recentAppIndices.first() - run)->deleteLater();
231+ endRemoveRows();
232+ recentAppIndices.takeFirst();
233+ ++run;
234+ }
235+ }
236+
237+ m_appManager = appManager;
238+ connect(m_appManager, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(applicationAdded(QModelIndex,int)));
239+ connect(m_appManager, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SLOT(applicationRemoved(QModelIndex,int)));
240+ connect(m_appManager, SIGNAL(focusedApplicationIdChanged()), SLOT(focusedAppIdChanged()));
241+
242+ Q_EMIT applicationManagerChanged();
243+
244+ for (int i = 0; i < appManager->count(); ++i) {
245+ applicationAdded(QModelIndex(), i);
246+ }
247+}
248+
249+
194void LauncherModel::storeAppList()
250void LauncherModel::storeAppList()
195{
251{
196 QStringList appIds;
252 QStringList appIds;
197 Q_FOREACH(LauncherItem *item, m_list) {
253 Q_FOREACH(LauncherItem *item, m_list) {
198− if (item->pinned() || item->recent()) {
254+ if (item->pinned()) {
199 appIds << item->appId();
255 appIds << item->appId();
200 }
256 }
201 }
257 }
@@ -233,3 +289,63 @@ void LauncherModel::countChanged(const QString &appId, int count)
233 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
289 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
234 }
290 }
235}
291}
292+
293+void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
294+{
295+ Q_UNUSED(parent);
296+
297+ ApplicationInfoInterface *app = m_appManager->get(row);
298+
299+ bool found = false;
300+ Q_FOREACH(LauncherItem *item, m_list) {
301+ if (app->appId() == item->appId()) {
302+ found = true;
303+ break;
304+ }
305+ }
306+ if (found) {
307+ // Shall we paint some running/recent app highlight? If yes, do it here.
308+ } else {
309+ LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString());
310+ item->setRecent(true);
311+ item->setFocused(app->focused());
312+
313+ beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
314+ m_list.append(item);
315+ endInsertRows();
316+ }
317+}
318+
319+void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
320+{
321+ Q_UNUSED(parent)
322+
323+ int appIndex = -1;
324+ for (int i = 0; i < m_list.count(); ++i) {
325+ if (m_list.at(i)->appId() == m_appManager->get(row)->appId()) {
326+ appIndex = i;
327+ break;
328+ }
329+ }
330+
331+ if (appIndex > -1 && !m_list.at(appIndex)->pinned()) {
332+ beginRemoveRows(QModelIndex(), appIndex, appIndex);
333+ m_list.takeAt(appIndex)->deleteLater();
334+ endRemoveRows();
335+ }
336+}
337+
338+void LauncherModel::focusedAppIdChanged()
339+{
340+ QString appId = m_appManager->focusedApplicationId();
341+ for (int i = 0; i < m_list.count(); ++i) {
342+ LauncherItem *item = m_list.at(i);
343+ if (!item->focused() && item->appId() == appId) {
344+ item->setFocused(true);
345+ Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
346+ } else if (item->focused() && item->appId() != appId) {
347+ item->setFocused(false);
348+ Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
349+ }
350+ }
351+}
plugins/Unity/Launcher/launchermodel.h+10
@@ -22,6 +22,7 @@
22
22
23// unity-api
23// unity-api
24#include <unity/shell/launcher/LauncherModelInterface.h>
24#include <unity/shell/launcher/LauncherModelInterface.h>
25+#include <unity/shell/application/ApplicationManagerInterface.h>
25
26
26// Qt
27// Qt
27#include <QAbstractListModel>
28#include <QAbstractListModel>
@@ -30,6 +31,7 @@ class LauncherItem;
30class LauncherBackend;
31class LauncherBackend;
31
32
32using namespace unity::shell::launcher;
33using namespace unity::shell::launcher;
34+using namespace unity::shell::application;
33
35
34class LauncherModel: public LauncherModelInterface
36class LauncherModel: public LauncherModelInterface
35{
37{
@@ -50,6 +52,9 @@ public:
50 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
52 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
51 Q_INVOKABLE void setUser(const QString &username);
53 Q_INVOKABLE void setUser(const QString &username);
52
54
55+ unity::shell::application::ApplicationManagerInterface* applicationManager() const;
56+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
57+
53private:
58private:
54 void storeAppList();
59 void storeAppList();
55 int findApplication(const QString &appId);
60 int findApplication(const QString &appId);
@@ -58,9 +63,14 @@ private Q_SLOTS:
58 void progressChanged(const QString &appId, int progress);
63 void progressChanged(const QString &appId, int progress);
59 void countChanged(const QString &appId, int count);
64 void countChanged(const QString &appId, int count);
60
65
66+ void applicationAdded(const QModelIndex &parent, int row);
67+ void applicationRemoved(const QModelIndex &parent, int row);
68+ void focusedAppIdChanged();
69+
61private:
70private:
62 QList<LauncherItem*> m_list;
71 QList<LauncherItem*> m_list;
63 LauncherBackend *m_backend;
72 LauncherBackend *m_backend;
73+ ApplicationManagerInterface *m_appManager;
64};
74};
65
75
66#endif // LAUNCHERMODEL_H
76#endif // LAUNCHERMODEL_H
plugins/Unity/applicationpreview.cpp+5−1
@@ -19,6 +19,7 @@
19
19
20// local
20// local
21#include "applicationpreview.h"
21#include "applicationpreview.h"
22+#include "iconutils.h"
22
23
23#include <QDebug>
24#include <QDebug>
24
25
@@ -61,7 +62,10 @@ QString ApplicationPreview::license() const
61QString ApplicationPreview::appIcon() const
62QString ApplicationPreview::appIcon() const
62{
63{
63 if (m_unityAppPreview) {
64 if (m_unityAppPreview) {
64− return QString::fromUtf8(g_icon_to_string(m_unityAppPreview->app_icon()));
65+ auto giconString = g_icon_to_string(m_unityAppPreview->app_icon());
66+ QString result(gIconToDeclarativeImageProviderString(QString::fromUtf8(giconString)));
67+ g_free(giconString);
68+ return result;
65 } else {
69 } else {
66 qWarning() << "Preview not set";
70 qWarning() << "Preview not set";
67 }
71 }
plugins/Unity/categories.cpp−3
@@ -43,7 +43,6 @@ Categories::Categories(QObject* parent)
43 m_roles[Categories::RoleHints] = "hints";
43 m_roles[Categories::RoleHints] = "hints";
44 m_roles[Categories::RoleResults] = "results";
44 m_roles[Categories::RoleResults] = "results";
45 m_roles[Categories::RoleCount] = "count";
45 m_roles[Categories::RoleCount] = "count";
46− m_roles[Categories::RoleCategoryIndex] = "categoryIndex";
47
46
48 // TODO This should not be needed but accumulatting the count changes
47 // TODO This should not be needed but accumulatting the count changes
49 // makes the visualization more stable and also makes crashes on fast
48 // makes the visualization more stable and also makes crashes on fast
@@ -222,8 +221,6 @@ Categories::data(const QModelIndex& index, int role) const
222 return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
221 return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
223 }
222 }
224 return QVariant::fromValue(getResults(index.row())->rowCount());
223 return QVariant::fromValue(getResults(index.row())->rowCount());
225− case RoleCategoryIndex:
226− return QVariant::fromValue(index.row());
227 default:
224 default:
228 return QVariant();
225 return QVariant();
229 }
226 }
plugins/Unity/categories.h+1−2
@@ -50,8 +50,7 @@ public:
50 RoleProgressSource,
50 RoleProgressSource,
51 RoleHints,
51 RoleHints,
52 RoleResults,
52 RoleResults,
53− RoleCount,
53+ RoleCount
54− RoleCategoryIndex
55 };
54 };
56
55
57 Q_INVOKABLE void overrideResults(const QString& categoryId, QAbstractItemModel* model);
56 Q_INVOKABLE void overrideResults(const QString& categoryId, QAbstractItemModel* model);
plugins/Unity/categoryresults.cpp+5−2
@@ -19,6 +19,7 @@
19
19
20// self
20// self
21#include "categoryresults.h"
21#include "categoryresults.h"
22+#include "iconutils.h"
22
23
23// TODO: use something from libunity once it's public
24// TODO: use something from libunity once it's public
24enum ResultsColumn {
25enum ResultsColumn {
@@ -76,8 +77,10 @@ CategoryResults::data(const QModelIndex& index, int role) const
76 switch (role) {
77 switch (role) {
77 case RoleUri:
78 case RoleUri:
78 return DeeListModel::data(index, ResultsColumn::URI);
79 return DeeListModel::data(index, ResultsColumn::URI);
79− case RoleIconHint:
80+ case RoleIconHint: {
80− return DeeListModel::data(index, ResultsColumn::ICON_HINT);
81+ QString giconString(DeeListModel::data(index, ResultsColumn::ICON_HINT).toString());
82+ return QVariant::fromValue(gIconToDeclarativeImageProviderString(giconString));
83+ }
81 case RoleCategory:
84 case RoleCategory:
82 return DeeListModel::data(index, ResultsColumn::CATEGORY);
85 return DeeListModel::data(index, ResultsColumn::CATEGORY);
83 case RoleMimetype:
86 case RoleMimetype:
plugins/Unity/categoryresults.h+1−1
@@ -24,7 +24,7 @@
24// dee-qt
24// dee-qt
25#include "deelistmodel.h"
25#include "deelistmodel.h"
26
26
27−class CategoryResults : public DeeListModel
27+class Q_DECL_EXPORT CategoryResults : public DeeListModel
28{
28{
29 Q_OBJECT
29 Q_OBJECT
30
30
plugins/Unity/iconutils.cppnew file+91
@@ -0,0 +1,91 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Michal Hruby <michal.hruby@canonical.com>
18+ */
19+
20+#include <QStringList>
21+#include <QUrl>
22+
23+#include <gio/gio.h>
24+#include "iconutils.h"
25+
26+#define BASE_THEME_ICON_URI "image://theme/"
27+
28+QString gIconToDeclarativeImageProviderString(QString const &giconString)
29+{
30+ if (giconString.isEmpty()) return giconString;
31+
32+ if (giconString.startsWith('/') || giconString.startsWith(QLatin1String("http")) ||
33+ giconString.startsWith(QLatin1String("file:"))) {
34+ return giconString;
35+ }
36+
37+ if (!giconString.startsWith(QLatin1String(". "))) {
38+ // must be a themed icon
39+ QString themedIcon(BASE_THEME_ICON_URI);
40+ themedIcon.append(giconString);
41+ return themedIcon;
42+ }
43+
44+ // special case annotated icon
45+ if (giconString.startsWith(QLatin1String(". UnityProtocolAnnotatedIcon "))) {
46+ QString annotatedIcon;
47+ QString serializedData(QUrl::fromPercentEncoding(giconString.mid(29).toUtf8()));
48+ GVariant *variant = g_variant_parse(G_VARIANT_TYPE_VARDICT, serializedData.toUtf8().constData(), NULL, NULL, NULL);
49+ gchar *baseUri;
50+ if (variant != NULL && g_variant_lookup(variant, "base-icon", "&s", &baseUri)) {
51+ annotatedIcon = gIconToDeclarativeImageProviderString(QString(baseUri));
52+ // FIXME: enclose in image://annotated/... once unity supports that
53+ }
54+ if (variant != NULL) g_variant_unref(variant);
55+
56+ return annotatedIcon;
57+ }
58+
59+ // handle real gicon strings
60+ QString result;
61+ GError *error = NULL;
62+ GIcon *icon = g_icon_new_for_string(giconString.toLocal8Bit().constData(), &error);
63+ if (error != NULL || icon == NULL) {
64+ qWarning("Unable to deserialize icon: %s", giconString.toLocal8Bit().constData());
65+ g_clear_error(&error);
66+ return result;
67+ }
68+
69+ if (G_IS_THEMED_ICON(icon)) {
70+ QString themedIcon(BASE_THEME_ICON_URI);
71+ QStringList list;
72+ const char* const *iconNames = g_themed_icon_get_names(G_THEMED_ICON(icon));
73+ if (iconNames != NULL) {
74+ for (const char * const *iter = iconNames; *iter != NULL; ++iter) {
75+ list << QLatin1String(*iter);
76+ }
77+ }
78+ themedIcon.append(list.join(QString(",")));
79+ result = themedIcon;
80+ } else if (G_IS_FILE_ICON(icon)) {
81+ GFile *file = g_file_icon_get_file(G_FILE_ICON(icon));
82+ gchar *uri = g_file_get_uri(file);
83+ QString iconUri(uri);
84+ g_free(uri);
85+ result = iconUri;
86+ }
87+
88+ g_object_unref(icon);
89+
90+ return result;
91+}
plugins/Unity/iconutils.hnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Michal Hruby <michal.hruby@canonical.com>
18+ */
19+
20+#ifndef ICONUTILS_H
21+#define ICONUTILS_H
22+
23+#include <QString>
24+
25+QString gIconToDeclarativeImageProviderString(QString const &giconString);
26+
27+#endif
plugins/Unity/preview.cpp+5−1
@@ -26,6 +26,7 @@
26#include "moviepreview.h"
26#include "moviepreview.h"
27#include "musicpreview.h"
27#include "musicpreview.h"
28#include "socialpreview.h"
28#include "socialpreview.h"
29+#include "iconutils.h"
29#include "variantutils.h"
30#include "variantutils.h"
30
31
31// Qt
32// Qt
@@ -102,7 +103,10 @@ QVariantMap Preview::infoHintsHash() const
102QString Preview::image() const
103QString Preview::image() const
103{
104{
104 if (m_unityPreview) {
105 if (m_unityPreview) {
105− return QString::fromUtf8(g_icon_to_string(m_unityPreview->image()));
106+ auto giconString = g_icon_to_string(m_unityPreview->image());
107+ QString result(gIconToDeclarativeImageProviderString(QString::fromUtf8(giconString)));
108+ g_free(giconString);
109+ return result;
106 } else {
110 } else {
107 qWarning() << "Preview not set";
111 qWarning() << "Preview not set";
108 }
112 }
plugins/Unity/socialpreview.cpp+5−1
@@ -19,6 +19,7 @@
19
19
20#include "socialpreview.h"
20#include "socialpreview.h"
21#include "socialpreviewcomment.h"
21#include "socialpreviewcomment.h"
22+#include "iconutils.h"
22
23
23#include <QDebug>
24#include <QDebug>
24
25
@@ -51,7 +52,10 @@ QString SocialPreview::content() const
51QString SocialPreview::avatar() const
52QString SocialPreview::avatar() const
52{
53{
53 if (m_unitySocialPreview) {
54 if (m_unitySocialPreview) {
54− return QString::fromUtf8(g_icon_to_string(m_unitySocialPreview->avatar()));
55+ auto giconString = g_icon_to_string(m_unitySocialPreview->avatar());
56+ QString result(gIconToDeclarativeImageProviderString(QString::fromUtf8(giconString)));
57+ g_free(giconString);
58+ return result;
55 } else {
59 } else {
56 qWarning() << "Preview not set";
60 qWarning() << "Preview not set";
57 }
61 }
plugins/Utils/qsortfilterproxymodelqml.cpp+32
@@ -29,6 +29,17 @@ QSortFilterProxyModelQML::QSortFilterProxyModelQML(QObject *parent)
29 connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(countChanged()));
29 connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(countChanged()));
30}
30}
31
31
32+/*
33+ * Enter row index of filtered/sorted model, returns row index of source model
34+ */
35+int QSortFilterProxyModelQML::mapRowToSource(int row)
36+{
37+ if (sourceModel() == NULL)
38+ return -1;
39+
40+ return QSortFilterProxyModel::mapToSource(index(row, 0)).row();
41+}
42+
32QHash<int, QByteArray> QSortFilterProxyModelQML::roleNames() const
43QHash<int, QByteArray> QSortFilterProxyModelQML::roleNames() const
33{
44{
34 return sourceModel() ? sourceModel()->roleNames() : QHash<int, QByteArray>();
45 return sourceModel() ? sourceModel()->roleNames() : QHash<int, QByteArray>();
@@ -51,6 +62,7 @@ QSortFilterProxyModelQML::setModel(QAbstractItemModel *itemModel)
51 connect(itemModel, SIGNAL(modelReset()), SIGNAL(totalCountChanged()));
62 connect(itemModel, SIGNAL(modelReset()), SIGNAL(totalCountChanged()));
52 connect(itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
63 connect(itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
53 connect(itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
64 connect(itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
65+
54 Q_EMIT totalCountChanged();
66 Q_EMIT totalCountChanged();
55 Q_EMIT modelChanged();
67 Q_EMIT modelChanged();
56 }
68 }
@@ -121,3 +133,23 @@ QSortFilterProxyModelQML::findFirst(int role, const QVariant& value) const
121 return -1;
133 return -1;
122 }
134 }
123}
135}
136+
137+int
138+QSortFilterProxyModelQML::mapFromSource(int row)
139+{
140+ if (sourceModel() != NULL) {
141+ return QSortFilterProxyModel::mapFromSource(sourceModel()->index(row, 0)).row();
142+ } else {
143+ return -1;
144+ }
145+}
146+
147+int
148+QSortFilterProxyModelQML::mapToSource(int row)
149+{
150+ if (sourceModel() != NULL) {
151+ return QSortFilterProxyModel::mapToSource(index(row, 0)).row();
152+ } else {
153+ return -1;
154+ }
155+}
plugins/Utils/qsortfilterproxymodelqml.h+4
@@ -34,6 +34,7 @@ public:
34 Q_INVOKABLE QVariant data(int row, int role);
34 Q_INVOKABLE QVariant data(int row, int role);
35 Q_INVOKABLE int count();
35 Q_INVOKABLE int count();
36 Q_INVOKABLE int findFirst(int role, const QVariant& value) const;
36 Q_INVOKABLE int findFirst(int role, const QVariant& value) const;
37+ Q_INVOKABLE int mapRowToSource(int row);
37 virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
38 virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
38
39
39 /* getters */
40 /* getters */
@@ -45,6 +46,9 @@ public:
45 void setModel(QAbstractItemModel *model);
46 void setModel(QAbstractItemModel *model);
46 void setInvertMatch(bool invertMatch);
47 void setInvertMatch(bool invertMatch);
47
48
49+ Q_INVOKABLE int mapFromSource(int row);
50+ Q_INVOKABLE int mapToSource(int row);
51+
48Q_SIGNALS:
52Q_SIGNALS:
49 void totalCountChanged();
53 void totalCountChanged();
50 void countChanged();
54 void countChanged();
po/ca.po+32−32
@@ -8,83 +8,83 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-08-27 13:36-0400\n"
10"POT-Creation-Date: 2013-08-27 13:36-0400\n"
11−"PO-Revision-Date: 2013-06-27 00:24+0000\n"
11+"PO-Revision-Date: 2013-09-11 10:30+0000\n"
12−"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12+"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
13"Language-Team: Catalan <ca@li.org>\n"
13"Language-Team: Catalan <ca@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-30 05:37+0000\n"
18+"X-Launchpad-Export-Date: 2013-09-12 08:06+0000\n"
19−"X-Generator: Launchpad (build 16750)\n"
19+"X-Generator: Launchpad (build 16761)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
23−msgstr ""
23+msgstr "Telèfon"
24
24
25#: Applications/applications.js:29
25#: Applications/applications.js:29
26msgid "Camera"
26msgid "Camera"
27−msgstr ""
27+msgstr "Càmera"
28
28
29#: Applications/applications.js:35
29#: Applications/applications.js:35
30msgid "Gallery"
30msgid "Gallery"
31−msgstr ""
31+msgstr "Galeria"
32
32
33#: Applications/applications.js:40
33#: Applications/applications.js:40
34msgid "Facebook"
34msgid "Facebook"
35−msgstr ""
35+msgstr "Facebook"
36
36
37#: Applications/applications.js:46
37#: Applications/applications.js:46
38msgid "Browser"
38msgid "Browser"
39−msgstr ""
39+msgstr "Navegador"
40
40
41#: Applications/applications.js:51
41#: Applications/applications.js:51
42msgid "Twitter"
42msgid "Twitter"
43−msgstr ""
43+msgstr "Twitter"
44
44
45#: Applications/applications.js:57
45#: Applications/applications.js:57
46msgid "GMail"
46msgid "GMail"
47−msgstr ""
47+msgstr "GMail"
48
48
49#: Applications/applications.js:62
49#: Applications/applications.js:62
50msgid "Weather"
50msgid "Weather"
51−msgstr ""
51+msgstr "El temps"
52
52
53#: Applications/applications.js:68
53#: Applications/applications.js:68
54msgid "Notepad"
54msgid "Notepad"
55−msgstr ""
55+msgstr "Bloc de notes"
56
56
57#: Applications/applications.js:74
57#: Applications/applications.js:74
58msgid "Calendar"
58msgid "Calendar"
59−msgstr ""
59+msgstr "Calendari"
60
60
61#: Applications/applications.js:80
61#: Applications/applications.js:80
62msgid "Media Player"
62msgid "Media Player"
63−msgstr ""
63+msgstr "Reproductor multimèdia"
64
64
65#: Applications/applications.js:86
65#: Applications/applications.js:86
66msgid "Evernote"
66msgid "Evernote"
67−msgstr ""
67+msgstr "Evernote"
68
68
69#: Applications/applications.js:91
69#: Applications/applications.js:91
70msgid "Map"
70msgid "Map"
71−msgstr ""
71+msgstr "Mapa"
72
72
73#: Applications/applications.js:96
73#: Applications/applications.js:96
74msgid "Pinterest"
74msgid "Pinterest"
75−msgstr ""
75+msgstr "Pinterest"
76
76
77#: Applications/applications.js:101
77#: Applications/applications.js:101
78msgid "SoundCloud"
78msgid "SoundCloud"
79−msgstr ""
79+msgstr "SoundCloud"
80
80
81#: Applications/applications.js:106
81#: Applications/applications.js:106
82msgid "Wikipedia"
82msgid "Wikipedia"
83−msgstr ""
83+msgstr "Viquipèdia"
84
84
85#: Applications/applications.js:111
85#: Applications/applications.js:111
86msgid "YouTube"
86msgid "YouTube"
87−msgstr ""
87+msgstr "YouTube"
88
88
89#: Components/EdgeDemoOverlay.qml:145
89#: Components/EdgeDemoOverlay.qml:145
90msgid "Skip intro"
90msgid "Skip intro"
@@ -92,7 +92,7 @@ msgstr ""
92
92
93#: Components/EdgeDemo.qml:98
93#: Components/EdgeDemo.qml:98
94msgid "Right edge"
94msgid "Right edge"
95−msgstr ""
95+msgstr "Vora dreta"
96
96
97#: Components/EdgeDemo.qml:99
97#: Components/EdgeDemo.qml:99
98msgid "Try swiping from the right edge to unlock the phone"
98msgid "Try swiping from the right edge to unlock the phone"
@@ -100,7 +100,7 @@ msgstr ""
100
100
101#: Components/EdgeDemo.qml:135
101#: Components/EdgeDemo.qml:135
102msgid "Top edge"
102msgid "Top edge"
103−msgstr ""
103+msgstr "Vora superior"
104
104
105#: Components/EdgeDemo.qml:136
105#: Components/EdgeDemo.qml:136
106msgid "Try swiping from the top edge to access the indicators"
106msgid "Try swiping from the top edge to access the indicators"
@@ -116,7 +116,7 @@ msgstr ""
116
116
117#: Components/EdgeDemo.qml:189
117#: Components/EdgeDemo.qml:189
118msgid "Left edge"
118msgid "Left edge"
119−msgstr ""
119+msgstr "Vora esquerra"
120
120
121#: Components/EdgeDemo.qml:190
121#: Components/EdgeDemo.qml:190
122msgid "Swipe from the left to reveal the launcher for quick access to apps"
122msgid "Swipe from the left to reveal the launcher for quick access to apps"
@@ -124,7 +124,7 @@ msgstr ""
124
124
125#: Components/EdgeDemo.qml:217
125#: Components/EdgeDemo.qml:217
126msgid "Well done"
126msgid "Well done"
127−msgstr ""
127+msgstr "Enhorabona!"
128
128
129#: Components/EdgeDemo.qml:218
129#: Components/EdgeDemo.qml:218
130msgid "You have now mastered the edge gestures and can start using the phone"
130msgid "You have now mastered the edge gestures and can start using the phone"
@@ -138,7 +138,7 @@ msgstr "Cerques recents"
138#: Dash/Apps/AppInfo.qml:73
138#: Dash/Apps/AppInfo.qml:73
139#, qt-format
139#, qt-format
140msgid "(%1)"
140msgid "(%1)"
141−msgstr ""
141+msgstr "(%1)"
142
142
143#. TRANSLATORS: Number of persons who wrote reviews for this app
143#. TRANSLATORS: Number of persons who wrote reviews for this app
144#: Dash/Apps/AppInfo.qml:85
144#: Dash/Apps/AppInfo.qml:85
@@ -158,7 +158,7 @@ msgstr ""
158
158
159#: Dash/Apps/AppReviews.qml:84
159#: Dash/Apps/AppReviews.qml:84
160msgid "Review"
160msgid "Review"
161−msgstr ""
161+msgstr "Ressenya"
162
162
163#: Dash/Apps/AppReviews.qml:118
163#: Dash/Apps/AppReviews.qml:118
164msgid "Send"
164msgid "Send"
@@ -166,15 +166,15 @@ msgstr ""
166
166
167#: Dash/Apps/AppReviews.qml:136
167#: Dash/Apps/AppReviews.qml:136
168msgid "Comments:"
168msgid "Comments:"
169−msgstr ""
169+msgstr "Comentaris:"
170
170
171#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
171#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
172msgid "Featured"
172msgid "Featured"
173−msgstr ""
173+msgstr "Destacats"
174
174
175#: Dash/DashMusic.qml:28 Dash/DashVideos.qml:29
175#: Dash/DashMusic.qml:28 Dash/DashVideos.qml:29
176msgid "Recent"
176msgid "Recent"
177−msgstr ""
177+msgstr "Recents"
178
178
179#: Dash/DashMusic.qml:29 Dash/DashVideos.qml:30
179#: Dash/DashMusic.qml:29 Dash/DashVideos.qml:30
180msgid "New Releases"
180msgid "New Releases"
@@ -206,7 +206,7 @@ msgstr ""
206
206
207#: Dash/Video/VideoPreview.qml:176
207#: Dash/Video/VideoPreview.qml:176
208msgid "Author:"
208msgid "Author:"
209−msgstr ""
209+msgstr "Autor:"
210
210
211#: Greeter/Lockscreen.qml:134
211#: Greeter/Lockscreen.qml:134
212msgid "Emergency Call"
212msgid "Emergency Call"
@@ -219,7 +219,7 @@ msgstr ""
219
219
220#: Greeter/PassphraseLockscreen.qml:59
220#: Greeter/PassphraseLockscreen.qml:59
221msgid "Hello"
221msgid "Hello"
222−msgstr ""
222+msgstr "Hola"
223
223
224#: Hud/HudParametrizedActionsPage.qml:132
224#: Hud/HudParametrizedActionsPage.qml:132
225msgid "Confirm"
225msgid "Confirm"
po/es.po+24−22
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-08-27 13:36-0400\n"
10"POT-Creation-Date: 2013-08-27 13:36-0400\n"
11−"PO-Revision-Date: 2013-07-08 07:25+0000\n"
11+"PO-Revision-Date: 2013-09-04 22:12+0000\n"
12"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
12"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
13"Language-Team: Spanish <es@li.org>\n"
13"Language-Team: Spanish <es@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-30 05:37+0000\n"
18+"X-Launchpad-Export-Date: 2013-09-05 05:35+0000\n"
19−"X-Generator: Launchpad (build 16750)\n"
19+"X-Generator: Launchpad (build 16758)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
@@ -88,47 +88,49 @@ msgstr "YouTube"
88
88
89#: Components/EdgeDemoOverlay.qml:145
89#: Components/EdgeDemoOverlay.qml:145
90msgid "Skip intro"
90msgid "Skip intro"
91−msgstr ""
91+msgstr "Omitir la introducción"
92
92
93#: Components/EdgeDemo.qml:98
93#: Components/EdgeDemo.qml:98
94msgid "Right edge"
94msgid "Right edge"
95−msgstr ""
95+msgstr "Borde derecho"
96
96
97#: Components/EdgeDemo.qml:99
97#: Components/EdgeDemo.qml:99
98msgid "Try swiping from the right edge to unlock the phone"
98msgid "Try swiping from the right edge to unlock the phone"
99−msgstr ""
99+msgstr "Deslice el dedo desde el borde derecho para desbloquear el teléfono"
100
100
101#: Components/EdgeDemo.qml:135
101#: Components/EdgeDemo.qml:135
102msgid "Top edge"
102msgid "Top edge"
103−msgstr ""
103+msgstr "Borde superior"
104
104
105#: Components/EdgeDemo.qml:136
105#: Components/EdgeDemo.qml:136
106msgid "Try swiping from the top edge to access the indicators"
106msgid "Try swiping from the top edge to access the indicators"
107−msgstr ""
107+msgstr "Deslice el dedo desde el borde superior para acceder a los menús"
108
108
109#: Components/EdgeDemo.qml:161
109#: Components/EdgeDemo.qml:161
110msgid "Close"
110msgid "Close"
111−msgstr ""
111+msgstr "Cerrar"
112
112
113#: Components/EdgeDemo.qml:162
113#: Components/EdgeDemo.qml:162
114msgid "Swipe up again to close the settings screen"
114msgid "Swipe up again to close the settings screen"
115−msgstr ""
115+msgstr "Deslice hacia arriba para cerrar la pantalla de configuración"
116
116
117#: Components/EdgeDemo.qml:189
117#: Components/EdgeDemo.qml:189
118msgid "Left edge"
118msgid "Left edge"
119−msgstr ""
119+msgstr "Borde izquierdo"
120
120
121#: Components/EdgeDemo.qml:190
121#: Components/EdgeDemo.qml:190
122msgid "Swipe from the left to reveal the launcher for quick access to apps"
122msgid "Swipe from the left to reveal the launcher for quick access to apps"
123msgstr ""
123msgstr ""
124+"Deslice el dedo desde la izquierda para mostrar el lanzador y acceder "
125+"rápidamente a las aplicaciones"
124
126
125#: Components/EdgeDemo.qml:217
127#: Components/EdgeDemo.qml:217
126msgid "Well done"
128msgid "Well done"
127−msgstr ""
129+msgstr "Bien hecho"
128
130
129#: Components/EdgeDemo.qml:218
131#: Components/EdgeDemo.qml:218
130msgid "You have now mastered the edge gestures and can start using the phone"
132msgid "You have now mastered the edge gestures and can start using the phone"
131−msgstr ""
133+msgstr "Ahora domina el uso de gestos de los bordes. Puede usar el teléfono"
132
134
133#: Components/PageHeader.qml:298
135#: Components/PageHeader.qml:298
134msgid "Recent searches"
136msgid "Recent searches"
@@ -138,35 +140,35 @@ msgstr "Búsquedas recientes"
138#: Dash/Apps/AppInfo.qml:73
140#: Dash/Apps/AppInfo.qml:73
139#, qt-format
141#, qt-format
140msgid "(%1)"
142msgid "(%1)"
141−msgstr ""
143+msgstr "(%1)"
142
144
143#. TRANSLATORS: Number of persons who wrote reviews for this app
145#. TRANSLATORS: Number of persons who wrote reviews for this app
144#: Dash/Apps/AppInfo.qml:85
146#: Dash/Apps/AppInfo.qml:85
145#, qt-format
147#, qt-format
146msgid "%1 review"
148msgid "%1 review"
147msgid_plural "%1 reviews"
149msgid_plural "%1 reviews"
148−msgstr[0] ""
150+msgstr[0] "%1 reseña"
149−msgstr[1] ""
151+msgstr[1] "%1 reseñas"
150
152
151#: Dash/Apps/AppPreview.qml:175
153#: Dash/Apps/AppPreview.qml:175
152msgid "Rate this"
154msgid "Rate this"
153−msgstr ""
155+msgstr "Valorar esto"
154
156
155#: Dash/Apps/AppReviews.qml:72
157#: Dash/Apps/AppReviews.qml:72
156msgid "Add a review"
158msgid "Add a review"
157−msgstr ""
159+msgstr "Añadir una reseña"
158
160
159#: Dash/Apps/AppReviews.qml:84
161#: Dash/Apps/AppReviews.qml:84
160msgid "Review"
162msgid "Review"
161−msgstr ""
163+msgstr "Reseñar"
162
164
163#: Dash/Apps/AppReviews.qml:118
165#: Dash/Apps/AppReviews.qml:118
164msgid "Send"
166msgid "Send"
165−msgstr ""
167+msgstr "Enviar"
166
168
167#: Dash/Apps/AppReviews.qml:136
169#: Dash/Apps/AppReviews.qml:136
168msgid "Comments:"
170msgid "Comments:"
169−msgstr ""
171+msgstr "Comentarios:"
170
172
171#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
173#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
172msgid "Featured"
174msgid "Featured"
@@ -210,7 +212,7 @@ msgstr "Autor:"
210
212
211#: Greeter/Lockscreen.qml:134
213#: Greeter/Lockscreen.qml:134
212msgid "Emergency Call"
214msgid "Emergency Call"
213−msgstr ""
215+msgstr "Llamada de emergencia"
214
216
215#: Greeter/PassphraseLockscreen.qml:59
217#: Greeter/PassphraseLockscreen.qml:59
216#, qt-format
218#, qt-format
po/is.ponew file+256
@@ -0,0 +1,256 @@
1+# Icelandic translation for unity8
2+# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-08-27 13:36-0400\n"
11+"PO-Revision-Date: 2013-09-18 16:22+0000\n"
12+"Last-Translator: Andri Thorlacius <Unknown>\n"
13+"Language-Team: Icelandic <is@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-09-19 05:14+0000\n"
19+"X-Generator: Launchpad (build 16765)\n"
20+
21+#: Applications/applications.js:23
22+msgid "Phone"
23+msgstr "Sími"
24+
25+#: Applications/applications.js:29
26+msgid "Camera"
27+msgstr "Myndavél"
28+
29+#: Applications/applications.js:35
30+msgid "Gallery"
31+msgstr "Myndasafn"
32+
33+#: Applications/applications.js:40
34+msgid "Facebook"
35+msgstr "Facebook"
36+
37+#: Applications/applications.js:46
38+msgid "Browser"
39+msgstr "Vafri"
40+
41+#: Applications/applications.js:51
42+msgid "Twitter"
43+msgstr "Twitter"
44+
45+#: Applications/applications.js:57
46+msgid "GMail"
47+msgstr "GMail"
48+
49+#: Applications/applications.js:62
50+msgid "Weather"
51+msgstr "Veður"
52+
53+#: Applications/applications.js:68
54+msgid "Notepad"
55+msgstr "Skrifblokk"
56+
57+#: Applications/applications.js:74
58+msgid "Calendar"
59+msgstr "Dagatal"
60+
61+#: Applications/applications.js:80
62+msgid "Media Player"
63+msgstr "Margmiðlunarspilari"
64+
65+#: Applications/applications.js:86
66+msgid "Evernote"
67+msgstr "Evernote"
68+
69+#: Applications/applications.js:91
70+msgid "Map"
71+msgstr "Kort"
72+
73+#: Applications/applications.js:96
74+msgid "Pinterest"
75+msgstr "Pinterest"
76+
77+#: Applications/applications.js:101
78+msgid "SoundCloud"
79+msgstr "SoundCloud"
80+
81+#: Applications/applications.js:106
82+msgid "Wikipedia"
83+msgstr "Wikipedia"
84+
85+#: Applications/applications.js:111
86+msgid "YouTube"
87+msgstr "YouTube"
88+
89+#: Components/EdgeDemoOverlay.qml:145
90+msgid "Skip intro"
91+msgstr "Hoppa yfir kynningu"
92+
93+#: Components/EdgeDemo.qml:98
94+msgid "Right edge"
95+msgstr "Hægri brún"
96+
97+#: Components/EdgeDemo.qml:99
98+msgid "Try swiping from the right edge to unlock the phone"
99+msgstr ""
100+"Prufaðu að renna puttanum frá hægri brúninni til að aflæsa símanum þínum."
101+
102+#: Components/EdgeDemo.qml:135
103+msgid "Top edge"
104+msgstr "Efri brún"
105+
106+#: Components/EdgeDemo.qml:136
107+msgid "Try swiping from the top edge to access the indicators"
108+msgstr ""
109+
110+#: Components/EdgeDemo.qml:161
111+msgid "Close"
112+msgstr "Loka"
113+
114+#: Components/EdgeDemo.qml:162
115+msgid "Swipe up again to close the settings screen"
116+msgstr ""
117+
118+#: Components/EdgeDemo.qml:189
119+msgid "Left edge"
120+msgstr "Vinstri brún"
121+
122+#: Components/EdgeDemo.qml:190
123+msgid "Swipe from the left to reveal the launcher for quick access to apps"
124+msgstr ""
125+
126+#: Components/EdgeDemo.qml:217
127+msgid "Well done"
128+msgstr "Vel gert"
129+
130+#: Components/EdgeDemo.qml:218
131+msgid "You have now mastered the edge gestures and can start using the phone"
132+msgstr ""
133+
134+#: Components/PageHeader.qml:298
135+msgid "Recent searches"
136+msgstr "Nýlegar leitir"
137+
138+#. TRANSLATORS: Number of persons who rated this app
139+#: Dash/Apps/AppInfo.qml:73
140+#, qt-format
141+msgid "(%1)"
142+msgstr "(%1)"
143+
144+#. TRANSLATORS: Number of persons who wrote reviews for this app
145+#: Dash/Apps/AppInfo.qml:85
146+#, qt-format
147+msgid "%1 review"
148+msgid_plural "%1 reviews"
149+msgstr[0] "%1 ummæli"
150+msgstr[1] "%1 ummæli"
151+
152+#: Dash/Apps/AppPreview.qml:175
153+msgid "Rate this"
154+msgstr "Gefa þessu einkunn"
155+
156+#: Dash/Apps/AppReviews.qml:72
157+msgid "Add a review"
158+msgstr "Gefa umsögn"
159+
160+#: Dash/Apps/AppReviews.qml:84
161+msgid "Review"
162+msgstr "Umsögn"
163+
164+#: Dash/Apps/AppReviews.qml:118
165+msgid "Send"
166+msgstr "Senda"
167+
168+#: Dash/Apps/AppReviews.qml:136
169+msgid "Comments:"
170+msgstr "Athugasemdir:"
171+
172+#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
173+msgid "Featured"
174+msgstr "Efst á baugi"
175+
176+#: Dash/DashMusic.qml:28 Dash/DashVideos.qml:29
177+msgid "Recent"
178+msgstr "Nýlegt"
179+
180+#: Dash/DashMusic.qml:29 Dash/DashVideos.qml:30
181+msgid "New Releases"
182+msgstr "Nýjar Útgáfur"
183+
184+#: Dash/DashMusic.qml:30
185+msgid "Top Charting"
186+msgstr "Topp Listi"
187+
188+#: Dash/DashMusic.qml:91
189+msgid "Music"
190+msgstr "Tónlist"
191+
192+#: Dash/DashVideos.qml:31
193+msgid "Popular Online"
194+msgstr "Vinsælt á Netinu"
195+
196+#: Dash/DashVideos.qml:146
197+msgid "Videos"
198+msgstr "Myndbönd"
199+
200+#: Dash/Video/VideoPreview.qml:130
201+msgid "Directed by:"
202+msgstr "Leikstýrt af:"
203+
204+#: Dash/Video/VideoPreview.qml:153
205+msgid "Starring:"
206+msgstr "Meðal leikara eru:"
207+
208+#: Dash/Video/VideoPreview.qml:176
209+msgid "Author:"
210+msgstr "Höfundur:"
211+
212+#: Greeter/Lockscreen.qml:134
213+msgid "Emergency Call"
214+msgstr "Neyðarsímtal"
215+
216+#: Greeter/PassphraseLockscreen.qml:59
217+#, qt-format
218+msgid "Hello %1"
219+msgstr "Hæ %1"
220+
221+#: Greeter/PassphraseLockscreen.qml:59
222+msgid "Hello"
223+msgstr "Hæ"
224+
225+#: Hud/HudParametrizedActionsPage.qml:132
226+msgid "Confirm"
227+msgstr "Staðfesta"
228+
229+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
230+msgid "Type or say a command"
231+msgstr "Skrifaðu eða segðu eitthvað"
232+
233+#: Hud/Hud.qml:84
234+msgid "Loading. Please Wait..."
235+msgstr "Hleð. Vinsamlegast Bíðið..."
236+
237+#: Hud/Hud.qml:89
238+msgid "Speak Now..."
239+msgstr "Talaðu Núna..."
240+
241+#: Hud/Hud.qml:94
242+msgid "Speaking..."
243+msgstr "Tala..."
244+
245+#: Panel/SearchIndicator.qml:27
246+msgid "Search"
247+msgstr "Leita"
248+
249+#: plugins/Unity/scope.cpp:317
250+msgid "Sorry, there is nothing that matches your search."
251+msgstr "Því miður, það var ekkert sem passaði við leitarorðið þitt."
252+
253+#: Shell.qml:429
254+#, qt-format
255+msgid "Please enter %1"
256+msgstr ""
po/sl.po+29−24
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-08-27 13:36-0400\n"
10"POT-Creation-Date: 2013-08-27 13:36-0400\n"
11−"PO-Revision-Date: 2013-07-02 12:59+0000\n"
11+"PO-Revision-Date: 2013-09-19 19:48+0000\n"
12"Last-Translator: Damir Jerovšek <Unknown>\n"
12"Last-Translator: Damir Jerovšek <Unknown>\n"
13"Language-Team: Slovenian <sl@li.org>\n"
13"Language-Team: Slovenian <sl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
17"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
18"n%100==4 ? 3 : 0);\n"
18"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2013-08-30 05:37+0000\n"
19+"X-Launchpad-Export-Date: 2013-09-20 06:21+0000\n"
20−"X-Generator: Launchpad (build 16750)\n"
20+"X-Generator: Launchpad (build 16765)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
@@ -25,7 +25,7 @@ msgstr "Telefon"
25
25
26#: Applications/applications.js:29
26#: Applications/applications.js:29
27msgid "Camera"
27msgid "Camera"
28−msgstr ""
28+msgstr "Fotoaparat"
29
29
30#: Applications/applications.js:35
30#: Applications/applications.js:35
31msgid "Gallery"
31msgid "Gallery"
@@ -89,47 +89,50 @@ msgstr "YouTube"
89
89
90#: Components/EdgeDemoOverlay.qml:145
90#: Components/EdgeDemoOverlay.qml:145
91msgid "Skip intro"
91msgid "Skip intro"
92−msgstr ""
92+msgstr "Preskoči uvod"
93
93
94#: Components/EdgeDemo.qml:98
94#: Components/EdgeDemo.qml:98
95msgid "Right edge"
95msgid "Right edge"
96−msgstr ""
96+msgstr "Desni rob"
97
97
98#: Components/EdgeDemo.qml:99
98#: Components/EdgeDemo.qml:99
99msgid "Try swiping from the right edge to unlock the phone"
99msgid "Try swiping from the right edge to unlock the phone"
100−msgstr ""
100+msgstr "Poskusite povleči od desnega roba za odklep telefona"
101
101
102#: Components/EdgeDemo.qml:135
102#: Components/EdgeDemo.qml:135
103msgid "Top edge"
103msgid "Top edge"
104−msgstr ""
104+msgstr "Zgornji rob"
105
105
106#: Components/EdgeDemo.qml:136
106#: Components/EdgeDemo.qml:136
107msgid "Try swiping from the top edge to access the indicators"
107msgid "Try swiping from the top edge to access the indicators"
108−msgstr ""
108+msgstr "Poskusite povleči od zgornjega roba za dostop do kazalnikov"
109
109
110#: Components/EdgeDemo.qml:161
110#: Components/EdgeDemo.qml:161
111msgid "Close"
111msgid "Close"
112−msgstr ""
112+msgstr "Zapri"
113
113
114#: Components/EdgeDemo.qml:162
114#: Components/EdgeDemo.qml:162
115msgid "Swipe up again to close the settings screen"
115msgid "Swipe up again to close the settings screen"
116−msgstr ""
116+msgstr "Povlecite ponovno navzgor za zaprtje zaslona z nastavitvami"
117
117
118#: Components/EdgeDemo.qml:189
118#: Components/EdgeDemo.qml:189
119msgid "Left edge"
119msgid "Left edge"
120−msgstr ""
120+msgstr "Levi rob"
121
121
122#: Components/EdgeDemo.qml:190
122#: Components/EdgeDemo.qml:190
123msgid "Swipe from the left to reveal the launcher for quick access to apps"
123msgid "Swipe from the left to reveal the launcher for quick access to apps"
124msgstr ""
124msgstr ""
125+"Poskusite povleči od leve za razkritje zaganjalnika za hiter dostop do "
126+"programov"
125
127
126#: Components/EdgeDemo.qml:217
128#: Components/EdgeDemo.qml:217
127msgid "Well done"
129msgid "Well done"
128−msgstr ""
130+msgstr "Odlično"
129
131
130#: Components/EdgeDemo.qml:218
132#: Components/EdgeDemo.qml:218
131msgid "You have now mastered the edge gestures and can start using the phone"
133msgid "You have now mastered the edge gestures and can start using the phone"
132msgstr ""
134msgstr ""
135+"Zdaj, ko ste osvojili kretnje z robovi, lahko začnete uporabljati telefon"
133
136
134#: Components/PageHeader.qml:298
137#: Components/PageHeader.qml:298
135msgid "Recent searches"
138msgid "Recent searches"
@@ -139,35 +142,37 @@ msgstr "Nedavna iskanja"
139#: Dash/Apps/AppInfo.qml:73
142#: Dash/Apps/AppInfo.qml:73
140#, qt-format
143#, qt-format
141msgid "(%1)"
144msgid "(%1)"
142−msgstr ""
145+msgstr "(%1)"
143
146
144#. TRANSLATORS: Number of persons who wrote reviews for this app
147#. TRANSLATORS: Number of persons who wrote reviews for this app
145#: Dash/Apps/AppInfo.qml:85
148#: Dash/Apps/AppInfo.qml:85
146#, qt-format
149#, qt-format
147msgid "%1 review"
150msgid "%1 review"
148msgid_plural "%1 reviews"
151msgid_plural "%1 reviews"
149−msgstr[0] ""
152+msgstr[0] "%1 ocen"
153+msgstr[1] "%1 ocena"
150−msgstr[1] ""
154+msgstr[2] "%1 oceni"
155+msgstr[3] "%1 ocene"
151
156
152#: Dash/Apps/AppPreview.qml:175
157#: Dash/Apps/AppPreview.qml:175
153msgid "Rate this"
158msgid "Rate this"
154−msgstr ""
159+msgstr "Ocenite"
155
160
156#: Dash/Apps/AppReviews.qml:72
161#: Dash/Apps/AppReviews.qml:72
157msgid "Add a review"
162msgid "Add a review"
158−msgstr ""
163+msgstr "Dodaj oceno"
159
164
160#: Dash/Apps/AppReviews.qml:84
165#: Dash/Apps/AppReviews.qml:84
161msgid "Review"
166msgid "Review"
162−msgstr ""
167+msgstr "Ocena"
163
168
164#: Dash/Apps/AppReviews.qml:118
169#: Dash/Apps/AppReviews.qml:118
165msgid "Send"
170msgid "Send"
166−msgstr ""
171+msgstr "Pošlji"
167
172
168#: Dash/Apps/AppReviews.qml:136
173#: Dash/Apps/AppReviews.qml:136
169msgid "Comments:"
174msgid "Comments:"
170−msgstr ""
175+msgstr "Opombe:"
171
176
172#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
177#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
173msgid "Featured"
178msgid "Featured"
@@ -211,7 +216,7 @@ msgstr ""
211
216
212#: Greeter/Lockscreen.qml:134
217#: Greeter/Lockscreen.qml:134
213msgid "Emergency Call"
218msgid "Emergency Call"
214−msgstr ""
219+msgstr "Klic v sili"
215
220
216#: Greeter/PassphraseLockscreen.qml:59
221#: Greeter/PassphraseLockscreen.qml:59
217#, qt-format
222#, qt-format
@@ -220,7 +225,7 @@ msgstr "Pozdravljeni, %1"
220
225
221#: Greeter/PassphraseLockscreen.qml:59
226#: Greeter/PassphraseLockscreen.qml:59
222msgid "Hello"
227msgid "Hello"
223−msgstr ""
228+msgstr "Pozdravljeni"
224
229
225#: Hud/HudParametrizedActionsPage.qml:132
230#: Hud/HudParametrizedActionsPage.qml:132
226msgid "Confirm"
231msgid "Confirm"
@@ -253,4 +258,4 @@ msgstr "Nič se ne ujema z vašim iskalnim nizom."
253#: Shell.qml:429
258#: Shell.qml:429
254#, qt-format
259#, qt-format
255msgid "Please enter %1"
260msgid "Please enter %1"
256−msgstr ""
261+msgstr "Vnesite %1"
run_on_device+2−1
@@ -65,8 +65,9 @@ setup_adb_forwarding() {
65}
65}
66
66
67install_dependencies() {
67install_dependencies() {
68+ exec_with_adb rm /etc/init/ssh.override
69+ exec_with_adb service ssh start
68 exec_with_adb apt-get update
70 exec_with_adb apt-get update
69− exec_with_adb apt-get -y install openssh-server
70 exec_with_ssh $SUDO apt-get -y install build-essential rsync bzr ccache gdb ninja-build devscripts equivs
71 exec_with_ssh $SUDO apt-get -y install build-essential rsync bzr ccache gdb ninja-build devscripts equivs
71}
72}
72
73
tests/autopilot/unity8/application_lifecycle/__init__.pynew file+20
@@ -0,0 +1,20 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Tests for the application lifecycle base package."""
tests/autopilot/unity8/application_lifecycle/tests/__init__.pynew file

No changes to the content.

tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.pynew file+136
@@ -0,0 +1,136 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Tests for the application lifecycle."""
21+
22+from __future__ import absolute_import
23+
24+from unity8.shell import disable_qml_mocking
25+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
26+
27+from autopilot.matchers import Eventually
28+from autopilot.platform import model
29+from testtools.matchers import Equals, NotEquals
30+
31+import logging
32+
33+logger = logging.getLogger(__name__)
34+
35+
36+class ApplicationLifecycleTests(UnityTestCase):
37+
38+ scenarios = _get_device_emulation_scenarios()
39+
40+ def setUp(self):
41+ super(ApplicationLifecycleTests, self).setUp()
42+ if model() == "Desktop":
43+ self.skipTest("Test cannot be run on the desktop.")
44+
45+ def swipe_screen_from_right(self):
46+ qml_view = self.main_window.get_qml_view()
47+ width = qml_view.width
48+ height = qml_view.height
49+ start_x = width
50+ start_y = int(height/2)
51+ end_x = int(width/2)
52+ end_y = start_y
53+
54+ logger.info("Swiping screen from the right edge")
55+ self.touch.drag(start_x, start_y, end_x, end_y)
56+
57+ def _get_current_focused_app_id(self):
58+ return self._proxy.select_single("Shell").currentFocusedAppId
59+
60+ def _launch_app(self, app_name):
61+ """Launches the application *app_name*
62+
63+ Assumes that the desktop file resides at:
64+ /usr/share/applications/{app_name}.desktop
65+
66+ """
67+ desktop_file = "--desktop_file_hint="\
68+ "/usr/share/applications/{app_name}.desktop".format(
69+ app_name=app_name
70+ )
71+ return self.launch_test_application(
72+ app_name,
73+ desktop_file,
74+ "--stage_hint=main_stage",
75+ app_type='qt'
76+ )
77+
78+ @disable_qml_mocking
79+ def test_can_launch_application(self):
80+ """Must be able to launch an application."""
81+ self.launch_unity()
82+ self.main_window.get_greeter().swipe()
83+
84+ app = self._launch_app("messaging-app")
85+
86+ self.assertThat(app, NotEquals(None))
87+ self.assertThat(
88+ self._get_current_focused_app_id(),
89+ Eventually(Equals("messaging-app"))
90+ )
91+
92+ @disable_qml_mocking
93+ def test_can_launch_multiple_applications(self):
94+ """A second application launched must be focused."""
95+ self.launch_unity()
96+ self.main_window.get_greeter().swipe()
97+
98+ self._launch_app("messaging-app")
99+ self.assertThat(
100+ self._get_current_focused_app_id(),
101+ Eventually(Equals("messaging-app"))
102+ )
103+
104+ self._launch_app("address-book-app")
105+ self.assertThat(
106+ self._get_current_focused_app_id(),
107+ Eventually(Equals("address-book-app"))
108+ )
109+
110+ @disable_qml_mocking
111+ def test_app_moves_from_unfocused_to_focused(self):
112+ """An application that is in the unfocused state must be able to be
113+ brought back to the focused state.
114+
115+ """
116+ self.launch_unity()
117+ self.main_window.get_greeter().swipe()
118+
119+ self._launch_app("messaging-app")
120+ self.assertThat(
121+ self._get_current_focused_app_id(),
122+ Eventually(Equals("messaging-app"))
123+ )
124+
125+ self._launch_app("address-book-app")
126+ self.assertThat(
127+ self._get_current_focused_app_id(),
128+ Eventually(Equals("address-book-app"))
129+ )
130+
131+ self.swipe_screen_from_right()
132+
133+ self.assertThat(
134+ self._get_current_focused_app_id(),
135+ Eventually(Equals("messaging-app"))
136+ )
tests/autopilot/unity8/shell/__init__.py+10
@@ -36,3 +36,13 @@ def with_lightdm_mock(mock_type):
36 return fn(*args, **kwargs)
36 return fn(*args, **kwargs)
37 return wrapper
37 return wrapper
38 return with_lightdm_mock_internal
38 return with_lightdm_mock_internal
39+
40+
41+def disable_qml_mocking(fn):
42+ """Simple decorator that disables the QML mocks from being loaded."""
43+ @wraps(fn)
44+ def wrapper(*args, **kwargs):
45+ tests_self = args[0]
46+ tests_self._qml_mock_enabled = False
47+ return fn(*args, **kwargs)
48+ return wrapper
tests/autopilot/unity8/shell/tests/__init__.py+4−1
@@ -86,6 +86,7 @@ class UnityTestCase(AutopilotTestCase):
86
86
87 self._proxy = None
87 self._proxy = None
88 self._lightdm_mock_type = None
88 self._lightdm_mock_type = None
89+ self._qml_mock_enabled = True
89 self.touch = Touch.create()
90 self.touch = Touch.create()
90 self._setup_display_details()
91 self._setup_display_details()
91
92
@@ -160,7 +161,9 @@ class UnityTestCase(AutopilotTestCase):
160
161
161 if self._lightdm_mock_type is None:
162 if self._lightdm_mock_type is None:
162 self.patch_lightdm_mock()
163 self.patch_lightdm_mock()
164+
165+ if self._qml_mock_enabled:
163− self._setup_extra_mock_environment_patch()
166+ self._setup_extra_mock_environment_patch()
164
167
165 app_proxy = self.launch_test_application(
168 app_proxy = self.launch_test_application(
166 binary_path,
169 binary_path,
tests/autopilot/unity8/shell/tests/test_hud.py+7−2
@@ -34,6 +34,8 @@ class TestHud(UnityTestCase):
34
34
35 def test_show_hud_button_appears(self):
35 def test_show_hud_button_appears(self):
36 """Swiping up while an app is active must show the 'show hud' button.
36 """Swiping up while an app is active must show the 'show hud' button.
37+ The button must disappear not opening the HUD when releasing the
38+ mouse again somewhere on the screen except on the button itself.
37
39
38 """
40 """
39 self.launch_unity()
41 self.launch_unity()
@@ -49,9 +51,12 @@ class TestHud(UnityTestCase):
49 hud_show_button
51 hud_show_button
50 )
52 )
51 self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
53 self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
52− self.addCleanup(self.touch.release)
54+ self.addCleanup(self._maybe_release_finger)
53− self.touch._finger_move(swipe_coords.end_x, swipe_coords.end_y)
55+ self.touch._finger_move(swipe_coords.end_x, swipe_coords.end_y + hud_show_button.height);
54 self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
56 self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
57+ self.touch.release();
58+ self.assertThat(hud_show_button.opacity, Eventually(Equals(0.0)))
59+ self.assertThat(hud.shown, Equals(False))
55
60
56 def test_show_hud_appears(self):
61 def test_show_hud_appears(self):
57 """Releasing the touch on the 'show hud' button must display the hud.
62 """Releasing the touch on the 'show hud' button must display the hud.
tests/mocks/AccountsService/AccountsService.cpp+27−4
@@ -19,15 +19,38 @@
19#include "AccountsService.h"
19#include "AccountsService.h"
20
20
21AccountsService::AccountsService(QObject* parent)
21AccountsService::AccountsService(QObject* parent)
22− : QObject(parent)
22+ : QObject(parent),
23+ m_backgroundFile(TOP_SRCDIR "/graphics/phone_background.jpg")
23{
24{
24}
25}
25
26
26−QVariant AccountsService::getUserProperty(const QString &, const QString &)
27+QString AccountsService::getUser()
27{
28{
28− return QVariant(false);
29+ return "testuser";
29}
30}
30
31
31−void AccountsService::setUserProperty(const QString &, const QString &, const QVariant &)
32+void AccountsService::setUser(const QString &user)
32{
33{
34+ Q_UNUSED(user)
35+}
36+
37+bool AccountsService::getDemoEdges()
38+{
39+ return false;
40+}
41+
42+void AccountsService::setDemoEdges(bool demoEdges)
43+{
44+ Q_UNUSED(demoEdges)
45+}
46+
47+QString AccountsService::getBackgroundFile()
48+{
49+ return m_backgroundFile;
50+}
51+
52+void AccountsService::setBackgroundFile(const QString &backgroundFile)
53+{
54+ m_backgroundFile = backgroundFile;
55+ backgroundFileChanged();
33}
56}
tests/mocks/AccountsService/AccountsService.h+27−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2012,2013 Canonical, Ltd.
2+ * Copyright (C) 2013 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -27,12 +27,36 @@
27class AccountsService: public QObject
27class AccountsService: public QObject
28{
28{
29 Q_OBJECT
29 Q_OBJECT
30+ Q_PROPERTY (QString user
31+ READ getUser
32+ WRITE setUser
33+ NOTIFY userChanged)
34+ Q_PROPERTY (bool demoEdges
35+ READ getDemoEdges
36+ WRITE setDemoEdges
37+ NOTIFY demoEdgesChanged)
38+ Q_PROPERTY (QString backgroundFile
39+ READ getBackgroundFile
40+ WRITE setBackgroundFile // only available in mock
41+ NOTIFY backgroundFileChanged)
30
42
31public:
43public:
32 explicit AccountsService(QObject *parent = 0);
44 explicit AccountsService(QObject *parent = 0);
33
45
34− Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &property);
46+ QString getUser();
35− Q_INVOKABLE void setUserProperty(const QString &user, const QString &property, const QVariant &value);
47+ void setUser(const QString &user);
48+ bool getDemoEdges();
49+ void setDemoEdges(bool demoEdges);
50+ QString getBackgroundFile();
51+ void setBackgroundFile(const QString &backgroundFile);
52+
53+Q_SIGNALS:
54+ void userChanged();
55+ void demoEdgesChanged();
56+ void backgroundFileChanged();
57+
58+private:
59+ QString m_backgroundFile;
36};
60};
37
61
38#endif
62#endif
tests/mocks/AccountsService/CMakeLists.txt+2
@@ -1,3 +1,5 @@
1+add_definitions(-DTOP_SRCDIR="${CMAKE_SOURCE_DIR}")
2+
1add_library(MockAccountsService-qml MODULE
3add_library(MockAccountsService-qml MODULE
2 AccountsService.cpp
4 AccountsService.cpp
3 plugin.cpp
5 plugin.cpp
tests/mocks/LightDM/demo/GreeterPrivate.cpp+4−2
@@ -55,11 +55,13 @@ void GreeterPrivate::handleRespond(const QString &response)
55 QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
55 QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
56 QVariant password = settings.value("password", "none");
56 QVariant password = settings.value("password", "none");
57
57
58+ QString passwordValue;
58 if (password == "pin") {
59 if (password == "pin") {
59− authenticated = (response == "1234");
60+ passwordValue = settings.value("passwordValue", "1234").toString();
60 } else {
61 } else {
61− authenticated = (response == "password");
62+ passwordValue = settings.value("passwordValue", "password").toString();
62 }
63 }
64+ authenticated = (response == passwordValue);
63 Q_EMIT q->authenticationComplete();
65 Q_EMIT q->authenticationComplete();
64}
66}
65
67
tests/mocks/Ubuntu/Application/CMakeLists.txtdeleted−29
@@ -1,29 +0,0 @@
1−set(FakeUbuntuApplicationQml_SOURCES
2− plugin.cpp
3− ApplicationInfo.cpp
4− ApplicationImage.cpp
5− ApplicationListModel.cpp
6− ApplicationManager.cpp
7−)
8−
9−add_library(FakeUbuntuApplicationQml MODULE ${FakeUbuntuApplicationQml_SOURCES})
10−
11−qt5_use_modules(FakeUbuntuApplicationQml Core Quick)
12−
13−# copy files into build directory for shadow builds
14−add_custom_target(UbuntuAppQmlDirFile ALL
15− COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" ${CMAKE_CURRENT_BINARY_DIR}
16− DEPENDS qmldir
17−)
18−add_custom_target(InputFilterAreaQmlFile ALL
19− COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/InputFilterArea.qml" ${CMAKE_CURRENT_BINARY_DIR}
20− DEPENDS InputFilterArea.qml
21−)
22−
23−install(TARGETS FakeUbuntuApplicationQml
24− DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Ubuntu/Application
25− )
26−
27−install(FILES qmldir InputFilterArea.qml
28− DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Ubuntu/Application
29− )
tests/mocks/Ubuntu/Application/qmldirdeleted−3
@@ -1,3 +0,0 @@
1−module Ubuntu.Application
2−plugin FakeUbuntuApplicationQml
3−InputFilterArea 0.1 InputFilterArea.qml
tests/mocks/Ubuntu/CMakeLists.txt−1
@@ -1,2 +1 @@
1−add_subdirectory(Application)
2add_subdirectory(DownloadDaemonListener)
1add_subdirectory(DownloadDaemonListener)
tests/mocks/Ubuntu/Application/ApplicationImage.cpp → tests/mocks/Unity/Application/ApplicationImage.cpprenamed

The content is the same.

tests/mocks/Ubuntu/Application/ApplicationImage.h → tests/mocks/Unity/Application/ApplicationImage.hrenamed

The content is the same.

tests/mocks/Ubuntu/Application/ApplicationInfo.cpp → tests/mocks/Unity/Application/ApplicationInfo.cpprenamed+15−2
@@ -21,11 +21,24 @@
21#include <QQuickView>
21#include <QQuickView>
22#include <QQmlComponent>
22#include <QQmlComponent>
23
23
24+ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
25+ : ApplicationInfoInterface(appId, parent)
26+ ,m_appId(appId)
27+ ,m_stage(MainStage)
28+ ,m_state(Starting)
29+ ,m_focused(false)
30+ ,m_fullscreen(false)
31+ ,m_windowItem(0)
32+ ,m_windowComponent(0)
33+ ,m_parentItem(0)
34+{
35+}
36+
24ApplicationInfo::ApplicationInfo(QObject *parent)
37ApplicationInfo::ApplicationInfo(QObject *parent)
25− : QObject(parent)
38+ : ApplicationInfoInterface(QString(), parent)
26− ,m_handle(0)
27 ,m_stage(MainStage)
39 ,m_stage(MainStage)
28 ,m_state(Starting)
40 ,m_state(Starting)
41+ ,m_focused(false)
29 ,m_fullscreen(false)
42 ,m_fullscreen(false)
30 ,m_windowItem(0)
43 ,m_windowItem(0)
31 ,m_windowComponent(0)
44 ,m_windowComponent(0)
tests/mocks/Ubuntu/Application/ApplicationInfo.h → tests/mocks/Unity/Application/ApplicationInfo.hrenamed+12−18
@@ -22,21 +22,18 @@
22
22
23class QQuickItem;
23class QQuickItem;
24
24
25+// unity-api
26+#include <unity/shell/application/ApplicationInfoInterface.h>
27+
25// A pretty dumb file. Just a container for properties.
28// A pretty dumb file. Just a container for properties.
26// Implemented in C++ instead of QML just because of the enumerations
29// Implemented in C++ instead of QML just because of the enumerations
27// See QTBUG-14861
30// See QTBUG-14861
31+
32+using namespace unity::shell::application;
33+
28−class ApplicationInfo : public QObject {
34+class ApplicationInfo : public ApplicationInfoInterface {
29 Q_OBJECT
35 Q_OBJECT
30− Q_ENUMS(Stage)
36+
31− Q_ENUMS(State)
32− Q_PROPERTY(QString desktopFile READ desktopFile WRITE setDesktopFile NOTIFY desktopFileChanged)
33− Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
34− Q_PROPERTY(QString comment READ comment WRITE setComment NOTIFY commentChanged)
35− Q_PROPERTY(QString icon READ icon WRITE setIcon NOTIFY iconChanged)
36− Q_PROPERTY(QString exec READ exec WRITE setExec NOTIFY execChanged)
37− Q_PROPERTY(qint64 handle READ handle WRITE setHandle NOTIFY handleChanged)
38− Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)
39− Q_PROPERTY(State state READ state WRITE setState NOTIFY stateChanged)
40 Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged)
37 Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged)
41
38
42 // Only exists in this fake implementation
39 // Only exists in this fake implementation
@@ -48,10 +45,8 @@ class ApplicationInfo : public QObject {
48 Q_PROPERTY(QString windowQml READ windowQml WRITE setWindowQml NOTIFY windowQmlChanged)
45 Q_PROPERTY(QString windowQml READ windowQml WRITE setWindowQml NOTIFY windowQmlChanged)
49
46
50 public:
47 public:
51− enum Stage { MainStage, SideStage };
52− enum State { Starting, Running };
53−
54 ApplicationInfo(QObject *parent = NULL);
48 ApplicationInfo(QObject *parent = NULL);
49+ ApplicationInfo(const QString &appId, QObject *parent = NULL);
55
50
56 #define IMPLEMENT_PROPERTY(name, Name, type) \
51 #define IMPLEMENT_PROPERTY(name, Name, type) \
57 public: \
52 public: \
@@ -68,14 +63,13 @@ class ApplicationInfo : public QObject {
68 private: \
63 private: \
69 type m_##name;
64 type m_##name;
70
65
71− IMPLEMENT_PROPERTY(desktopFile, DesktopFile, QString)
66+ IMPLEMENT_PROPERTY(appId, AppId, QString)
72 IMPLEMENT_PROPERTY(name, Name, QString)
67 IMPLEMENT_PROPERTY(name, Name, QString)
73 IMPLEMENT_PROPERTY(comment, Comment, QString)
68 IMPLEMENT_PROPERTY(comment, Comment, QString)
74− IMPLEMENT_PROPERTY(icon, Icon, QString)
69+ IMPLEMENT_PROPERTY(icon, Icon, QUrl)
75− IMPLEMENT_PROPERTY(exec, Exec, QString)
76− IMPLEMENT_PROPERTY(handle, Handle, qint64)
77 IMPLEMENT_PROPERTY(stage, Stage, Stage)
70 IMPLEMENT_PROPERTY(stage, Stage, Stage)
78 IMPLEMENT_PROPERTY(state, State, State)
71 IMPLEMENT_PROPERTY(state, State, State)
72+ IMPLEMENT_PROPERTY(focused, Focused, bool)
79 IMPLEMENT_PROPERTY(fullscreen, Fullscreen, bool)
73 IMPLEMENT_PROPERTY(fullscreen, Fullscreen, bool)
80 IMPLEMENT_PROPERTY(imageQml, ImageQml, QString)
74 IMPLEMENT_PROPERTY(imageQml, ImageQml, QString)
81 IMPLEMENT_PROPERTY(windowQml, WindowQml, QString)
75 IMPLEMENT_PROPERTY(windowQml, WindowQml, QString)
tests/mocks/Ubuntu/Application/ApplicationListModel.cpp → tests/mocks/Unity/Application/ApplicationListModel.cpprenamed

The content is the same.

tests/mocks/Ubuntu/Application/ApplicationListModel.h → tests/mocks/Unity/Application/ApplicationListModel.hrenamed

The content is the same.

tests/mocks/Ubuntu/Application/ApplicationManager.cpp → tests/mocks/Unity/Application/ApplicationManager.cpprenamed+185−153
@@ -16,7 +16,6 @@
16
16
17#include "ApplicationManager.h"
17#include "ApplicationManager.h"
18#include "ApplicationInfo.h"
18#include "ApplicationInfo.h"
19−#include "ApplicationListModel.h"
20#include "paths.h"
19#include "paths.h"
21
20
22#include <QDir>
21#include <QDir>
@@ -26,11 +25,7 @@
26#include <QQmlComponent>
25#include <QQmlComponent>
27
26
28ApplicationManager::ApplicationManager(QObject *parent)
27ApplicationManager::ApplicationManager(QObject *parent)
29− : QObject(parent)
28+ : ApplicationManagerInterface(parent)
30− , m_mainStageApplications(new ApplicationListModel())
31− , m_sideStageApplications(new ApplicationListModel())
32− , m_mainStageFocusedApplication(0)
33− , m_sideStageFocusedApplication(0)
34 , m_mainStageComponent(0)
29 , m_mainStageComponent(0)
35 , m_mainStage(0)
30 , m_mainStage(0)
36 , m_sideStageComponent(0)
31 , m_sideStageComponent(0)
@@ -41,8 +36,84 @@ ApplicationManager::ApplicationManager(QObject *parent)
41
36
42ApplicationManager::~ApplicationManager()
37ApplicationManager::~ApplicationManager()
43{
38{
44− delete m_mainStageApplications;
39+}
45− delete m_sideStageApplications;
40+
41+int ApplicationManager::rowCount(const QModelIndex& parent) const {
42+ return !parent.isValid() ? m_runningApplications.size() : 0;
43+}
44+
45+QVariant ApplicationManager::data(const QModelIndex& index, int role) const {
46+ if (index.row() < 0 || index.row() >= m_runningApplications.size())
47+ return QVariant();
48+
49+ auto app = m_runningApplications.at(index.row());
50+ switch(role) {
51+ case RoleAppId:
52+ return app->appId();
53+ case RoleName:
54+ return app->name();
55+ case RoleComment:
56+ return app->comment();
57+ case RoleIcon:
58+ return app->icon();
59+ case RoleStage:
60+ return app->stage();
61+ case RoleState:
62+ return app->state();
63+ case RoleFocused:
64+ return app->focused();
65+ default:
66+ return QVariant();
67+ }
68+}
69+
70+ApplicationInfo *ApplicationManager::get(int row) const {
71+ if (row < 0 || row >= m_runningApplications.size())
72+ return nullptr;
73+ return m_runningApplications.at(row);
74+}
75+
76+ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const {
77+ for (ApplicationInfo *app : m_runningApplications) {
78+ if (app->appId() == appId) {
79+ return app;
80+ }
81+ }
82+ return nullptr;
83+}
84+
85+void ApplicationManager::add(ApplicationInfo *application) {
86+ if (!application)
87+ return;
88+
89+ beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size());
90+ m_runningApplications.append(application);
91+ endInsertRows();
92+ Q_EMIT countChanged();
93+}
94+
95+void ApplicationManager::remove(ApplicationInfo *application) {
96+ int i = m_runningApplications.indexOf(application);
97+ if (i != -1) {
98+ beginRemoveRows(QModelIndex(), i, i);
99+ m_runningApplications.removeAt(i);
100+ endRemoveRows();
101+ Q_EMIT countChanged();
102+ }
103+}
104+
105+void ApplicationManager::move(int from, int to) {
106+ if (from == to) return;
107+
108+ if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) {
109+ QModelIndex parent;
110+ /* When moving an item down, the destination index needs to be incremented
111+ * by one, as explained in the documentation:
112+ * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
113+ beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
114+ m_runningApplications.move(from, to);
115+ endMoveRows();
116+ }
46}
117}
47
118
48int ApplicationManager::keyboardHeight() const
119int ApplicationManager::keyboardHeight() const
@@ -70,36 +141,21 @@ ApplicationManager::FormFactorHint ApplicationManager::formFactorHint() const
70 return PhoneFormFactor;
141 return PhoneFormFactor;
71}
142}
72
143
73−ApplicationListModel* ApplicationManager::mainStageApplications() const
144+ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
74−{
145+ const QStringList &arguments)
75− return m_mainStageApplications;
76−}
77−
78−ApplicationListModel* ApplicationManager::sideStageApplications() const
79{
146{
80− return m_sideStageApplications;
147+ return startApplication(appId, NoFlag, arguments);
81}
148}
82
149
83−ApplicationInfo* ApplicationManager::mainStageFocusedApplication() const
150+ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
84−{
85− return m_mainStageFocusedApplication;
86−}
87−
88−ApplicationInfo* ApplicationManager::sideStageFocusedApplication() const
89−{
90− return m_sideStageFocusedApplication;
91−}
92−
93−ApplicationInfo* ApplicationManager::startProcess(QString desktopFile,
94 ExecFlags flags,
151 ExecFlags flags,
95− QStringList arguments)
152+ const QStringList &arguments)
96{
153{
97 Q_UNUSED(arguments)
154 Q_UNUSED(arguments)
98 ApplicationInfo *application = 0;
155 ApplicationInfo *application = 0;
99
156
100− for (int i = 0; i < m_availableApplications.count(); ++i) {
101− ApplicationInfo *availableApp = m_availableApplications[i];
157+ for (ApplicationInfo *availableApp : m_availableApplications) {
102− if (availableApp->desktopFile() == desktopFile) {
158+ if (availableApp->appId() == appId) {
103 application = availableApp;
159 application = availableApp;
104 break;
160 break;
105 }
161 }
@@ -109,86 +165,91 @@ ApplicationInfo* ApplicationManager::startProcess(QString desktopFile,
109 return 0;
165 return 0;
110
166
111 if (flags.testFlag(ApplicationManager::ForceMainStage)
167 if (flags.testFlag(ApplicationManager::ForceMainStage)
112− || application->stage() == ApplicationInfo::MainStage) {
168+ && application->stage() == ApplicationInfo::SideStage) {
113− m_mainStageApplications->add(application);
169+ application->setStage(ApplicationInfo::MainStage);
114− } else {
115− m_sideStageApplications->add(application);
116 }
170 }
171+ add(application);
117
172
118 return application;
173 return application;
119}
174}
120
175
121−void ApplicationManager::stopProcess(ApplicationInfo* application)
176+bool ApplicationManager::stopApplication(const QString &appId)
122{
177{
178+ ApplicationInfo *application = findApplication(appId);
123− if (m_mainStageApplications->contains(application)) {
179+ if (application == nullptr)
124− m_mainStageApplications->remove(application);
180+ return false;
125
181
126− if (m_mainStageFocusedApplication == application) {
182+ remove(application);
127− m_mainStageFocusedApplication = 0;
183+ Q_EMIT focusedApplicationIdChanged();
128− Q_EMIT mainStageFocusedApplicationChanged();
184+ return true;
129− }
185+}
130− } else if (m_sideStageApplications->contains(application)){
131− m_sideStageApplications->remove(application);
132
186
133− if (m_sideStageFocusedApplication == application) {
187+QString ApplicationManager::focusedApplicationId() const {
134− m_sideStageFocusedApplication = 0;
188+ for (ApplicationInfo *app : m_runningApplications) {
135− Q_EMIT sideStageFocusedApplicationChanged();
189+ if (app->focused()) {
190+ return app->appId();
136 }
191 }
137 }
192 }
193+ return QString();
138}
194}
139
195
140−void ApplicationManager::focusApplication(int handle)
196+bool ApplicationManager::focusApplication(const QString &appId)
141{
197{
142− for (int i = 0; i < m_mainStageApplications->m_applications.count(); ++i) {
198+ ApplicationInfo *application = findApplication(appId);
143− ApplicationInfo *application = m_mainStageApplications->m_applications[i];
199+ if (application == nullptr)
144− if (application->handle() == handle) {
200+ return false;
145− if (m_mainStageFocusedApplication)
201+
146− m_mainStageFocusedApplication->hideWindow();
202+ if (application->stage() == ApplicationInfo::MainStage) {
147− m_mainStageFocusedApplication = application;
203+ // unfocus currently focused mainstage app
148− if (!m_mainStage)
204+ for (ApplicationInfo *app : m_runningApplications) {
149− createMainStage();
205+ if (app->focused() && app->stage() == ApplicationInfo::MainStage) {
150− application->showWindow(m_mainStage);
206+ app->setFocused(false);
151− m_mainStage->setZ(-1000);
207+ app->hideWindow();
152− if (m_sideStage)
208+ }
153− m_sideStage->setZ(-2000);
154− Q_EMIT mainStageFocusedApplicationChanged();
155− return;
156 }
209 }
157− }
158
210
159− for (int i = 0; i < m_sideStageApplications->m_applications.count(); ++i) {
211+ // focus this app
160− ApplicationInfo *application = m_sideStageApplications->m_applications[i];
212+ application->setFocused(true);
161− if (application->handle() == handle) {
213+ if (!m_mainStage)
162− if (m_sideStageFocusedApplication)
214+ createMainStage();
163− m_sideStageFocusedApplication->hideWindow();
215+ application->showWindow(m_mainStage);
164− m_sideStageFocusedApplication = application;
216+ m_mainStage->setZ(-1000);
165− if (!m_sideStage)
217+ if (m_sideStage)
166− createSideStage();
218+ m_sideStage->setZ(-2000);
167− application->showWindow(m_sideStage);
219+ } else if (application->stage() == ApplicationInfo::SideStage) {
168− m_sideStage->setZ(-1000);
220+ // unfocus currently focused sidestage app
169− if (m_mainStage)
221+ for (ApplicationInfo *app : m_runningApplications) {
170− m_mainStage->setZ(-2000);
222+ if (app->focused() && app->stage() == ApplicationInfo::SideStage) {
171− Q_EMIT sideStageFocusedApplicationChanged();
223+ app->setFocused(false);
172− return;
224+ app->hideWindow();
225+ }
173 }
226 }
227+
228+ // focus this app
229+ application->setFocused(true);
230+ if (!m_sideStage)
231+ createSideStage();
232+ application->showWindow(m_sideStage);
233+ m_sideStage->setZ(-1000);
234+ if (m_mainStage)
235+ m_mainStage->setZ(-2000);
174 }
236 }
237+
238+ // move app to top of stack
239+ move(m_runningApplications.indexOf(application), 0);
240+ Q_EMIT focusedApplicationIdChanged();
241+ return true;
175}
242}
176
243
177−void ApplicationManager::unfocusCurrentApplication(StageHint stageHint)
244+void ApplicationManager::unfocusCurrentApplication()
178{
245{
179− if (stageHint == SideStage) {
246+ for (ApplicationInfo *app : m_runningApplications) {
180− if (m_sideStageFocusedApplication) {
247+ if (app->focused()) {
181− m_sideStageFocusedApplication->hideWindow();
248+ app->hideWindow();
182− m_sideStageFocusedApplication = 0;
249+ app->setFocused(false);
183− Q_EMIT sideStageFocusedApplicationChanged();
184− }
185− } else {
186− if (m_mainStageFocusedApplication) {
187− m_mainStageFocusedApplication->hideWindow();
188− m_mainStageFocusedApplication = 0;
189− Q_EMIT mainStageFocusedApplicationChanged();
190 }
250 }
191 }
251 }
252+ Q_EMIT focusedApplicationIdChanged();
192}
253}
193
254
194void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
255void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
@@ -213,7 +274,7 @@ void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
213 " fillMode: Image.PreserveAspectCrop\n"
274 " fillMode: Image.PreserveAspectCrop\n"
214 "}").arg(topMargin)
275 "}").arg(topMargin)
215 .arg(shellAppDirectory())
276 .arg(shellAppDirectory())
216− .arg(application->icon());
277+ .arg(application->icon().toString());
217 application->setWindowQml(windowQml);
278 application->setWindowQml(windowQml);
218
279
219 QString imageQml = QString(
280 QString imageQml = QString(
@@ -224,159 +285,130 @@ void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
224 " smooth: true\n"
285 " smooth: true\n"
225 " fillMode: Image.PreserveAspectCrop\n"
286 " fillMode: Image.PreserveAspectCrop\n"
226 "}").arg(shellAppDirectory())
287 "}").arg(shellAppDirectory())
227− .arg(application->icon());
288+ .arg(application->icon().toString());
228 application->setImageQml(imageQml);
289 application->setImageQml(imageQml);
229}
290}
230
291
231void ApplicationManager::buildListOfAvailableApplications()
292void ApplicationManager::buildListOfAvailableApplications()
232{
293{
233 ApplicationInfo *application;
294 ApplicationInfo *application;
234− qint64 nextHandle = 1;
235
295
236 application = new ApplicationInfo(this);
296 application = new ApplicationInfo(this);
237− application->setDesktopFile("/usr/share/applications/phone-app.desktop");
297+ application->setAppId("phone-app");
238 application->setName("Phone");
298 application->setName("Phone");
239− application->setIcon("phone");
299+ application->setIcon(QUrl("phone"));
240− application->setExec("/usr/bin/phone-app");
241 application->setStage(ApplicationInfo::SideStage);
300 application->setStage(ApplicationInfo::SideStage);
242− application->setHandle(nextHandle++);
243 generateQmlStrings(application);
301 generateQmlStrings(application);
244 m_availableApplications.append(application);
302 m_availableApplications.append(application);
245
303
246 application = new ApplicationInfo(this);
304 application = new ApplicationInfo(this);
247− application->setDesktopFile("/usr/share/applications/camera-app.desktop");
305+ application->setAppId("camera-app");
248 application->setName("Camera");
306 application->setName("Camera");
249− application->setIcon("camera");
307+ application->setIcon(QUrl("camera"));
250 application->setFullscreen(true);
308 application->setFullscreen(true);
251− application->setExec("/usr/bin/camera-app --fullscreen");
252− application->setHandle(nextHandle++);
253 generateQmlStrings(application);
309 generateQmlStrings(application);
254 m_availableApplications.append(application);
310 m_availableApplications.append(application);
255
311
256 application = new ApplicationInfo(this);
312 application = new ApplicationInfo(this);
257− application->setDesktopFile("/usr/share/applications/gallery-app.desktop");
313+ application->setAppId("gallery-app");
258 application->setName("Gallery");
314 application->setName("Gallery");
259− application->setIcon("gallery");
315+ application->setIcon(QUrl("gallery"));
260− application->setExec("/usr/bin/gallery-app");
261− application->setHandle(nextHandle++);
262 generateQmlStrings(application);
316 generateQmlStrings(application);
263 m_availableApplications.append(application);
317 m_availableApplications.append(application);
264
318
265 application = new ApplicationInfo(this);
319 application = new ApplicationInfo(this);
266− application->setDesktopFile("/usr/share/applications/facebook-webapp.desktop");
320+ application->setAppId("facebook-webapp");
267 application->setName("Facebook");
321 application->setName("Facebook");
268− application->setIcon("facebook");
322+ application->setIcon(QUrl("facebook"));
269− application->setExec("/usr/bin/webbrowser-app --chromeless http://m.facebook.com");
270 application->setStage(ApplicationInfo::SideStage);
323 application->setStage(ApplicationInfo::SideStage);
271− application->setHandle(nextHandle++);
272 generateQmlStrings(application);
324 generateQmlStrings(application);
273 m_availableApplications.append(application);
325 m_availableApplications.append(application);
274
326
275 application = new ApplicationInfo(this);
327 application = new ApplicationInfo(this);
276− application->setDesktopFile("/usr/share/applications/webbrowser-app.desktop");
328+ application->setAppId("webbrowser-app");
277 application->setName("Browser");
329 application->setName("Browser");
278− application->setIcon("browser");
330+ application->setIcon(QUrl("browser"));
279− application->setExec("/usr/bin/webbrowser-app");
280− application->setHandle(nextHandle++);
281 generateQmlStrings(application);
331 generateQmlStrings(application);
282 m_availableApplications.append(application);
332 m_availableApplications.append(application);
283
333
284 application = new ApplicationInfo(this);
334 application = new ApplicationInfo(this);
285− application->setDesktopFile("/usr/share/applications/twitter-webapp.desktop");
335+ application->setAppId("twitter-webapp");
286 application->setName("Twitter");
336 application->setName("Twitter");
287− application->setIcon("twitter");
337+ application->setIcon(QUrl("twitter"));
288− application->setExec("/usr/bin/webbrowser-app --chromeless http://www.twitter.com");
289 application->setStage(ApplicationInfo::SideStage);
338 application->setStage(ApplicationInfo::SideStage);
290− application->setHandle(nextHandle++);
291 generateQmlStrings(application);
339 generateQmlStrings(application);
292 m_availableApplications.append(application);
340 m_availableApplications.append(application);
293
341
294 application = new ApplicationInfo(this);
342 application = new ApplicationInfo(this);
295− application->setDesktopFile("/usr/share/applications/gmail-webapp.desktop");
343+ application->setAppId("gmail-webapp");
296 application->setName("GMail");
344 application->setName("GMail");
297− application->setIcon("gmail");
345+ application->setIcon(QUrl("gmail"));
298− application->setExec("/usr/bin/webbrowser-app --chromeless http://m.gmail.com");
299− application->setHandle(nextHandle++);
300 m_availableApplications.append(application);
346 m_availableApplications.append(application);
301
347
302 application = new ApplicationInfo(this);
348 application = new ApplicationInfo(this);
303− application->setDesktopFile("/usr/share/applications/ubuntu-weather-app.desktop");
349+ application->setAppId("ubuntu-weather-app");
304 application->setName("Weather");
350 application->setName("Weather");
305− application->setIcon("weather");
351+ application->setIcon(QUrl("weather"));
306− application->setExec("/usr/bin/qmlscene /usr/share/ubuntu-weather-app/ubuntu-weather-app.qml");
307 application->setStage(ApplicationInfo::SideStage);
352 application->setStage(ApplicationInfo::SideStage);
308− application->setHandle(nextHandle++);
309 generateQmlStrings(application);
353 generateQmlStrings(application);
310 m_availableApplications.append(application);
354 m_availableApplications.append(application);
311
355
312 application = new ApplicationInfo(this);
356 application = new ApplicationInfo(this);
313− application->setDesktopFile("/usr/share/applications/notes-app.desktop");
357+ application->setAppId("notes-app");
314 application->setName("Notepad");
358 application->setName("Notepad");
315− application->setIcon("notepad");
359+ application->setIcon(QUrl("notepad"));
316− application->setExec("/usr/bin/qmlscene /usr/share/notes-app/NotesApp.qml");
317 application->setStage(ApplicationInfo::SideStage);
360 application->setStage(ApplicationInfo::SideStage);
318− application->setHandle(nextHandle++);
319 m_availableApplications.append(application);
361 m_availableApplications.append(application);
320
362
321 application = new ApplicationInfo(this);
363 application = new ApplicationInfo(this);
322− application->setDesktopFile("/usr/share/applications/calendar-app.desktop");
364+ application->setAppId("calendar-app");
323 application->setName("Calendar");
365 application->setName("Calendar");
324− application->setIcon("calendar");
366+ application->setIcon(QUrl("calendar"));
325− application->setExec("/usr/bin/qmlscene /usr/share/calendar-app/calendar.qml");
326 application->setStage(ApplicationInfo::SideStage);
367 application->setStage(ApplicationInfo::SideStage);
327− application->setHandle(nextHandle++);
328 m_availableApplications.append(application);
368 m_availableApplications.append(application);
329
369
330 application = new ApplicationInfo(this);
370 application = new ApplicationInfo(this);
331− application->setDesktopFile("/usr/share/applications/mediaplayer-app.desktop");
371+ application->setAppId("mediaplayer-app");
332 application->setName("Media Player");
372 application->setName("Media Player");
333− application->setIcon("mediaplayer-app");
373+ application->setIcon(QUrl("mediaplayer-app"));
334 application->setFullscreen(true);
374 application->setFullscreen(true);
335− application->setExec("/usr/bin/mediaplayer-app");
336− application->setHandle(nextHandle++);
337 m_availableApplications.append(application);
375 m_availableApplications.append(application);
338
376
339 application = new ApplicationInfo(this);
377 application = new ApplicationInfo(this);
340− application->setDesktopFile("/usr/share/applications/evernote.desktop");
378+ application->setAppId("evernote");
341 application->setName("Evernote");
379 application->setName("Evernote");
342− application->setIcon("evernote");
380+ application->setIcon(QUrl("evernote"));
343− application->setHandle(nextHandle++);
344 m_availableApplications.append(application);
381 m_availableApplications.append(application);
345
382
346 application = new ApplicationInfo(this);
383 application = new ApplicationInfo(this);
347− application->setDesktopFile("/usr/share/applications/map.desktop");
384+ application->setAppId("map");
348 application->setName("Map");
385 application->setName("Map");
349− application->setIcon("map");
386+ application->setIcon(QUrl("map"));
350− application->setHandle(nextHandle++);
351 generateQmlStrings(application);
387 generateQmlStrings(application);
352 m_availableApplications.append(application);
388 m_availableApplications.append(application);
353
389
354 application = new ApplicationInfo(this);
390 application = new ApplicationInfo(this);
355− application->setDesktopFile("/usr/share/applications/pinterest.desktop");
391+ application->setAppId("pinterest");
356 application->setName("Pinterest");
392 application->setName("Pinterest");
357− application->setIcon("pinterest");
393+ application->setIcon(QUrl("pinterest"));
358− application->setHandle(nextHandle++);
359 m_availableApplications.append(application);
394 m_availableApplications.append(application);
360
395
361 application = new ApplicationInfo(this);
396 application = new ApplicationInfo(this);
362− application->setDesktopFile("/usr/share/applications/soundcloud.desktop");
397+ application->setAppId("soundcloud");
363 application->setName("SoundCloud");
398 application->setName("SoundCloud");
364− application->setIcon("soundcloud");
399+ application->setIcon(QUrl("soundcloud"));
365− application->setHandle(nextHandle++);
366 m_availableApplications.append(application);
400 m_availableApplications.append(application);
367
401
368 application = new ApplicationInfo(this);
402 application = new ApplicationInfo(this);
369− application->setDesktopFile("/usr/share/applications/wikipedia.desktop");
403+ application->setAppId("wikipedia");
370 application->setName("Wikipedia");
404 application->setName("Wikipedia");
371− application->setIcon("wikipedia");
405+ application->setIcon(QUrl("wikipedia"));
372− application->setHandle(nextHandle++);
373 m_availableApplications.append(application);
406 m_availableApplications.append(application);
374
407
375 application = new ApplicationInfo(this);
408 application = new ApplicationInfo(this);
376− application->setDesktopFile("/usr/share/applications/youtube.desktop");
409+ application->setAppId("youtube");
377 application->setName("YouTube");
410 application->setName("YouTube");
378− application->setIcon("youtube");
411+ application->setIcon(QUrl("youtube"));
379− application->setHandle(nextHandle++);
380 m_availableApplications.append(application);
412 m_availableApplications.append(application);
381}
413}
382
414
tests/mocks/Ubuntu/Application/ApplicationManager.h → tests/mocks/Unity/Application/ApplicationManager.hrenamed+26−26
@@ -20,12 +20,15 @@
20#include <QObject>
20#include <QObject>
21#include <QList>
21#include <QList>
22#include <QStringList>
22#include <QStringList>
23−#include "ApplicationListModel.h"
24#include "ApplicationInfo.h"
23#include "ApplicationInfo.h"
25
24
25+// unity-api
26+#include <unity/shell/application/ApplicationManagerInterface.h>
27+
26class QQuickItem;
28class QQuickItem;
29+using namespace unity::shell::application;
27
30
28−class ApplicationManager : public QObject {
31+class ApplicationManager : public ApplicationManagerInterface {
29 Q_OBJECT
32 Q_OBJECT
30 Q_ENUMS(Role)
33 Q_ENUMS(Role)
31 Q_ENUMS(StageHint)
34 Q_ENUMS(StageHint)
@@ -39,12 +42,6 @@ class ApplicationManager : public QObject {
39 Q_PROPERTY(int sideStageWidth READ sideStageWidth)
42 Q_PROPERTY(int sideStageWidth READ sideStageWidth)
40 Q_PROPERTY(StageHint stageHint READ stageHint)
43 Q_PROPERTY(StageHint stageHint READ stageHint)
41 Q_PROPERTY(FormFactorHint formFactorHint READ formFactorHint)
44 Q_PROPERTY(FormFactorHint formFactorHint READ formFactorHint)
42− Q_PROPERTY(ApplicationListModel* mainStageApplications READ mainStageApplications)
43− Q_PROPERTY(ApplicationListModel* sideStageApplications READ sideStageApplications)
44− Q_PROPERTY(ApplicationInfo* mainStageFocusedApplication READ mainStageFocusedApplication
45− NOTIFY mainStageFocusedApplicationChanged)
46− Q_PROPERTY(ApplicationInfo* sideStageFocusedApplication READ sideStageFocusedApplication
47− NOTIFY sideStageFocusedApplicationChanged)
48
45
49 Q_PROPERTY(bool fake READ fake CONSTANT)
46 Q_PROPERTY(bool fake READ fake CONSTANT)
50
47
@@ -64,7 +61,8 @@ class ApplicationManager : public QObject {
64 DesktopFormFactor, PhoneFormFactor, TabletFormFactor
61 DesktopFormFactor, PhoneFormFactor, TabletFormFactor
65 };
62 };
66 enum FavoriteApplication {
63 enum FavoriteApplication {
67− CameraApplication, GalleryApplication, BrowserApplication, ShareApplication
64+ CameraApplication, GalleryApplication, BrowserApplication, ShareApplication,
65+ PhoneApplication, DialerApplication, MessagingApplication, AddressbookApplication
68 };
66 };
69 enum Flag {
67 enum Flag {
70 NoFlag = 0x0,
68 NoFlag = 0x0,
@@ -77,29 +75,34 @@ class ApplicationManager : public QObject {
77 int sideStageWidth() const;
75 int sideStageWidth() const;
78 StageHint stageHint() const;
76 StageHint stageHint() const;
79 FormFactorHint formFactorHint() const;
77 FormFactorHint formFactorHint() const;
80− ApplicationListModel* mainStageApplications() const;
81− ApplicationListModel* sideStageApplications() const;
82− ApplicationInfo* mainStageFocusedApplication() const;
83− ApplicationInfo* sideStageFocusedApplication() const;
84
78
85 bool fake() { return true; }
79 bool fake() { return true; }
86
80
87− Q_INVOKABLE void focusApplication(int handle);
81+ // QAbstractItemModel methods.
82+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
83+ QVariant data(const QModelIndex& index, int role) const override;
84+ Q_INVOKABLE ApplicationInfo *get(int index) const override;
85+ Q_INVOKABLE ApplicationInfo *findApplication(const QString &appId) const override;
86+
87+ Q_INVOKABLE void move(int from, int to);
88+
89+ // Application control methods
90+ Q_INVOKABLE bool focusApplication(const QString &appId) override;
88− Q_INVOKABLE void unfocusCurrentApplication(StageHint stageHint);
91+ Q_INVOKABLE void unfocusCurrentApplication() override;
89− Q_INVOKABLE ApplicationInfo* startProcess(QString desktopFile,
92+ Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, const QStringList &arguments = QStringList()) override;
90− ExecFlags flags,
91− QStringList arguments = QStringList());
93+ Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments = QStringList());
92− Q_INVOKABLE void stopProcess(ApplicationInfo* application);
94+ Q_INVOKABLE bool stopApplication(const QString &appId) override;
93− Q_INVOKABLE void startWatcher() {}
95+
96+ QString focusedApplicationId() const override;
94
97
95 Q_SIGNALS:
98 Q_SIGNALS:
96 void keyboardHeightChanged();
99 void keyboardHeightChanged();
97 void keyboardVisibleChanged();
100 void keyboardVisibleChanged();
98− void mainStageFocusedApplicationChanged();
99− void sideStageFocusedApplicationChanged();
100 void focusRequested(FavoriteApplication favoriteApplication);
101 void focusRequested(FavoriteApplication favoriteApplication);
101
102
102 private:
103 private:
104+ void add(ApplicationInfo *application);
105+ void remove(ApplicationInfo* application);
103 void showApplicationWindow(ApplicationInfo *application);
106 void showApplicationWindow(ApplicationInfo *application);
104 void buildListOfAvailableApplications();
107 void buildListOfAvailableApplications();
105 void generateQmlStrings(ApplicationInfo *application);
108 void generateQmlStrings(ApplicationInfo *application);
@@ -109,10 +112,7 @@ class ApplicationManager : public QObject {
109 void createSideStage();
112 void createSideStage();
110 int m_keyboardHeight;
113 int m_keyboardHeight;
111 bool m_keyboardVisible;
114 bool m_keyboardVisible;
112− ApplicationListModel* m_mainStageApplications;
115+ QList<ApplicationInfo*> m_runningApplications;
113− ApplicationListModel* m_sideStageApplications;
114− ApplicationInfo* m_mainStageFocusedApplication;
115− ApplicationInfo* m_sideStageFocusedApplication;
116 QList<ApplicationInfo*> m_availableApplications;
116 QList<ApplicationInfo*> m_availableApplications;
117 QQmlComponent *m_mainStageComponent;
117 QQmlComponent *m_mainStageComponent;
118 QQuickItem *m_mainStage;
118 QQuickItem *m_mainStage;
tests/mocks/Unity/Application/CMakeLists.txtnew file+37
@@ -0,0 +1,37 @@
1+include(FindPkgConfig)
2+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application)
3+
4+set(FakeUnityApplicationQml_SOURCES
5+ plugin.cpp
6+ ApplicationInfo.cpp
7+ ApplicationImage.cpp
8+ ApplicationManager.cpp
9+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
10+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
11+)
12+
13+add_library(FakeUnityApplicationQml MODULE ${FakeUnityApplicationQml_SOURCES})
14+
15+qt5_use_modules(FakeUnityApplicationQml Core Quick)
16+
17+# copy files into build directory for shadow builds
18+add_custom_target(UnityAppQmlDirFile ALL
19+ COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" ${CMAKE_CURRENT_BINARY_DIR}
20+ DEPENDS qmldir
21+)
22+add_custom_target(InputFilterAreaQmlFile ALL
23+ COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/InputFilterArea.qml" ${CMAKE_CURRENT_BINARY_DIR}
24+ DEPENDS InputFilterArea.qml
25+)
26+add_custom_target(OSKControllerQmlFile ALL
27+ COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/OSKController.qml" ${CMAKE_CURRENT_BINARY_DIR}
28+ DEPENDS OSKController.qml
29+)
30+
31+install(TARGETS FakeUnityApplicationQml
32+ DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Unity/Application
33+ )
34+
35+install(FILES qmldir InputFilterArea.qml OSKController.qml
36+ DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Unity/Application
37+ )
tests/mocks/Ubuntu/Application/InputFilterArea.qml → tests/mocks/Unity/Application/InputFilterArea.qmlrenamed

The content is the same.

tests/mocks/Unity/Application/OSKController.qmlnew file+20
@@ -0,0 +1,20 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0;
18+
19+Item {
20+}
tests/mocks/Ubuntu/Application/plugin.cpp → tests/mocks/Unity/Application/plugin.cpprenamed+6−5
@@ -17,7 +17,6 @@
17#include "plugin.h"
17#include "plugin.h"
18#include "ApplicationInfo.h"
18#include "ApplicationInfo.h"
19#include "ApplicationImage.h"
19#include "ApplicationImage.h"
20−#include "ApplicationListModel.h"
21#include "ApplicationManager.h"
20#include "ApplicationManager.h"
22
21
23#include <qqml.h>
22#include <qqml.h>
@@ -28,11 +27,13 @@ static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scrip
28 return new ApplicationManager();
27 return new ApplicationManager();
29}
28}
30
29
31−void FakeUbuntuApplicationQmlPlugin::registerTypes(const char *uri)
30+void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
32{
31{
33− qmlRegisterSingletonType<ApplicationManager>(
32+ qmlRegisterUncreatableType<unity::shell::application::ApplicationManagerInterface>(uri, 0, 1, "ApplicationManagerInterface", "Abstract interface. Cannot be created in QML");
34− uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
33+ qmlRegisterSingletonType<ApplicationManager>(uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
34+
35+ qmlRegisterUncreatableType<unity::shell::application::ApplicationInfoInterface>(uri, 0, 1, "ApplicationInfoInterface", "Abstract interface. Cannot be created in QML");
35 qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo");
36 qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo");
37+
36 qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");
38 qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage");
37− qmlRegisterType<ApplicationListModel>(uri, 0, 1, "ApplicationListModel");
38}
39}
tests/mocks/Ubuntu/Application/plugin.h → tests/mocks/Unity/Application/plugin.hrenamed+1−1
@@ -19,7 +19,7 @@
19
19
20#include <QQmlExtensionPlugin>
20#include <QQmlExtensionPlugin>
21
21
22−class FakeUbuntuApplicationQmlPlugin : public QQmlExtensionPlugin
22+class FakeUnityApplicationQmlPlugin : public QQmlExtensionPlugin
23{
23{
24 Q_OBJECT
24 Q_OBJECT
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
tests/mocks/Unity/Application/qmldirnew file+4
@@ -0,0 +1,4 @@
1+module Unity.Application
2+plugin FakeUnityApplicationQml
3+InputFilterArea 0.1 InputFilterArea.qml
4+OSKController 0.1 OSKController.qml
tests/mocks/Unity/CMakeLists.txt+4
@@ -1,3 +1,4 @@
1+add_subdirectory(Application)
1add_subdirectory(Indicators)
2add_subdirectory(Indicators)
2
3
3pkg_search_module(DEE dee-1.0 REQUIRED)
4pkg_search_module(DEE dee-1.0 REQUIRED)
@@ -17,6 +18,9 @@ add_definitions(-DQT_NO_KEYWORDS)
17
18
18set(UnityQML_SOURCES
19set(UnityQML_SOURCES
19 ${CMAKE_SOURCE_DIR}/plugins/Unity/categoryresults.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Unity/categoryresults.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/Unity/iconutils.cpp
22+ fake_applications_scope.cpp
23+ fake_preview.cpp
20 fake_scope.cpp
24 fake_scope.cpp
21 fake_scopes.cpp
25 fake_scopes.cpp
22 fake_categories.cpp
26 fake_categories.cpp
tests/mocks/Unity/Indicators/CMakeLists.txt+1
@@ -13,6 +13,7 @@ set(IndicatorsFakeQml_SOURCES
13 fakeplugin.cpp
13 fakeplugin.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
14 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
15 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
16+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/visibleindicatorsmodel.cpp
16)
17)
17
18
18set(IndicatorsFakeQml_RESOURCES
19set(IndicatorsFakeQml_RESOURCES
tests/mocks/Unity/Indicators/IndicatorsModel.qml+47−76
@@ -23,82 +23,53 @@ ListModel {
23
23
24 function load() {}
24 function load() {}
25
25
26− ListElement {
26+ // Need to do this dynamically.
27+ // Apparently ListModels dont order roles visually when using ListElement
28+ // which throws off the filter for non-visible indicators.
29+ Component.onCompleted: {
27− identifier: "indicator-fake1";
30+ append({ "identifier": "indicator-fake1",
28− priority: 0
31+ "priority": 0,
29− title: "Menu1";
32+ "title": "Menu1",
30− description: "";
33+ "description": "",
31− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
34+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget1.qml",
32− pageSource: "qrc:/tests/indciators/qml/fake_menu_page1.qml";
35+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page1.qml",
33− indicatorProperties: "";
34− isValid: "";
35− }
36− ListElement {
37− identifier: "indicator-fake2";
38− priority: 1
39− title: "Menu2";
40− description: "";
41− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget2.qml";
42− pageSource: "qrc:/tests/indciators/qml/fake_menu_page2.qml";
43− indicatorProperties: "";
36+ "indicatorProperties": ""
44− isValid: "";
45− }
46− ListElement {
47− identifier: "indicator-fake3";
48− priority: 2
49− title: "Menu3";
50− description: "";
51− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget3.qml";
52− pageSource: "qrc:/tests/indciators/qml/fake_menu_page3.qml";
53− indicatorProperties: "";
54− isValid: "";
55− }
37+ });
56− ListElement {
57− identifier: "indicator-fake4";
58− priority: 3
59− title: "Menu4";
60− description: "";
61− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget4.qml";
62− pageSource: "qrc:/tests/indciators/qml/fake_menu_page4.qml";
63− indicatorProperties: "";
64− isValid: "";
65− }
66− ListElement {
67− identifier: "indicator-fake5";
68− priority: 4
69− title: "Menu5";
70− description: "";
71− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget5.qml";
72− pageSource: "qrc:/tests/indciators/qml/fake_menu_page5.qml";
73− indicatorProperties: "";
74− isValid: "";
75− }
76
38
77− function data(row, role) {
39+ append({ "identifier": "indicator-fake2",
78− if (role == Indicators.IndicatorsModelRole.Identifier) {
40+ "priority": 1,
79− return get(row).identifier;
41+ "title": "Menu2",
80− }
42+ "description": "",
81− else if (role == Indicators.IndicatorsModelRole.Priority) {
43+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget2.qml",
82− return get(row).priority
44+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page2.qml",
83− }
45+ "indicatorProperties": ""
84− else if (role == Indicators.IndicatorsModelRole.Title) {
85− return get(row).title
86− }
46+ });
87− else if (role == Indicators.IndicatorsModelRole.Description) {
47+
88− return get(row).description
48+ append({ "identifier": "indicator-fake3",
89− }
49+ "priority": 2,
90− else if (role == Indicators.IndicatorsModelRole.WidgetSource) {
50+ "title": "Menu3",
91− return get(row).widgetSource
51+ "description": "",
92− }
52+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget3.qml",
93− else if (role == Indicators.IndicatorsModelRole.PageSource) {
53+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page3.qml",
94− return get(row).pageSource
54+ "indicatorProperties": ""
95− }
96− else if (role == Indicators.IndicatorsModelRole.IndicatorProperties) {
97− return get(row).indicatorProperties
98− }
99− else if (role == Indicators.IndicatorsModelRole.IsValid) {
100− return get(row).isValid
101− }
102− }
55+ });
103
56
57+ append({ "identifier": "indicator-fake4",
58+ "priority": 3,
59+ "title": "Menu4",
60+ "description": "",
61+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget4.qml",
62+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page4.qml",
63+ "indicatorProperties": ""
64+ });
65+
66+ append({ "identifier": "indicator-fake5",
67+ "priority": 4,
68+ "title": "Menu5",
69+ "description": "",
70+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget5.qml",
71+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page5.qml",
72+ "indicatorProperties": ""
73+ });
74+ }
104}
75}
tests/mocks/Unity/Indicators/fakeplugin.cpp+2
@@ -25,12 +25,14 @@
25// local
25// local
26#include "indicators.h"
26#include "indicators.h"
27#include "menucontentactivator.h"
27#include "menucontentactivator.h"
28+#include "visibleindicatorsmodel.h"
28
29
29void IndicatorsFakePlugin::registerTypes(const char * uri)
30void IndicatorsFakePlugin::registerTypes(const char * uri)
30{
31{
31 Q_INIT_RESOURCE(indicators_fake);
32 Q_INIT_RESOURCE(indicators_fake);
32
33
33 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
34 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
35+ qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
34
36
35 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
37 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
36 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
38 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
tests/mocks/Unity/Indicators/qml/fake_menu_widget2.qml+2
@@ -20,4 +20,6 @@ Rectangle {
20 width: 40
20 width: 40
21 height: 40
21 height: 40
22 color : "yellow"
22 color : "yellow"
23+
24+ enabled: false
23}
25}
tests/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
1include(FindPkgConfig)
1include(FindPkgConfig)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
3
3
4add_definitions(-DTOP_SRCDIR="${CMAKE_SOURCE_DIR}")
4add_definitions(-DTOP_SRCDIR="${CMAKE_SOURCE_DIR}")
5
5
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+16
@@ -33,6 +33,7 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
33 m_recent(false),
33 m_recent(false),
34 m_progress(-1),
34 m_progress(-1),
35 m_count(0),
35 m_count(0),
36+ m_focused(false),
36 m_quickList(new MockQuickListModel(this))
37 m_quickList(new MockQuickListModel(this))
37{
38{
38
39
@@ -128,6 +129,21 @@ void MockLauncherItem::setCount(int count)
128 }
129 }
129}
130}
130
131
132+bool MockLauncherItem::focused() const
133+{
134+ return m_focused;
135+}
136+
137+void MockLauncherItem::setFocused(bool focused)
138+{
139+ if (m_focused != focused)
140+ {
141+ m_focused = focused;
142+ Q_EMIT focusedChanged(focused);
143+ }
144+}
145+
146+
131unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
147unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
132{
148{
133 return m_quickList;
149 return m_quickList;
tests/mocks/Unity/Launcher/MockLauncherItem.h+4
@@ -52,6 +52,9 @@ public:
52 int count() const;
52 int count() const;
53 void setCount(int count);
53 void setCount(int count);
54
54
55+ bool focused() const;
56+ void setFocused(bool focused);
57+
55 unity::shell::launcher::QuickListModelInterface *quickList() const;
58 unity::shell::launcher::QuickListModelInterface *quickList() const;
56
59
57private:
60private:
@@ -64,6 +67,7 @@ private:
64 bool m_recent;
67 bool m_recent;
65 int m_progress;
68 int m_progress;
66 int m_count;
69 int m_count;
70+ bool m_focused;
67 MockQuickListModel *m_quickList;
71 MockQuickListModel *m_quickList;
68};
72};
69
73
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+14−2
@@ -25,6 +25,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
26 item->setProgress(0);
26 item->setProgress(0);
27 m_list.append(item);
27 m_list.append(item);
28+ item->setFocused(true);
28 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29 item->setProgress(10);
30 item->setProgress(10);
30 m_list.append(item);
31 m_list.append(item);
@@ -49,6 +50,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
49 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50 item->setProgress(50);
51 item->setProgress(50);
51 item->setCount(5);
52 item->setCount(5);
53+ item->setFocused(true);
52 m_list.append(item);
54 m_list.append(item);
53 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
55 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
54 m_list.append(item);
56 m_list.append(item);
@@ -69,8 +71,6 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
69 LauncherItemInterface *item = m_list.at(index.row());
71 LauncherItemInterface *item = m_list.at(index.row());
70 switch(role)
72 switch(role)
71 {
73 {
72− case RoleDesktopFile:
73− return item->desktopFile();
74 case RoleName:
74 case RoleName:
75 return item->name();
75 return item->name();
76 case RoleIcon:
76 case RoleIcon:
@@ -85,6 +85,8 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
85 return item->progress();
85 return item->progress();
86 case RoleCount:
86 case RoleCount:
87 return item->count();
87 return item->count();
88+ case RoleFocused:
89+ return item->focused();
88 }
90 }
89
91
90 return QVariant();
92 return QVariant();
@@ -180,3 +182,13 @@ void MockLauncherModel::setUser(const QString &username)
180 Q_UNUSED(username)
182 Q_UNUSED(username)
181 // TODO: implement this...
183 // TODO: implement this...
182}
184}
185+
186+void MockLauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager)
187+{
188+ Q_UNUSED(applicationManager)
189+}
190+
191+unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
192+{
193+ return nullptr;
194+}
tests/mocks/Unity/Launcher/MockLauncherModel.h+3
@@ -45,6 +45,9 @@ public:
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
46 Q_INVOKABLE void setUser(const QString &username);
46 Q_INVOKABLE void setUser(const QString &username);
47
47
48+ unity::shell::application::ApplicationManagerInterface *applicationManager() const;
49+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
50+
48Q_SIGNALS:
51Q_SIGNALS:
49 void quickListTriggered(const QString &appId, int index);
52 void quickListTriggered(const QString &appId, int index);
50
53
tests/mocks/Unity/fake_applications_scope.cppnew file+68
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "fake_applications_scope.h"
18+
19+static const gchar * categories_model_schema[] = {
20+ "s", //ID
21+ "s", // DISPLAY_NAME
22+ "s", // ICON_HINT
23+ "s", // RENDERER_NAME
24+ "a{sv}" // HINTS
25+};
26+
27+ApplicationsScope::ApplicationsScope(bool visible, QObject* parent)
28+ : Scope(parent)
29+{
30+ m_id = "applications.scope";
31+ m_name = "Applications";
32+ m_visible = visible;
33+ m_categories->setModel(createCategoriesModel());
34+}
35+
36+DeeModel* ApplicationsScope::createCategoriesModel()
37+{
38+ DeeModel* category_model = dee_sequence_model_new();
39+ dee_model_set_schema_full(category_model, categories_model_schema, G_N_ELEMENTS(categories_model_schema));
40+ GVariant* hints = g_variant_new_array(g_variant_type_element(G_VARIANT_TYPE_VARDICT), NULL, 0);
41+
42+ GVariant* children[1];
43+ children[0] = g_variant_new_dict_entry(g_variant_new_string("content-type"),
44+ g_variant_new_variant(g_variant_new_string("apps")));
45+ GVariant* recentHints = g_variant_new_array(g_variant_type_element(G_VARIANT_TYPE_VARDICT), children, 1);
46+ dee_model_append(category_model,
47+ "recent",
48+ "Recent",
49+ "gtk-apply",
50+ "special",
51+ recentHints);
52+
53+ dee_model_append(category_model,
54+ "installed",
55+ "Installed",
56+ "gtk-apply",
57+ "grid",
58+ hints);
59+
60+ dee_model_append(category_model,
61+ "suggested",
62+ "Suggested",
63+ "gtk-apply",
64+ "grid",
65+ hints);
66+
67+ return category_model;
68+}
tests/mocks/Unity/fake_applications_scope.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FAKE_APPLICATIONS_SCOPE_H
18+#define FAKE_APPLICATIONS_SCOPE_H
19+
20+#include "fake_scope.h"
21+
22+class ApplicationsScope : public Scope
23+{
24+ Q_OBJECT
25+
26+public:
27+ ApplicationsScope(bool visible, QObject* parent = 0);
28+
29+private:
30+ DeeModel* createCategoriesModel();
31+};
32+
33+#endif // FAKE_APPLICATIONS_SCOPE_H
tests/mocks/Unity/fake_categories.cpp+84−12
@@ -24,11 +24,20 @@
24
24
25#define CATEGORY_COLUMN 2
25#define CATEGORY_COLUMN 2
26
26
27+// TODO: use something from libunity once it's public
28+enum CategoryColumn {
29+ ID,
30+ DISPLAY_NAME,
31+ ICON_HINT,
32+ RENDERER_NAME,
33+ HINTS
34+};
35+
27Categories::Categories(QObject* parent)
36Categories::Categories(QObject* parent)
28 : DeeListModel(parent)
37 : DeeListModel(parent)
29{
38{
30 // FIXME: need to clean up unused filters on countChanged
39 // FIXME: need to clean up unused filters on countChanged
31− m_roles[Categories::RoleId] = "id";
40+ m_roles[Categories::RoleCategoryId] = "categoryId";
32 m_roles[Categories::RoleName] = "name";
41 m_roles[Categories::RoleName] = "name";
33 m_roles[Categories::RoleIcon] = "icon";
42 m_roles[Categories::RoleIcon] = "icon";
34 m_roles[Categories::RoleRenderer] = "renderer";
43 m_roles[Categories::RoleRenderer] = "renderer";
@@ -36,7 +45,6 @@ Categories::Categories(QObject* parent)
36 m_roles[Categories::RoleHints] = "hints";
45 m_roles[Categories::RoleHints] = "hints";
37 m_roles[Categories::RoleResults] = "results";
46 m_roles[Categories::RoleResults] = "results";
38 m_roles[Categories::RoleCount] = "count";
47 m_roles[Categories::RoleCount] = "count";
39− m_roles[Categories::RoleCategoryIndex] = "categoryIndex";
40
48
41 // TODO This should not be needed but accumulatting the count changes
49 // TODO This should not be needed but accumulatting the count changes
42 // makes the visualization more stable and also makes crashes on fast
50 // makes the visualization more stable and also makes crashes on fast
@@ -74,6 +82,25 @@ Categories::onCountChanged()
74 }
82 }
75}
83}
76
84
85+void Categories::onRowCountChanged()
86+{
87+ QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>(sender());
88+ // find the corresponding category index
89+ for (auto iter = m_overriddenCategories.begin(); iter != m_overriddenCategories.end(); ++iter) {
90+ if (iter.value() == model) {
91+ for (int i = 0; i < rowCount(); i++) {
92+ auto id = data(index(i), RoleCategoryId).toString();
93+ if (id != iter.key()) continue;
94+ QVector<int> roles;
95+ roles.append(RoleCount);
96+ QModelIndex changedIndex = index(i);
97+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
98+ break;
99+ }
100+ }
101+ }
102+}
103+
77void
104void
78Categories::onEmitCountChanged()
105Categories::onEmitCountChanged()
79{
106{
@@ -93,6 +120,41 @@ Categories::roleNames() const
93 return m_roles;
120 return m_roles;
94}
121}
95
122
123+void Categories::onOverrideModelDestroyed()
124+{
125+ QObject* model = sender();
126+ auto iter = m_overriddenCategories.begin();
127+ while (iter != m_overriddenCategories.end()) {
128+ if (iter.value() == model) {
129+ iter = m_overriddenCategories.erase(iter);
130+ continue;
131+ }
132+ ++iter;
133+ }
134+}
135+
136+void Categories::overrideResults(const QString& categoryId, QAbstractItemModel* model)
137+{
138+ m_overriddenCategories[categoryId] = model;
139+ // watch the model
140+ connect(model, &QObject::destroyed, this, &Categories::onOverrideModelDestroyed);
141+ connect(model, &QAbstractItemModel::rowsInserted, this, &Categories::onRowCountChanged);
142+ connect(model, &QAbstractItemModel::rowsRemoved, this, &Categories::onRowCountChanged);
143+ connect(model, &QAbstractItemModel::modelReset, this, &Categories::onRowCountChanged);
144+
145+ // emit the dataChanged signal if the category is already in the model
146+ for (int i = 0; i < rowCount(); i++) {
147+ auto id = data(index(i), RoleCategoryId).toString();
148+ if (id != categoryId) continue;
149+ QVector<int> roles;
150+ roles.append(RoleCount);
151+ roles.append(RoleResults);
152+ QModelIndex changedIndex = index(i);
153+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
154+ break;
155+ }
156+}
157+
96void Categories::setResultModel(DeeModel* model)
158void Categories::setResultModel(DeeModel* model)
97{
159{
98 // FIXME: should ref it
160 // FIXME: should ref it
@@ -154,27 +216,37 @@ Categories::data(const QModelIndex& index, int role) const
154 }
216 }
155
217
156 switch (role) {
218 switch (role) {
157− case RoleId:
219+ case RoleCategoryId:
158− return DeeListModel::data(index, 0); //ID
220+ return DeeListModel::data(index, CategoryColumn::ID);
159 case RoleName:
221 case RoleName:
160− return DeeListModel::data(index, 1); //DISPLAY_NAME
222+ return DeeListModel::data(index, CategoryColumn::DISPLAY_NAME);
161 case RoleIcon:
223 case RoleIcon:
162− return DeeListModel::data(index, 2); //ICON_HINT
224+ return DeeListModel::data(index, CategoryColumn::ICON_HINT);
163 case RoleRenderer:
225 case RoleRenderer:
164− return DeeListModel::data(index, 3); //RENDERER_NAME
226+ return DeeListModel::data(index, CategoryColumn::RENDERER_NAME);
165 case RoleContentType:
227 case RoleContentType:
166 {
228 {
167− auto hints = DeeListModel::data(index, 4).toHash();
229+ auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
168− return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
230+ return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
169 }
231 }
170 case RoleHints:
232 case RoleHints:
171− return DeeListModel::data(index, 4); //HINTS
233+ return DeeListModel::data(index, CategoryColumn::HINTS);
172 case RoleResults:
234 case RoleResults:
235+ if (m_overriddenCategories.size() > 0)
236+ {
237+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
238+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
239+ return QVariant::fromValue(m_overriddenCategories[id]);
240+ }
173 return QVariant::fromValue(getFilter(index.row()));
241 return QVariant::fromValue(getFilter(index.row()));
174 case RoleCount:
242 case RoleCount:
243+ if (m_overriddenCategories.size() > 0)
244+ {
245+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
246+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
247+ return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
248+ }
175 return QVariant::fromValue(getFilter(index.row())->rowCount());
249 return QVariant::fromValue(getFilter(index.row())->rowCount());
176− case RoleCategoryIndex:
177− return QVariant::fromValue(index.row());
178 default:
250 default:
179 return QVariant();
251 return QVariant();
180 }
252 }
tests/mocks/Unity/fake_categories.h+7−3
@@ -34,17 +34,18 @@ class Categories : public DeeListModel
34public:
34public:
35 Categories(QObject* parent = 0);
35 Categories(QObject* parent = 0);
36 enum Roles {
36 enum Roles {
37− RoleId,
37+ RoleCategoryId,
38 RoleName,
38 RoleName,
39 RoleIcon,
39 RoleIcon,
40 RoleRenderer,
40 RoleRenderer,
41 RoleContentType,
41 RoleContentType,
42 RoleHints,
42 RoleHints,
43 RoleResults,
43 RoleResults,
44− RoleCount,
44+ RoleCount
45− RoleCategoryIndex
46 };
45 };
47
46
47+ Q_INVOKABLE void overrideResults(const QString& categoryId, QAbstractItemModel* model);
48+
48 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
49 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
49
50
50 QHash<int, QByteArray> roleNames() const;
51 QHash<int, QByteArray> roleNames() const;
@@ -54,7 +55,9 @@ public:
54
55
55private Q_SLOTS:
56private Q_SLOTS:
56 void onCountChanged();
57 void onCountChanged();
58+ void onRowCountChanged();
57 void onEmitCountChanged();
59 void onEmitCountChanged();
60+ void onOverrideModelDestroyed();
58
61
59private:
62private:
60 DeeModel* getResultsForCategory(unsigned index) const;
63 DeeModel* getResultsForCategory(unsigned index) const;
@@ -64,6 +67,7 @@ private:
64 QTimer m_timer;
67 QTimer m_timer;
65 QSet<DeeListModel*> m_timerFilters;
68 QSet<DeeListModel*> m_timerFilters;
66 QHash<int, QByteArray> m_roles;
69 QHash<int, QByteArray> m_roles;
70+ QMap<QString, QAbstractItemModel*> m_overriddenCategories;
67 mutable QMap<int, DeeListModel*> m_filters;
71 mutable QMap<int, DeeListModel*> m_filters;
68};
72};
69
73
tests/mocks/Unity/fake_preview.cppnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "fake_preview.h"
18+
19+QString Preview::rendererName() const
20+{
21+ return "foo";
22+}
23+
24+QString Preview::title() const
25+{
26+ return "Title";
27+}
28+
29+QString Preview::subtitle() const
30+{
31+ return "Subtitle";
32+}
33+
34+QString Preview::description() const
35+{
36+ return "Description";
37+}
38+
39+QVariant Preview::actions()
40+{
41+ return QVariant();
42+}
43+
44+QVariant Preview::infoHints()
45+{
46+ return QVariant();
47+}
48+
49+QVariantMap Preview::infoHintsHash() const
50+{
51+ return QVariantMap();
52+}
53+
54+QString Preview::image() const
55+{
56+ return "";
57+}
58+
59+QString Preview::imageSourceUri() const
60+{
61+ return "";
62+}
63+
64+void Preview::execute(const QString& actionId, const QHash<QString, QVariant>& hints)
65+{
66+}
tests/mocks/Unity/fake_preview.hnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FAKE_PREVIEW_H
18+#define FAKE_PREVIEW_H
19+
20+// Qt
21+#include <QObject>
22+#include <QString>
23+#include <QMetaType>
24+#include <QList>
25+#include <QVariantMap>
26+
27+class Q_DECL_EXPORT Preview : public QObject
28+{
29+ Q_OBJECT
30+
31+ Q_PROPERTY(QString rendererName READ rendererName NOTIFY previewChanged)
32+ Q_PROPERTY(QString title READ title NOTIFY previewChanged)
33+ Q_PROPERTY(QString subtitle READ subtitle NOTIFY previewChanged)
34+ Q_PROPERTY(QString description READ description NOTIFY previewChanged)
35+ Q_PROPERTY(QVariant actions READ actions NOTIFY previewChanged)
36+ Q_PROPERTY(QVariant infoHints READ infoHints NOTIFY previewChanged)
37+ Q_PROPERTY(QVariantMap infoMap READ infoHintsHash NOTIFY previewChanged)
38+ Q_PROPERTY(QString image READ image NOTIFY previewChanged)
39+ Q_PROPERTY(QString imageSourceUri READ imageSourceUri NOTIFY previewChanged)
40+
41+public:
42+ explicit Preview(QObject *parent = 0);
43+
44+ QString rendererName() const;
45+ QString title() const;
46+ QString subtitle() const;
47+ QString description() const;
48+ QVariant actions();
49+ QVariant infoHints();
50+ QVariantMap infoHintsHash() const;
51+ QString image() const;
52+ QString imageSourceUri() const;
53+
54+ Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
55+
56+Q_SIGNALS:
57+ void previewChanged();
58+};
59+
60+Q_DECLARE_METATYPE(Preview *)
61+
62+#endif // FAKE_PREVIEW_H
tests/mocks/Unity/fake_scope.cpp+33−9
@@ -20,6 +20,8 @@
20#include <dee.h>
20#include <dee.h>
21#include "paths.h"
21#include "paths.h"
22
22
23+#include "fake_preview.h"
24+
23static DeeModel* create_categories_model(unsigned category_count);
25static DeeModel* create_categories_model(unsigned category_count);
24static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
26static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
25
27
@@ -127,6 +129,36 @@ void Scope::setNoResultsHint(const QString& str) {
127 }
129 }
128}
130}
129
131
132+void Scope::activate(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
133+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
134+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata)
135+{
136+ Q_UNUSED(uri);
137+ Q_UNUSED(icon_hint);
138+ Q_UNUSED(category);
139+ Q_UNUSED(result_type);
140+ Q_UNUSED(mimetype);
141+ Q_UNUSED(title);
142+ Q_UNUSED(comment);
143+ Q_UNUSED(dnd_uri);
144+ Q_UNUSED(metadata);
145+}
146+
147+void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
148+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
149+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata)
150+{
151+ Q_UNUSED(uri);
152+ Q_UNUSED(icon_hint);
153+ Q_UNUSED(category);
154+ Q_UNUSED(result_type);
155+ Q_UNUSED(mimetype);
156+ Q_UNUSED(title);
157+ Q_UNUSED(comment);
158+ Q_UNUSED(dnd_uri);
159+ Q_UNUSED(metadata);
160+}
161+
130static const gchar * categories_model_schema[] = {
162static const gchar * categories_model_schema[] = {
131 "s", //ID
163 "s", //ID
132 "s", // DISPLAY_NAME
164 "s", // DISPLAY_NAME
@@ -168,14 +200,6 @@ static const gchar * results_model_schema[] = {
168 "a{sv}" // METADATA
200 "a{sv}" // METADATA
169};
201};
170
202
171−static const gchar * icons[] = {
172− "Applications.png",
173− "Home.png",
174− "Music.png",
175− "People.png",
176− "Videos.png",
177−};
178−
179DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
203DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
180 DeeModel* results_model = dee_sequence_model_new();
204 DeeModel* results_model = dee_sequence_model_new();
181 dee_model_set_schema_full(results_model, results_model_schema, G_N_ELEMENTS(results_model_schema));
205 dee_model_set_schema_full(results_model, results_model_schema, G_N_ELEMENTS(results_model_schema));
@@ -187,7 +211,7 @@ DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
187
211
188 dee_model_append(results_model,
212 dee_model_append(results_model,
189 ("uri://result."+std::to_string(i)).c_str(),
213 ("uri://result."+std::to_string(i)).c_str(),
190− (shellAppDirectory() + "Dash/graphics/scopeIcons/" + (icons[i%G_N_ELEMENTS(icons)])).toLatin1().data(),
214+ (shellAppDirectory() + "graphics/applicationIcons/dash.png").toLatin1().data(),
191 category,
215 category,
192 0,
216 0,
193 "application/x-desktop",
217 "application/x-desktop",
tests/mocks/Unity/fake_scope.h+15−1
@@ -21,6 +21,8 @@
21#include <QObject>
21#include <QObject>
22#include "fake_categories.h"
22#include "fake_categories.h"
23
23
24+class Preview;
25+
24class Scope : public QObject
26class Scope : public QObject
25{
27{
26 Q_OBJECT
28 Q_OBJECT
@@ -63,6 +65,13 @@ public:
63 void setNoResultsHint(const QString& hint);
65 void setNoResultsHint(const QString& hint);
64 void setFormFactor(const QString& form_factor);
66 void setFormFactor(const QString& form_factor);
65
67
68+ Q_INVOKABLE void activate(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
69+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
70+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
71+ Q_INVOKABLE void preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
72+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
73+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
74+
66Q_SIGNALS:
75Q_SIGNALS:
67 void idChanged(const QString&);
76 void idChanged(const QString&);
68 void nameChanged(const QString&);
77 void nameChanged(const QString&);
@@ -78,7 +87,12 @@ Q_SIGNALS:
78 void noResultsHintChanged();
87 void noResultsHintChanged();
79 void formFactorChanged();
88 void formFactorChanged();
80
89
81−private:
90+ // signals triggered by activate(..) or preview(..) requests.
91+ void previewReady(Preview *preview);
92+ void activateApplication(const QString &desktop);
93+
94+protected:
95+
82 QString m_id;
96 QString m_id;
83 QString m_iconHint;
97 QString m_iconHint;
84 QString m_description;
98 QString m_description;
tests/mocks/Unity/fake_scopes.cpp+4−1
@@ -19,6 +19,9 @@
19// Self
19// Self
20#include "fake_scopes.h"
20#include "fake_scopes.h"
21
21
22+// Local
23+#include "fake_applications_scope.h"
24+
22// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
25// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
23
26
24// Qt
27// Qt
@@ -53,7 +56,7 @@ void Scopes::updateScopes()
53 addScope(new Scope("MockScope1", "People", true, this));
56 addScope(new Scope("MockScope1", "People", true, this));
54 addScope(new Scope("MockScope2", "Music", false, this));
57 addScope(new Scope("MockScope2", "Music", false, this));
55 addScope(new Scope("home.scope", "Home", true, this));
58 addScope(new Scope("home.scope", "Home", true, this));
56− addScope(new Scope("applications.scope", "Applications", true, this));
59+ addScope(new ApplicationsScope(true, this));
57 addScope(new Scope("MockScope5", "Videos", true, this));
60 addScope(new Scope("MockScope5", "Videos", true, this));
58
61
59 if (!m_loaded) {
62 if (!m_loaded) {
tests/plugins/AccountsService/CMakeLists.txt+1
@@ -25,6 +25,7 @@ qt5_use_modules(mock-server Core DBus)
25
25
26add_executable(test-accountsservice
26add_executable(test-accountsservice
27 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
27 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
28+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
28 client.cpp
29 client.cpp
29 )
30 )
30qt5_use_modules(test-accountsservice Core DBus Test)
31qt5_use_modules(test-accountsservice Core DBus Test)
tests/plugins/AccountsService/client.cpp+19−7
@@ -18,6 +18,7 @@
18 */
18 */
19
19
20#include "AccountsService.h"
20#include "AccountsService.h"
21+#include "AccountsServiceDBusAdaptor.h"
21#include <QSignalSpy>
22#include <QSignalSpy>
22#include <QTest>
23#include <QTest>
23
24
@@ -30,17 +31,28 @@ private Q_SLOTS:
30 void testInvalids()
31 void testInvalids()
31 {
32 {
32 // Test various invalid calls
33 // Test various invalid calls
33− AccountsService session;
34+ AccountsServiceDBusAdaptor session;
34− QCOMPARE(session.getUserProperty("NOPE", "demo-edges"), QVariant());
35+ QCOMPARE(session.getUserProperty("NOPE", "com.canonical.unity.AccountsService", "demo-edges"), QVariant());
35− QCOMPARE(session.getUserProperty("testuser", "NOPE"), QVariant());
36+ QCOMPARE(session.getUserProperty("testuser", "com.canonical.unity.AccountsService", "NOPE"), QVariant());
37+ }
38+
39+ void testGetSetServiceDBusAdaptor()
40+ {
41+ AccountsServiceDBusAdaptor session;
42+ session.setUserProperty("testuser", "com.canonical.unity.AccountsService", "demo-edges", QVariant(true));
43+ QCOMPARE(session.getUserProperty("testuser", "com.canonical.unity.AccountsService", "demo-edges"), QVariant(true));
44+ session.setUserProperty("testuser", "com.canonical.unity.AccountsService", "demo-edges", QVariant(false));
45+ QCOMPARE(session.getUserProperty("testuser", "com.canonical.unity.AccountsService", "demo-edges"), QVariant(false));
36 }
46 }
37
47
38− void testGetSet()
48+ void testGetSetService()
39 {
49 {
40 AccountsService session;
50 AccountsService session;
41− QCOMPARE(session.getUserProperty("testuser", "demo-edges"), QVariant(true));
51+ session.setUser("testuser");
42− session.setUserProperty("testuser", "demo-edges", QVariant(false));
52+ session.setDemoEdges(true);
53+ QCOMPARE(session.getDemoEdges(), true);
54+ session.setDemoEdges(false);
43− QCOMPARE(session.getUserProperty("testuser", "demo-edges"), QVariant(false));
55+ QCOMPARE(session.getDemoEdges(), false);
44 }
56 }
45};
57};
46
58
tests/plugins/Unity/CMakeLists.txt+4
@@ -5,6 +5,7 @@ pkg_check_modules(UNITYCORE REQUIRED unity-core-6.0)
5pkg_check_modules(LIBUNITYPROTO REQUIRED unity-protocol-private)
5pkg_check_modules(LIBUNITYPROTO REQUIRED unity-protocol-private)
6pkg_check_modules(LIBUNITY REQUIRED unity)
6pkg_check_modules(LIBUNITY REQUIRED unity)
7pkg_check_modules(LIBSIGCPP REQUIRED sigc++-2.0)
7pkg_check_modules(LIBSIGCPP REQUIRED sigc++-2.0)
8+pkg_check_modules(QTDEE REQUIRED libdee-qt5>=3.2)
8
9
9include_directories(
10include_directories(
10 ${Unity-qml_SOURCE_DIR}
11 ${Unity-qml_SOURCE_DIR}
@@ -14,6 +15,7 @@ include_directories(
14 ${LIBUNITY_INCLUDE_DIRS}
15 ${LIBUNITY_INCLUDE_DIRS}
15 ${LIBSIGCPP_INCLUDE_DIRS}
16 ${LIBSIGCPP_INCLUDE_DIRS}
16 ${UNITYCORE_INCLUDE_DIRS}
17 ${UNITYCORE_INCLUDE_DIRS}
18+ ${QTDEE_INCLUDE_DIRS}
17 )
19 )
18
20
19macro(run_tests)
21macro(run_tests)
@@ -36,6 +38,7 @@ macro(run_tests)
36 ${LIBUNITY_LDFLAGS}
38 ${LIBUNITY_LDFLAGS}
37 ${LIBUNITYPROTO_LDFLAGS}
39 ${LIBUNITYPROTO_LDFLAGS}
38 ${LIBSIGCPP_LDFLAGS}
40 ${LIBSIGCPP_LDFLAGS}
41+ ${QTDEE_LDFLAGS}
39 )
42 )
40
43
41 set(_test_list "${_test_list};${_test}")
44 set(_test_list "${_test_list};${_test}")
@@ -46,4 +49,5 @@ run_tests(
46 signalslisttest
49 signalslisttest
47 previewbindingstest
50 previewbindingstest
48 filtersbindingstest
51 filtersbindingstest
52+ resultstest
49 )
53 )
tests/plugins/Unity/Launcher/CMakeLists.txt+53−4
@@ -1,22 +1,71 @@
1+pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=3)
3+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=1)
4+
1include_directories(
5include_directories(
2 ${CMAKE_CURRENT_BINARY_DIR}
6 ${CMAKE_CURRENT_BINARY_DIR}
3 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
7 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
4 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
8 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
5 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend
9 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend
10+ ${GSETTINGS_QT_INCLUDE_DIRS}
6 )
11 )
7
12
8add_definitions(-DSM_BUSNAME=sessionBus)
13add_definitions(-DSM_BUSNAME=sessionBus)
9add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
14add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
15+add_definitions(-DLAUNCHER_TESTING)
10
16
17+### LauncherBackendTest
11−set(testCommand ${CMAKE_CURRENT_BINARY_DIR}/launcherbackendtestExec
18+set(testBackendCommand ${CMAKE_CURRENT_BINARY_DIR}/launcherbackendtestExec
12 -o ${CMAKE_BINARY_DIR}/launcherbackendtest.xml,xunitxml
19 -o ${CMAKE_BINARY_DIR}/launcherbackendtest.xml,xunitxml
13 -o -,txt)
20 -o -,txt)
14−add_test(NAME launcherbackendtest COMMAND ${testCommand})
21+add_test(NAME launcherbackendtest COMMAND ${testBackendCommand})
15−add_custom_target(launcherbackendtest ${testCommand})
22+add_custom_target(launcherbackendtest ${testBackendCommand})
16add_executable(launcherbackendtestExec
23add_executable(launcherbackendtestExec
17 launcherbackendtest.cpp
24 launcherbackendtest.cpp
18− ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
25+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp
26 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp
27 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp
21 )
28 )
29+target_link_libraries(launcherbackendtestExec ${GSETTINGS_QT_LDFLAGS})
22qt5_use_modules(launcherbackendtestExec Test Core DBus)
30qt5_use_modules(launcherbackendtestExec Test Core DBus)
31+
32+### LauncherModelTest
33+set(testModelCommand ${CMAKE_CURRENT_BINARY_DIR}/launchermodeltestExec
34+ -o ${CMAKE_BINARY_DIR}/launchermodeltest.xml,xunitxml
35+ -o -,txt)
36+add_test(NAME launchermodeltest COMMAND ${testModelCommand})
37+add_custom_target(launchermodeltest ${testModelCommand})
38+add_executable(launchermodeltestExec
39+ launchermodeltest.cpp
40+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
41+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
42+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
43+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
44+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp
45+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp
46+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
47+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
48+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
49+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
50+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
51+ )
52+target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
53+qt5_use_modules(launchermodeltestExec Test Core DBus)
54+
55+# copy .desktop files into build directory for shadow builds
56+file(GLOB DESKTOP_FILES *.desktop)
57+
58+foreach(DESKTOP_FILE ${DESKTOP_FILES})
59+ file(COPY "${DESKTOP_FILE}"
60+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
61+ )
62+endforeach()
63+
64+# copy .svg files into build directory for shadow builds
65+file(GLOB DESKTOP_FILES *.svg)
66+
67+foreach(DESKTOP_FILE ${DESKTOP_FILES})
68+ file(COPY "${DESKTOP_FILE}"
69+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
70+ )
71+endforeach()
tests/plugins/Unity/Launcher/click-icon.desktopnew file+4
@@ -0,0 +1,4 @@
1+[Desktop Entry]
2+Name=Click Package Icon
3+Icon=click-icon.svg
4+Path=/path/to/some/click/app
tests/plugins/Unity/Launcher/click-icon.svgnew file

No changes to the content.

tests/plugins/Unity/Launcher/launcherbackendtest.cpp+41−42
@@ -20,73 +20,72 @@
20#include "launcherbackend.h"
20#include "launcherbackend.h"
21
21
22#include <QtTest>
22#include <QtTest>
23−#include <QDebug>
24
23
25class LauncherBackendTest : public QObject
24class LauncherBackendTest : public QObject
26{
25{
27 Q_OBJECT
26 Q_OBJECT
28
27
29private Q_SLOTS:
28private Q_SLOTS:
29+
30+ void initTestCase()
31+ {
32+ // As we want to test absolute file paths in the .desktop file, we need to modify the
33+ // sample file and replace the path in there with the current build dir.
34+ QSettings settings(QDir::currentPath() + "/click-icon.desktop", QSettings::IniFormat);
35+ settings.setValue("Desktop Entry/Path", QDir::currentPath());
36+ }
37+
30 void testFileNames()
38 void testFileNames()
31 {
39 {
32 LauncherBackend backend(false);
40 LauncherBackend backend(false);
33
41
34− backend.setStoredApplications(QStringList() << "relative.path" << "/full/path");
42+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon" << "invalid");
35− QCOMPARE(backend.storedApplications(), QStringList() << "/usr/share/applications/relative.path" << "/full/path");
43+ QCOMPARE(backend.storedApplications(), QStringList() << "rel-icon" << "abs-icon");
36−
37− QCOMPARE(backend.desktopFile("one.desktop"), QString("/usr/share/applications/one.desktop"));
38− QCOMPARE(backend.desktopFile("two"), QString("/usr/share/applications/two"));
39− QCOMPARE(backend.desktopFile("/full"), QString("/full"));
40−
41− QCOMPARE(backend.desktopFile("/usr/share/applications/one.desktop"), QString("/usr/share/applications/one.desktop"));
42 }
44 }
43
45
44− void testDesktopReading()
46+ void testPinning()
45 {
47 {
46 LauncherBackend backend(false);
48 LauncherBackend backend(false);
47
49
48− QCOMPARE(backend.displayName(SRCDIR "/rel-icon.desktop"), QString("Relative Icon"));
50+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon");
49− QCOMPARE(backend.icon(SRCDIR "/rel-icon.desktop"), QString("image://gicon/rel-icon"));
51+ QCOMPARE(backend.isPinned("rel-icon"), false);
52+ QCOMPARE(backend.isPinned("abs-icon"), false);
50
53
51− QCOMPARE(backend.displayName(SRCDIR "/abs-icon.desktop"), QString("Absolute Icon"));
54+ backend.setPinned("rel-icon", true);
52− QCOMPARE(backend.icon(SRCDIR "/abs-icon.desktop"), QString("/path/to/icon.png"));
55+ QCOMPARE(backend.isPinned("rel-icon"), true);
53
56
54− QCOMPARE(backend.displayName(SRCDIR "/no-icon.desktop"), QString("No Icon"));
57+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon" << "no-name");
55− QCOMPARE(backend.icon(SRCDIR "/no-icon.desktop"), QString(""));
58+ QCOMPARE(backend.isPinned("rel-icon"), true);
59+ QCOMPARE(backend.isPinned("no-name"), false);
56
60
57− QCOMPARE(backend.displayName(SRCDIR "/no-name.desktop"), QString(""));
61+ backend.setPinned("no-name", true);
58− QCOMPARE(backend.icon(SRCDIR "/no-name.desktop"), QString("image://gicon/no-name"));
62+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon");
59−
63+ QCOMPARE(backend.isPinned("rel-icon"), true);
60− QCOMPARE(backend.displayName(SRCDIR "/no-exist.desktop"), QString(""));
64+ QCOMPARE(backend.isPinned("no-name"), false); // doesn't exist anymore!
61− QCOMPARE(backend.icon(SRCDIR "/no-exist.desktop"), QString(""));
62 }
65 }
63
66
64− void testPinning()
67+ void testIcon_data() {
65− {
68+ QTest::addColumn<QString>("appId");
66− LauncherBackend backend(false);
69+ QTest::addColumn<QString>("expectedIcon");
67
70
68− // Confirm that default entries are all pinned
71+ // Needs to expand a relative icon to the absolute path
69− auto defaultApps = backend.storedApplications();
72+ QTest::newRow("relative icon path") << "rel-icon" << QDir::currentPath() + "/rel-icon.svg";
70− QVERIFY(defaultApps.size() > 0);
71− for (auto app: defaultApps) {
72− QCOMPARE(backend.isPinned(app), true);
73− }
74
73
75− backend.setStoredApplications(QStringList() << "one" << "two");
74+ // In case an icon is not found on disk, it needs to fallback on image://theme/ for it
76− QCOMPARE(backend.isPinned("one"), false);
75+ QTest::newRow("fallback on theme") << "abs-icon" << "image://theme//path/to/icon.png";
77− QCOMPARE(backend.isPinned("two"), false);
78
76
79− backend.setPinned("two", true);
77+ // Click packages have a relative icon path but an absolute path as a separate entry
80− QCOMPARE(backend.isPinned("two"), true);
78+ QTest::newRow("click package icon") << "click-icon" << QDir::currentPath() + "/click-icon.svg";
79+ }
80+
81+ void testIcon() {
82+ QFETCH(QString, appId);
83+ QFETCH(QString, expectedIcon);
81
84
85+ LauncherBackend backend(false);
82− backend.setStoredApplications(QStringList() << "one" << "two" << "three");
86+ backend.setStoredApplications(QStringList() << appId);
83− QCOMPARE(backend.isPinned("two"), true);
84− QCOMPARE(backend.isPinned("three"), false);
85
87
86− backend.setPinned("three", true);
87− backend.setStoredApplications(QStringList() << "one" << "two");
88− QCOMPARE(backend.isPinned("two"), true);
88+ QCOMPARE(backend.icon(appId), expectedIcon);
89− QCOMPARE(backend.isPinned("three"), false); // doesn't exist anymore!
90 }
89 }
91};
90};
92
91
tests/plugins/Unity/Launcher/launchermodeltest.cppnew file+275
@@ -0,0 +1,275 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Michael Zanetti <michael.zanetti@canonical.com>
18+ */
19+
20+// unity-api
21+#include <unity/shell/launcher/LauncherModelInterface.h>
22+#include <unity/shell/application/ApplicationInfoInterface.h>
23+
24+#include "launcheritem.h"
25+#include "launchermodel.h"
26+
27+#include <QtTest>
28+
29+// This is a mock, specifically to test the LauncherModel
30+class MockApp: public unity::shell::application::ApplicationInfoInterface
31+{
32+ Q_OBJECT
33+public:
34+ MockApp(const QString &appId, QObject *parent = 0): ApplicationInfoInterface(appId, parent), m_appId(appId), m_focused(false) { }
35+ QString appId() const { return m_appId; }
36+ QString name() const { return "mock"; }
37+ QString comment() const { return "this is a mock"; }
38+ QUrl icon() const { return QUrl(); }
39+ ApplicationInfoInterface::Stage stage() const { return ApplicationInfoInterface::MainStage; }
40+ ApplicationInfoInterface::State state() const { return ApplicationInfoInterface::Running; }
41+ bool focused() const { return m_focused; }
42+
43+ // Methods used for mocking (not in the interface)
44+ void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
45+private:
46+ QString m_appId;
47+ bool m_focused;
48+};
49+
50+// This is a mock, specifically to test the LauncherModel
51+class MockAppManager: public unity::shell::application::ApplicationManagerInterface
52+{
53+ Q_OBJECT
54+public:
55+ MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
56+ int rowCount(const QModelIndex &) const { return m_list.count(); }
57+ QVariant data(const QModelIndex &, int ) const { return QVariant(); }
58+ QString focusedApplicationId() const {
59+ Q_FOREACH(MockApp *app, m_list) {
60+ if (app->focused()) return app->appId();
61+ }
62+ return QString();
63+ }
64+ unity::shell::application::ApplicationInfoInterface *get(int index) const { return m_list.at(index); }
65+ unity::shell::application::ApplicationInfoInterface *findApplication(const QString &appId) const {
66+ Q_FOREACH(MockApp* app, m_list) {
67+ if (app->appId() == appId) {
68+ return app;
69+ }
70+ }
71+ return nullptr;
72+ }
73+ unity::shell::application::ApplicationInfoInterface *startApplication(const QString &, const QStringList &) { return nullptr; }
74+ bool stopApplication(const QString &) { return false; }
75+ bool focusApplication(const QString &appId) {
76+ Q_FOREACH(MockApp* app, m_list) {
77+ app->setFocused(app->appId() == appId);
78+ }
79+ Q_EMIT focusedApplicationIdChanged();
80+ return true;
81+ }
82+
83+ void unfocusCurrentApplication() { }
84+
85+ void addApplication(MockApp *app) {
86+ beginInsertRows(QModelIndex(), count(), count());
87+ m_list.append(app);
88+ endInsertRows();
89+ }
90+ void removeApplication(int index) {
91+ beginRemoveRows(QModelIndex(), index, index);
92+ m_list.takeAt(index)->deleteLater();
93+ endRemoveRows();
94+ }
95+
96+private:
97+ QList<MockApp*> m_list;
98+};
99+
100+class LauncherModelTest : public QObject
101+{
102+ Q_OBJECT
103+
104+private:
105+ LauncherModel *launcherModel;
106+ MockAppManager *appManager;
107+
108+private Q_SLOTS:
109+
110+ void initTestCase() {
111+ launcherModel = new LauncherModel(this);
112+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
113+
114+ appManager = new MockAppManager(this);
115+ launcherModel->setApplicationManager(appManager);
116+ }
117+
118+ // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
119+ void init() {
120+ appManager->addApplication(new MockApp("abs-icon"));
121+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
122+
123+ appManager->addApplication(new MockApp("no-icon"));
124+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
125+ }
126+
127+ // Removing apps from appmanager and launcher as pinned ones would stick
128+ void cleanup() {
129+ while (appManager->count() > 0) {
130+ appManager->removeApplication(0);
131+ }
132+ while (launcherModel->rowCount(QModelIndex()) > 0) {
133+ launcherModel->requestRemove(launcherModel->get(0)->appId());
134+ }
135+ }
136+
137+ void testMove() {
138+ QCOMPARE(launcherModel->get(0)->pinned(), false);
139+ QCOMPARE(launcherModel->get(1)->pinned(), false);
140+
141+ LauncherItemInterface *item0BeforeMove = launcherModel->get(0);
142+ LauncherItemInterface *item1BeforeMove = launcherModel->get(1);
143+ launcherModel->move(1, 0);
144+
145+ QCOMPARE(item0BeforeMove, launcherModel->get(1));
146+ QCOMPARE(item1BeforeMove, launcherModel->get(0));
147+
148+ // moved item must be pinned now
149+ QCOMPARE(item0BeforeMove->pinned(), false);
150+ QCOMPARE(item1BeforeMove->pinned(), true);
151+ }
152+
153+ void testPinning() {
154+ QCOMPARE(launcherModel->get(0)->pinned(), false);
155+ QCOMPARE(launcherModel->get(1)->pinned(), false);
156+ launcherModel->pin(launcherModel->get(0)->appId());
157+ QCOMPARE(launcherModel->get(0)->pinned(), true);
158+ QCOMPARE(launcherModel->get(1)->pinned(), false);
159+ }
160+
161+ void testRemove_data() {
162+
163+ QTest::addColumn<bool>("pinned");
164+ QTest::addColumn<bool>("running");
165+
166+ QTest::newRow("non-pinned, running") << false << true;
167+ QTest::newRow("pinned, running") << true << false;
168+ QTest::newRow("pinned, non-running") << true << false;
169+ }
170+
171+ void testRemove() {
172+ QFETCH(bool, pinned);
173+ QFETCH(bool, running);
174+
175+ // In the beginning we always have two items
176+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
177+
178+ // pin one if required
179+ if (pinned) {
180+ launcherModel->pin(launcherModel->get(1)->appId());
181+ }
182+
183+ // stop it if required
184+ if (!running) {
185+ appManager->removeApplication(1);
186+ }
187+
188+ // Now remove it
189+ launcherModel->requestRemove(launcherModel->get(1)->appId());
190+
191+ if (running) {
192+ // both apps are running, both apps must still be here
193+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
194+
195+ // Item must be unpinned now
196+ QCOMPARE(launcherModel->get(1)->pinned(), false);
197+
198+ } else if (pinned) {
199+ // Item 1 must go away, item 0 is here to stay
200+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
201+ }
202+
203+ // done our checks. now stop the app if was still running
204+ if (running) {
205+ appManager->removeApplication(1);
206+ }
207+
208+ // It needs to go away in any case now
209+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
210+ }
211+
212+ void testQuickListPinningRemoving() {
213+ // we start with 2 unpinned items
214+ QCOMPARE(launcherModel->get(0)->pinned(), false);
215+ QCOMPARE(launcherModel->get(1)->pinned(), false);
216+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
217+
218+ // find the Pin item in the quicklist
219+ QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
220+ int pinActionIndex = -1;
221+ for (int i = 0; i < model->rowCount(QModelIndex()); ++i) {
222+ if (model->get(i).actionId() == "pin_item") {
223+ pinActionIndex = i;
224+ break;
225+ }
226+ }
227+ QVERIFY(pinActionIndex >= 0);
228+
229+ // trigger pin item quicklist action => Item must be pinned now.
230+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
231+ QCOMPARE(launcherModel->get(0)->pinned(), true);
232+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
233+
234+ // quicklist needs to transform to remove item. trigger it and check it item goes away
235+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
236+ QCOMPARE(launcherModel->get(0)->pinned(), false);
237+
238+ // still needs to be here as the app is still here
239+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
240+ // close the app
241+ appManager->removeApplication(0);
242+ // Now it needs to go away
243+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
244+ }
245+
246+ void testApplicationFocused() {
247+ // all apps unfocused at beginning...
248+ QCOMPARE(launcherModel->get(0)->focused(), false);
249+ QCOMPARE(launcherModel->get(1)->focused(), false);
250+
251+ appManager->focusApplication("abs-icon");
252+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
253+ QCOMPARE(launcherModel->get(0)->focused(), true);
254+ QCOMPARE(launcherModel->get(1)->focused(), false);
255+
256+ appManager->focusApplication("no-icon");
257+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
258+ QCOMPARE(launcherModel->get(0)->focused(), false);
259+ QCOMPARE(launcherModel->get(1)->focused(), true);
260+ }
261+
262+ void testClosingApps() {
263+ // At the start there are 2 items. Let's pin one.
264+ launcherModel->pin("abs-icon");
265+ while (appManager->count() > 0) {
266+ appManager->removeApplication(0);
267+ }
268+ // The pinned one needs to stay, the other needs to disappear
269+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
270+ QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
271+ }
272+};
273+
274+QTEST_GUILESS_MAIN(LauncherModelTest)
275+#include "launchermodeltest.moc"
tests/plugins/Unity/Launcher/rel-icon.desktop+1−1
@@ -1,3 +1,3 @@
1[Desktop Entry]
1[Desktop Entry]
2Name=Relative Icon
2Name=Relative Icon
3−Icon=rel-icon
3+Icon=rel-icon.svg
tests/plugins/Unity/Launcher/rel-icon.svgnew file

No changes to the content.

tests/plugins/Unity/resultstest.cppnew file+104
@@ -0,0 +1,104 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Michal Hruby <michal.hruby@canonical.com>
18+ */
19+
20+#include <QTest>
21+#include <dee.h>
22+
23+#include "resultstest.h"
24+#include "categoryresults.h"
25+
26+static DeeModel* createBackendModel()
27+{
28+ auto model = dee_sequence_model_new();
29+ dee_model_set_schema(model, "s", "s", "u", "u", "s", "s", "s", "s", "a{sv}", NULL);
30+
31+ return model;
32+}
33+
34+void ResultsTest::testAllColumns()
35+{
36+ auto deeModel = createBackendModel();
37+ GVariantBuilder builder;
38+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
39+ g_variant_builder_add(&builder, "{sv}", "metadata-field", g_variant_new_string("foo"));
40+ dee_model_append (deeModel,
41+ "test:uri",
42+ "themed-icon",
43+ 4,
44+ 0,
45+ "application/octet-stream",
46+ "Test",
47+ "Comment",
48+ "test:dnd-uri",
49+ g_variant_builder_end(&builder));
50+
51+ CategoryResults* results = new CategoryResults(this);
52+ results->setModel(deeModel);
53+
54+ auto index = results->index(0, 0); // there's just one result
55+ QCOMPARE(index.data(CategoryResults::Roles::RoleUri).toString(), QString("test:uri"));
56+ QCOMPARE(index.data(CategoryResults::Roles::RoleIconHint).toString(), QString("image://theme/themed-icon"));
57+ QCOMPARE(index.data(CategoryResults::Roles::RoleCategory).toInt(), 4);
58+ QCOMPARE(index.data(CategoryResults::Roles::RoleMimetype).toString(), QString("application/octet-stream"));
59+ QCOMPARE(index.data(CategoryResults::Roles::RoleTitle).toString(), QString("Test"));
60+ QCOMPARE(index.data(CategoryResults::Roles::RoleComment).toString(), QString("Comment"));
61+ QCOMPARE(index.data(CategoryResults::Roles::RoleDndUri).toString(), QString("test:dnd-uri"));
62+ auto metadata = index.data(CategoryResults::Roles::RoleMetadata).toHash();
63+ QCOMPARE(metadata["metadata-field"].toString(), QString("foo"));
64+}
65+
66+void ResultsTest::testIconColumn_data()
67+{
68+ QTest::addColumn<QString>("giconString");
69+ QTest::addColumn<QString>("result");
70+
71+ QTest::newRow("absolute path") << "/usr/share/icons/example.png" << "/usr/share/icons/example.png";
72+ QTest::newRow("file uri") << "file:///usr/share/icons/example.png" << "file:///usr/share/icons/example.png";
73+ QTest::newRow("http uri") << "http://images.ubuntu.com/example.jpg" << "http://images.ubuntu.com/example.jpg";
74+ QTest::newRow("themed icon") << "accessories-other" << "image://theme/accessories-other";
75+ QTest::newRow("fileicon") << ". GFileIcon http://example.org/resource.gif" << "http://example.org/resource.gif";
76+ QTest::newRow("themedicon") << ". GThemedIcon accessories-other accessories generic" << "image://theme/accessories-other,accessories,generic";
77+ QTest::newRow("annotatedicon") << ". UnityProtocolAnnotatedIcon %7B'base-icon':%20%3C'.%20GThemedIcon%20accessories-other%20accessories%20generic'%3E%7D" << "image://theme/accessories-other,accessories,generic";
78+}
79+
80+void ResultsTest::testIconColumn()
81+{
82+ QFETCH(QString, giconString);
83+ QFETCH(QString, result);
84+ auto deeModel = createBackendModel();
85+ dee_model_append (deeModel,
86+ "test:uri",
87+ giconString.toLocal8Bit().constData(),
88+ 0,
89+ 0,
90+ "application/octet-stream",
91+ "Test",
92+ "",
93+ "test:dnd-uri",
94+ g_variant_new_array(g_variant_type_element(G_VARIANT_TYPE_VARDICT), NULL, 0));
95+
96+ CategoryResults* results = new CategoryResults(this);
97+ results->setModel(deeModel);
98+
99+ auto index = results->index(0, 0); // there's just one result
100+ auto transformedIcon = index.data(CategoryResults::Roles::RoleIconHint).toString();
101+ QCOMPARE(transformedIcon, result);
102+}
103+
104+QTEST_MAIN(ResultsTest)
tests/plugins/Unity/resultstest.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Michal Hruby <michal.hruby@canonical.com>
18+ */
19+
20+#ifndef RESULTSTEST_H
21+#define RESULTSTEST_H
22+
23+#include <QObject>
24+
25+class ResultsTest : public QObject
26+{
27+ Q_OBJECT
28+
29+ private Q_SLOTS:
30+ void testAllColumns();
31+ void testIconColumn_data();
32+ void testIconColumn();
33+};
34+
35+#endif
tests/qmltests/CMakeLists.txt+1−1
@@ -66,7 +66,7 @@ add_qml_test(Launcher Launcher IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltes
66add_qml_test(Notifications Notifications IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_SOURCE_DIR}/tests/mocks)
66add_qml_test(Notifications Notifications IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_SOURCE_DIR}/tests/mocks)
67add_qml_test(Panel IndicatorRow)
67add_qml_test(Panel IndicatorRow)
68add_qml_test(Panel Indicators IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
68add_qml_test(Panel Indicators IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
69−add_qml_test(Panel MenuContent)
69+add_qml_test(Panel MenuContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
70add_qml_test(Panel Panel IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
70add_qml_test(Panel Panel IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
71add_qml_test(Panel SearchIndicator)
71add_qml_test(Panel SearchIndicator)
72add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
72add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
tests/qmltests/Components/tst_Stage.qml+1−1
@@ -18,7 +18,7 @@ import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import ".."
19import ".."
20import "../../../Components"
20import "../../../Components"
21−import Ubuntu.Application 0.1
21+import Unity.Application 0.1
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23import Unity.Test 0.1 as UT
23import Unity.Test 0.1 as UT
24import "tst_Stage"
24import "tst_Stage"
tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml+55−37
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19−import Ubuntu.Application 0.1
20import "../../../../Dash/Apps"
19import "../../../../Dash/Apps"
21import Unity.Test 0.1 as UT
20import Unity.Test 0.1 as UT
22
21
@@ -29,8 +28,12 @@ Item {
29
28
30 property bool sideStageEnabled: false
29 property bool sideStageEnabled: false
31
30
32− function stopProcess(application) {
31+ function stopApplication(appId) {
32+ for (var i=0, len=fakeRunningAppsModel.count; i<len; i++) {
33+ if (appId == fakeRunningAppsModel.get(i).appId) {
33− fakeRunningAppsModel.remove(application)
34+ fakeRunningAppsModel.remove(i)
35+ }
36+ }
34 }
37 }
35 }
38 }
36
39
@@ -40,47 +43,62 @@ Item {
40 property bool stageScreenshotsReady: false
43 property bool stageScreenshotsReady: false
41 property var applicationManager: fakeApplicationManager
44 property var applicationManager: fakeApplicationManager
42
45
43− function activateApplication(desktopFile) {
46+ function activateApplication(appId) {
44 }
47 }
45 }
48 }
46
49
50+ ListModel {
47− ApplicationListModel { id: fakeRunningAppsModel }
51+ id: fakeRunningAppsModel
48−
52+
49− ApplicationInfo {
53+ function contains(appId) {
50− id: phoneApp
54+ for (var i=0, len=fakeRunningAppsModel.count; i<len; i++) {
51− name: "Phone"
55+ if (appId == fakeRunningAppsModel.get(i).appId) {
52− icon: "phone-app"
56+ return true;
53− exec: "/usr/bin/phone-app"
57+ }
54− stage: ApplicationInfo.MainStage
58+ }
55− desktopFile: "phone.desktop"
59+ return false;
56− imageQml: "import QtQuick 2.0\n" +
60+ }
57− "Rectangle { \n" +
61+
58− " anchors.fill:parent \n" +
62+ function rePopulate() {
59− " color:'darkgreen' \n" +
63+ for (var i=0, len=availableAppsModel.count; i<len; i++) {
60− " Text { anchors.centerIn: parent; text: 'PHONE' } \n" +
64+ fakeRunningAppsModel.append(availableAppsModel.get(i));
65+ }
61− "}"
66+ }
62 }
67 }
63
68
64− ApplicationInfo {
69+ ListModel {
65− id: calendarApp
70+ id: availableAppsModel
71+ ListElement {
72+ name: "Phone"
73+ icon: "phone-app"
74+ exec: "/usr/bin/phone-app"
75+ appId: "phone"
76+ imageQml: "import QtQuick 2.0\n \
77+ Rectangle { \n \
78+ anchors.fill:parent \n \
79+ color:'darkgreen' \n \
80+ Text { anchors.centerIn: parent; text: 'PHONE' } \n \
81+ }"
82+ }
83+
84+ ListElement {
66− name: "Calendar"
85+ name: "Calendar"
67− icon: "calendar-app"
86+ icon: "calendar-app"
68− exec: "/usr/bin/calendar-app"
87+ exec: "/usr/bin/calendar-app"
69− stage: ApplicationInfo.MainStage
70− desktopFile: "calendar.desktop"
88+ appId: "calendar"
71− imageQml: "import QtQuick 2.0\n" +
89+ imageQml: "import QtQuick 2.0\n \
72− "Rectangle { \n" +
90+ Rectangle { \n \
73− " anchors.fill:parent \n" +
91+ anchors.fill:parent \n \
74− " color:'darkblue' \n" +
92+ color:'darkblue' \n \
75− " Text { anchors.centerIn: parent; text: 'CALENDAR'\n" +
93+ Text { anchors.centerIn: parent; text: 'CALENDAR'\n \
76− " color:'white'} \n" +
94+ color:'white'} \n \
77− "}"
95+ }"
96+ }
78 }
97 }
79
98
80 function resetRunningApplications() {
99 function resetRunningApplications() {
81 fakeRunningAppsModel.clear()
100 fakeRunningAppsModel.clear()
82− fakeRunningAppsModel.add(phoneApp)
101+ fakeRunningAppsModel.rePopulate()
83− fakeRunningAppsModel.add(calendarApp)
84 }
102 }
85
103
86 Component.onCompleted: {
104 Component.onCompleted: {
@@ -185,11 +203,11 @@ Item {
185 var calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")
203 var calendarTile = findChild(runningApplicationsGrid, "runningAppTile Calendar")
186 verify(calendarTile != undefined)
204 verify(calendarTile != undefined)
187
205
188− verify(fakeRunningAppsModel.contains(calendarApp))
206+ verify(fakeRunningAppsModel.contains("calendar"))
189
207
190 mouseClick(calendarTile, calendarTile.width/2, calendarTile.height/2)
208 mouseClick(calendarTile, calendarTile.width/2, calendarTile.height/2)
191
209
192− verify(!fakeRunningAppsModel.contains(calendarApp))
210+ verify(!fakeRunningAppsModel.contains("calendar"))
193
211
194 // The tile for the Calendar app should eventually vanish since the
212 // The tile for the Calendar app should eventually vanish since the
195 // application has been terminated
213 // application has been terminated
tests/qmltests/Dash/tst_DashContent.qml+1−1
@@ -106,7 +106,7 @@ Item {
106 contentEndReachedSpy.clear()
106 contentEndReachedSpy.clear()
107 clear_scope_status();
107 clear_scope_status();
108
108
109− // clear, wait for dahs to empty and load scopes.
109+ // clear, wait for dash to empty and load scopes.
110 var dashContentList = findChild(dashContent, "dashContentList");
110 var dashContentList = findChild(dashContent, "dashContentList");
111 verify(dashContentList != undefined)
111 verify(dashContentList != undefined)
112 scopesModel.clear();
112 scopesModel.clear();
tests/qmltests/Dash/tst_GenericScopeView.qml+3
@@ -44,10 +44,13 @@ Item {
44
44
45 function test_isCurrent() {
45 function test_isCurrent() {
46 var pageHeader = findChild(genericScopeView, "pageHeader");
46 var pageHeader = findChild(genericScopeView, "pageHeader");
47+ var previewLoader = findChild(genericScopeView, "previewLoader");
47 genericScopeView.isCurrent = true
48 genericScopeView.isCurrent = true
48 pageHeader.searchQuery = "test"
49 pageHeader.searchQuery = "test"
50+ previewLoader.open = true
49 genericScopeView.isCurrent = false
51 genericScopeView.isCurrent = false
50 tryCompare(pageHeader, "searchQuery", "")
52 tryCompare(pageHeader, "searchQuery", "")
53+ tryCompare(genericScopeView, "previewShown", false);
51 }
54 }
52 }
55 }
53 }
56 }
tests/qmltests/Greeter/tst_Phone.qml+19−1
@@ -18,8 +18,8 @@ import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import ".."
19import ".."
20import "../../../Greeter"
20import "../../../Greeter"
21+import AccountsService 0.1
21import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
22−import LightDM 0.1 as LightDM
23import Unity.Test 0.1 as UT
23import Unity.Test 0.1 as UT
24
24
25Item {
25Item {
@@ -32,6 +32,8 @@ Item {
32 height: parent.height
32 height: parent.height
33 x: 0; y: 0
33 x: 0; y: 0
34
34
35+ defaultBackground: "../../../graphics/phone_background.jpg"
36+
35 property int minX: 0
37 property int minX: 0
36
38
37 onXChanged: {
39 onXChanged: {
@@ -102,5 +104,21 @@ Item {
102 // Wait until we're back to 0
104 // Wait until we're back to 0
103 tryCompareFunction(function() { return greeter.x;}, 0);
105 tryCompareFunction(function() { return greeter.x;}, 0);
104 }
106 }
107+
108+ function test_wallpaper_data() {
109+ return [
110+ {tag: "set", accounts: "../tests/data/unity/backgrounds/blue.png", expected: "blue.png"},
111+ {tag: "unset", accounts: "", expected: "background.jpg"},
112+ {tag: "invalid", accounts: "invalid", expected: "background.jpg"},
113+ ]
114+ }
115+
116+ function test_wallpaper(data) {
117+ var loader = findChild(greeter, "greeterContentLoader")
118+ var wallpaper = findChild(loader.item, "wallpaper")
119+ AccountsService.backgroundFile = data.accounts
120+ tryCompareFunction(function() { return wallpaper.source.toString().indexOf(data.expected) !== -1; }, true)
121+ tryCompare(wallpaper, "status", Image.Ready)
122+ }
105 }
123 }
106}
124}
tests/qmltests/Launcher/tst_Launcher.qml+10−2
@@ -38,7 +38,7 @@ Item {
38 property string lastSelectedApplication
38 property string lastSelectedApplication
39
39
40 onLauncherApplicationSelected: {
40 onLauncherApplicationSelected: {
41− lastSelectedApplication = desktopFile
41+ lastSelectedApplication = appId
42 }
42 }
43
43
44 property int dashItemSelected_count: 0
44 property int dashItemSelected_count: 0
@@ -131,7 +131,7 @@ Item {
131 mouseClick(appIcon, appIcon.width/2, appIcon.height/2)
131 mouseClick(appIcon, appIcon.width/2, appIcon.height/2)
132
132
133 tryCompare(launcher, "lastSelectedApplication",
133 tryCompare(launcher, "lastSelectedApplication",
134− "/usr/share/applications/phone-app.desktop")
134+ "phone-app")
135
135
136 // Tapping on an application icon also dismisses the launcher
136 // Tapping on an application icon also dismisses the launcher
137 revealer.waitUntilLauncherDisappears()
137 revealer.waitUntilLauncherDisappears()
@@ -198,6 +198,14 @@ Item {
198 compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
198 compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
199 }
199 }
200 }
200 }
201+
202+ function test_focusedHighlight() {
203+ var launcherListView = findChild(launcher, "launcherListView");
204+ for (var i = 0; i < launcherListView.count; ++i) {
205+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
206+ compare(findChild(delegate, "focusedHighlight").visible, LauncherModel.get(i).focused)
207+ }
208+ }
201 }
209 }
202
210
203 UT.UnityTestCase {
211 UT.UnityTestCase {
tests/qmltests/Panel/tst_IndicatorItem.qml+2−2
@@ -53,9 +53,9 @@ Rectangle {
53 }
53 }
54
54
55 function test_empty() {
55 function test_empty() {
56− compare(indicatorItem.visible, false, "IndicatorItem should not be visible.");
56+ compare(indicatorItem.indicatorVisible, false, "IndicatorItem should not be visible.");
57 indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
57 indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
58− tryCompare(indicatorItem, "visible", true);
58+ tryCompare(indicatorItem, "indicatorVisible", true);
59 }
59 }
60 }
60 }
61}
61}
tests/qmltests/Panel/tst_Indicators.qml+30−11
@@ -100,26 +100,29 @@ Item {
100 {
100 {
101 // tests changing the lateral position of the revealer activates the correct indicator items.
101 // tests changing the lateral position of the revealer activates the correct indicator items.
102
102
103− var indicator_row = findChild(indicators, "indicatorRow")
103+ var indicatorRow = findChild(indicators, "indicatorRow")
104− var row_repeater = findChild(indicators, "rowRepeater")
104+ var rowRepeater = findChild(indicators, "rowRepeater")
105
105
106− for (var i = 0; i < row_repeater.count; i++) {
106+ for (var i = 0; i < rowRepeater.count; i++) {
107− var indicator_item = row_repeater.itemAt(i);
107+ var indicatorItem = rowRepeater.itemAt(i);
108
108
109+ if (!indicatorItem.visible)
110+ continue;
111+
109− var indicator_position = indicators.mapFromItem(indicator_item,
112+ var indicatorPosition = indicators.mapFromItem(indicatorItem,
110− indicator_item.width/2, indicator_item.height/2)
113+ indicatorItem.width/2, indicatorItem.height/2)
111
114
112 touchFlick(indicators,
115 touchFlick(indicators,
113− indicator_position.x, indicator_position.y,
116+ indicatorPosition.x, indicatorPosition.y,
114− indicator_position.x, indicators.openedHeight * 0.4,
117+ indicatorPosition.x, indicators.openedHeight * 0.4,
115 true /* beginTouch */, false /* endTouch */)
118 true /* beginTouch */, false /* endTouch */)
116
119
117− compare(indicator_row.currentItem, indicator_item,
120+ compare(indicatorRow.currentItem, indicatorItem,
118 "Incorrect item activated at position " + i);
121 "Incorrect item activated at position " + i);
119
122
120 touchFlick(indicators,
123 touchFlick(indicators,
121− indicator_position.x, indicators.openedHeight * 0.4,
124+ indicatorPosition.x, indicators.openedHeight * 0.4,
122− indicator_position.x, indicator_position.y,
125+ indicatorPosition.x, indicatorPosition.y,
123 false /* beginTouch */, true /* endTouch */)
126 false /* beginTouch */, true /* endTouch */)
124
127
125 // wait until fully closed
128 // wait until fully closed
@@ -158,5 +161,21 @@ Item {
158 compare(indicators.fullyOpened, true);
161 compare(indicators.fullyOpened, true);
159 }
162 }
160
163
164+ function test_row_visible_menuContent_visible_data() { return [
165+ {tag: "visible", index: 0, name: "indicator-fake1", visible: true },
166+ {tag: "invisible", index: 1, name: "indicator-fake2", visible: false }]
167+ }
168+
169+ function test_row_visible_menuContent_visible(data) {
170+ var indicatorTabs = findChild(indicators, "tabs");
171+ var rowRepeater = findChild(indicators, "rowRepeater");
172+
173+ var indicatorItem = rowRepeater.itemAt(data.index);
174+ tryCompareFunction(function() { return indicatorItem.width > 0}, true);
175+ tryCompare(indicatorItem, "visible", data.visible);
176+
177+ var indicatorTab = findChild(indicatorTabs, data.name)
178+ tryCompareFunction(function() { return indicatorTab !== undefined }, data.visible);
179+ }
161 }
180 }
162}
181}
tests/qmltests/Panel/tst_MenuContent.qml+9−2
@@ -48,8 +48,15 @@ Item {
48 id: menuContent
48 id: menuContent
49 indicatorsModel: indicatorsModel
49 indicatorsModel: indicatorsModel
50 contentReleaseInterval: 50
50 contentReleaseInterval: 50
51−
52 height: parent.height - 50
51 height: parent.height - 50
52+
53+ visibleIndicators: {
54+ 'indicator-fake1': true,
55+ 'indicator-fake2': true,
56+ 'indicator-fake3': true,
57+ 'indicator-fake4': true,
58+ 'indicator-fake5': true
59+ }
53 }
60 }
54
61
55 Rectangle {
62 Rectangle {
@@ -134,7 +141,7 @@ Item {
134 var menuIndex = i%menuCount;
141 var menuIndex = i%menuCount;
135
142
136 activate_content(menuIndex);
143 activate_content(menuIndex);
137− testTabObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
144+ testTabObjectName = indicatorsModel.get(menuIndex).identifier;
138 compare(tabs.selectedTabIndex, menuIndex, "Current tab index does not match selected tab index");
145 compare(tabs.selectedTabIndex, menuIndex, "Current tab index does not match selected tab index");
139 tryCompareFunction(selected_tab_equals_test_tab, true);
146 tryCompareFunction(selected_tab_equals_test_tab, true);
140 }
147 }
tests/qmltests/Panel/tst_Panel.qml+3
@@ -159,6 +159,9 @@ Item {
159 var indicatorItem = get_indicator_item(i);
159 var indicatorItem = get_indicator_item(i);
160 verify(indicatorItem != undefined);
160 verify(indicatorItem != undefined);
161
161
162+ if (!indicatorItem.visible)
163+ continue;
164+
162 var indicatorItemCoord = get_indicator_item_position(i);
165 var indicatorItemCoord = get_indicator_item_position(i);
163
166
164 touchPress(panel,
167 touchPress(panel,
tests/qmltests/tst_Shell.qml+33−9
@@ -20,7 +20,7 @@
20import QtQuick 2.0
20import QtQuick 2.0
21import QtTest 1.0
21import QtTest 1.0
22import GSettings 1.0
22import GSettings 1.0
23−import Ubuntu.Application 0.1
23+import Unity.Application 0.1
24import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
25import Powerd 0.1
25import Powerd 0.1
26
26
@@ -66,16 +66,16 @@ Item {
66 }
66 }
67
67
68 // kill all (fake) running apps
68 // kill all (fake) running apps
69− killApps(ApplicationManager.mainStageApplications);
69+ killApps(ApplicationManager);
70− killApps(ApplicationManager.sideStageApplications);
71
70
72 var dashHome = findChild(shell, "DashHome");
71 var dashHome = findChild(shell, "DashHome");
73 swipeUntilScopeViewIsReached(dashHome);
72 swipeUntilScopeViewIsReached(dashHome);
74 }
73 }
75
74
76 function killApps(apps) {
75 function killApps(apps) {
76+ if (!apps) return;
77 while (apps.count > 0) {
77 while (apps.count > 0) {
78− ApplicationManager.stopProcess(apps.get(0));
78+ ApplicationManager.stopApplication(apps.get(0).appId);
79 }
79 }
80 }
80 }
81
81
@@ -114,6 +114,26 @@ Item {
114 checkRightEdgeDragWithNoRunningApps();
114 checkRightEdgeDragWithNoRunningApps();
115 }
115 }
116
116
117+ function test_leftEdgeDrag_data() {
118+ return [
119+ {tag: "without launcher", revealLauncher: false},
120+ {tag: "with launcher", revealLauncher: true},
121+ ];
122+ }
123+
124+ function test_leftEdgeDrag(data) {
125+ dragLauncherIntoView();
126+ tapOnAppIconInLauncher();
127+ waitUntilApplicationWindowIsFullyVisible();
128+
129+ if (data.revealLauncher) {
130+ dragLauncherIntoView();
131+ }
132+
133+ swipeFromLeftEdge();
134+ waitUntilApplicationWindowIsFullyHidden();
135+ }
136+
117 function test_suspend() {
137 function test_suspend() {
118 var greeter = findChild(shell, "greeter");
138 var greeter = findChild(shell, "greeter");
119
139
@@ -122,8 +142,8 @@ Item {
122 tapOnAppIconInLauncher();
142 tapOnAppIconInLauncher();
123 waitUntilApplicationWindowIsFullyVisible();
143 waitUntilApplicationWindowIsFullyVisible();
124
144
125− var mainApp = ApplicationManager.mainStageFocusedApplication;
145+ var mainApp = ApplicationManager.focusedApplicationId;
126− tryCompareFunction(function() { return mainApp != null; }, true);
146+ tryCompareFunction(function() { return mainApp != ""; }, true);
127
147
128 // Try to suspend while proximity is engaged...
148 // Try to suspend while proximity is engaged...
129 Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
149 Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
@@ -132,11 +152,11 @@ Item {
132 // Now really suspend
152 // Now really suspend
133 Powerd.displayPowerStateChange(Powerd.Off, 0);
153 Powerd.displayPowerStateChange(Powerd.Off, 0);
134 tryCompare(greeter, "showProgress", 1);
154 tryCompare(greeter, "showProgress", 1);
135− tryCompare(ApplicationManager, "mainStageFocusedApplication", null);
155+ tryCompare(ApplicationManager, "focusedApplicationId", "");
136
156
137 // And wake up
157 // And wake up
138 Powerd.displayPowerStateChange(Powerd.On, 0);
158 Powerd.displayPowerStateChange(Powerd.On, 0);
139− tryCompare(ApplicationManager, "mainStageFocusedApplication", mainApp);
159+ tryCompare(ApplicationManager, "focusedApplicationId", mainApp);
140 tryCompare(greeter, "showProgress", 1);
160 tryCompare(greeter, "showProgress", 1);
141 }
161 }
142
162
@@ -223,7 +243,11 @@ Item {
223 function dragLauncherIntoView() {
243 function dragLauncherIntoView() {
224 var launcherPanel = findChild(shell, "launcherPanel");
244 var launcherPanel = findChild(shell, "launcherPanel");
225 verify(launcherPanel.x = - launcherPanel.width);
245 verify(launcherPanel.x = - launcherPanel.width);
226− swipeFromLeftEdge();
246+
247+ var touchStartX = 2;
248+ var touchStartY = shell.height / 2;
249+ touchFlick(shell, touchStartX, touchStartY, launcherPanel.width + units.gu(1), touchStartY);
250+
227 tryCompare(launcherPanel, "x", 0);
251 tryCompare(launcherPanel, "x", 0);
228 }
252 }
229
253
tests/uqmlscene/main.cpp−2
@@ -497,8 +497,6 @@ int main(int argc, char ** argv)
497 }
497 }
498
498
499 if (window) {
499 if (window) {
500− mouseTouchAdaptor.setTargetWindow(window);
501−
502 QSurfaceFormat surfaceFormat = window->requestedFormat();
500 QSurfaceFormat surfaceFormat = window->requestedFormat();
503 if (options.multisample)
501 if (options.multisample)
504 surfaceFormat.setSamples(16);
502 surfaceFormat.setSamples(16);