on xenial help sign in
Merged with trunk, fixed text-field width of extended snap-decisions, removed unused code from snap-decisions.
cc25fa3Mirco Müller
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+2−2
@@ -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 {
@@ -144,7 +144,7 @@ Item {
144
144
145 InputFilterArea {
145 InputFilterArea {
146 anchors.fill: parent
146 anchors.fill: parent
147− blockInput: hudButton.visible
147+ blockInput: (hudButton.opacity == 1)
148 }
148 }
149
149
150 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/EdgeDemoOverlay.qml+1−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 {
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/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/GenericScopeView.qml+4−1
@@ -21,10 +21,11 @@ import "../Components/ListItems" as ListItems
21
21
22ScopeView {
22ScopeView {
23 id: scopeView
23 id: scopeView
24− property alias previewShown: previewLoader.onScreen
24+ readonly property alias previewShown: previewLoader.onScreen
25
25
26 onIsCurrentChanged: {
26 onIsCurrentChanged: {
27 pageHeader.resetSearch();
27 pageHeader.resetSearch();
28+ previewLoader.open = false;
28 }
29 }
29
30
30 onMovementStarted: categoryView.showHeader()
31 onMovementStarted: categoryView.showHeader()
@@ -179,6 +180,7 @@ ScopeView {
179 case "grid": {
180 case "grid": {
180 switch (contentType) {
181 switch (contentType) {
181 case "video": return "Generic/GenericFilterGridPotrait.qml";
182 case "video": return "Generic/GenericFilterGridPotrait.qml";
183+ case "music": return "Music/MusicFilterGrid.qml";
182 default: return "Generic/GenericFilterGrid.qml";
184 default: return "Generic/GenericFilterGrid.qml";
183 }
185 }
184 }
186 }
@@ -246,6 +248,7 @@ ScopeView {
246 }
248 }
247
249
248 Loader {
250 Loader {
251+ objectName: "previewLoader"
249 id: previewLoader
252 id: previewLoader
250 property var previewData
253 property var previewData
251 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+4−3
@@ -40,11 +40,12 @@ MouseArea {
40 color: "black"
40 color: "black"
41 }
41 }
42
42
43− property url backgroundValue: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : shell.defaultBackground
43+ property url backgroundValue: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : greeter.defaultBackground
44 onBackgroundValueChanged: wallpaper.source = backgroundValue
44 onBackgroundValueChanged: wallpaper.source = backgroundValue
45
45
46 CrossFadeImage {
46 CrossFadeImage {
47 id: wallpaper
47 id: wallpaper
48+ objectName: "wallpaper"
48 anchors.fill: parent
49 anchors.fill: parent
49 fadeInFirst: false
50 fadeInFirst: false
50 }
51 }
@@ -58,8 +59,8 @@ MouseArea {
58 sourceSize.height: 0
59 sourceSize.height: 0
59 sourceSize.width: 0
60 sourceSize.width: 0
60 onStatusChanged: {
61 onStatusChanged: {
61− if (status == Image.Error && source != shell.defaultBackground) {
62+ if (status == Image.Error && source != greeter.defaultBackground) {
62− wallpaper.source = shell.defaultBackground
63+ wallpaper.source = greeter.defaultBackground
63 }
64 }
64 }
65 }
65 }
66 }
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/LauncherPanel.qml+3−3
@@ -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 {
@@ -300,7 +300,7 @@ Item {
300 } else if (clickedItem.angle < -12) {
300 } else if (clickedItem.angle < -12) {
301 launcherListView.flick(0, launcherListView.clickFlickSpeed);
301 launcherListView.flick(0, launcherListView.clickFlickSpeed);
302 } else {
302 } else {
303− root.applicationSelected(LauncherModel.get(index).desktopFile);
303+ root.applicationSelected(LauncherModel.get(index).appId);
304 }
304 }
305 return;
305 return;
306 }
306 }
@@ -311,7 +311,7 @@ Item {
311 } else if (clickedItem.angle < -30) {
311 } else if (clickedItem.angle < -30) {
312 launcherListView.flick(0, launcherListView.clickFlickSpeed);
312 launcherListView.flick(0, launcherListView.clickFlickSpeed);
313 } else {
313 } else {
314− root.applicationSelected(LauncherModel.get(index).desktopFile);
314+ root.applicationSelected(LauncherModel.get(index).appId);
315 }
315 }
316 }
316 }
317
317
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};
Notifications/NotificationMenuItemFactory.qml+11−21
@@ -31,7 +31,6 @@ Loader {
31
31
32 property var _map: {
32 property var _map: {
33 "com.canonical.snapdecision.textfield": textfield,
33 "com.canonical.snapdecision.textfield": textfield,
34− "com.canonical.snapdecision.selectionlist": selectionlist,
35 "com.canonical.snapdecision.pinlock" : pinLock,
34 "com.canonical.snapdecision.pinlock" : pinLock,
36 }
35 }
37
36
@@ -55,13 +54,17 @@ Loader {
55 Column {
54 Column {
56 spacing: units.gu(.5)
55 spacing: units.gu(.5)
57
56
57+ anchors {
58+ left: parent.left
59+ right: parent.right
60+ }
61+
58 Component.onCompleted: {
62 Component.onCompleted: {
59 menuModel.loadExtendedAttributes(menuIndex, {"x-echo-mode-password": "bool"});
63 menuModel.loadExtendedAttributes(menuIndex, {"x-echo-mode-password": "bool"});
60 textfield.echoMode = menuData.ext.xEchoModePassword ? TextInput.Password : TextInput.Normal
64 textfield.echoMode = menuData.ext.xEchoModePassword ? TextInput.Password : TextInput.Normal
61 }
65 }
62
66
63 Label {
67 Label {
64− anchors.left: parent.left
65 fontSize: "medium"
68 fontSize: "medium"
66 color: "white"
69 color: "white"
67 text: menuData.label
70 text: menuData.label
@@ -69,6 +72,12 @@ Loader {
69
72
70 TextField {
73 TextField {
71 id: textfield
74 id: textfield
75+
76+ anchors {
77+ left: parent.left
78+ right: parent.right
79+ }
80+
72 onTextChanged: {
81 onTextChanged: {
73 menuModel.changeState(menuIndex, text);
82 menuModel.changeState(menuIndex, text);
74 }
83 }
@@ -76,25 +85,6 @@ Loader {
76 }
85 }
77 }
86 }
78
87
79− Component {
80− id: selectionlist
81−
82− Column {
83− spacing: units.gu(.5)
84− anchors {
85− left: parent.left
86− right: parent.right
87− }
88−
89− Label {
90− anchors.horizontalCenter: parent.horizontalCenter
91− fontSize: "large"
92− color: "white"
93− text: "Selection List"
94− }
95− }
96− }
97−
98 Component {
88 Component {
99 id: pinLock
89 id: pinLock
100 PinLockscreen {
90 PinLockscreen {
Panel/Indicators/DefaultIndicatorWidget.qml+2−6
@@ -24,6 +24,7 @@ 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(0.5)
28 width: itemRow.width + units.gu(0.5)
28
29
29 property alias label: itemLabel.text
30 property alias label: itemLabel.text
@@ -63,19 +64,14 @@ Indicators.IndicatorWidget {
63
64
64 onActionStateChanged: {
65 onActionStateChanged: {
65 if (action == undefined || !action.valid) {
66 if (action == undefined || !action.valid) {
66− enabled = false;
67 return;
67 return;
68 }
68 }
69
69
70 if (action.state == undefined) {
70 if (action.state == undefined) {
71− label = "";
72− iconSource = "";
73− enabled = false;
74 return;
71 return;
75 }
72 }
76
73
77 label = action.state[Indicators.ActionState.Label];
74 label = action.state[Indicators.ActionState.Label];
78− iconSource = "image://gicon/" + action.state[Indicators.ActionState.IconSource];
75+ iconSource = "image://theme/" + action.state[Indicators.ActionState.IconSource];
79− enabled = action.state[Indicators.ActionState.Visible];
80 }
76 }
81}
77}
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+6−6
@@ -97,16 +97,16 @@ Indicators.IndicatorWidget {
97 function get_icon_for_signal(con_state, value) {
97 function get_icon_for_signal(con_state, value) {
98 if (con_state >= Indicators.NetworkConnection.Activating && con_state <= Indicators.NetworkConnection.Deactivating) {
98 if (con_state >= Indicators.NetworkConnection.Activating && con_state <= Indicators.NetworkConnection.Deactivating) {
99 if (value == 0) {
99 if (value == 0) {
100− return "image://gicon/nm-signal-00";
100+ return "image://theme/nm-signal-00";
101 } else if (value <= 25) {
101 } else if (value <= 25) {
102− return "image://gicon/nm-signal-25";
102+ return "image://theme/nm-signal-25";
103 } else if (value <= 50) {
103 } else if (value <= 50) {
104− return "image://gicon/nm-signal-50";
104+ return "image://theme/nm-signal-50";
105 } else if (value <= 75) {
105 } else if (value <= 75) {
106− return "image://gicon/nm-signal-75";
106+ return "image://theme/nm-signal-75";
107 }
107 }
108− return "image://gicon/nm-signal-100";
108+ return "image://theme/nm-signal-100";
109 }
109 }
110− return "image://gicon/wifi-none";
110+ return "image://theme/wifi-none";
111 }
111 }
112}
112}
Panel/Indicators/SoundIndicatorWidget.qml+1−1
@@ -21,6 +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 enabled: true
26}
26}
Shell.qml+50−36
@@ -17,12 +17,13 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import AccountsService 0.1
18import AccountsService 0.1
19import GSettings 1.0
19import GSettings 1.0
20−import Ubuntu.Application 0.1
20+import Unity.Application 0.1
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22import Ubuntu.Gestures 0.1
22import Ubuntu.Gestures 0.1
23import Unity.Launcher 0.1
23import Unity.Launcher 0.1
24import LightDM 0.1 as LightDM
24import LightDM 0.1 as LightDM
25import Powerd 0.1
25import Powerd 0.1
26+import SessionBroadcast 0.1
26import "Dash"
27import "Dash"
27import "Greeter"
28import "Greeter"
28import "Launcher"
29import "Launcher"
@@ -65,6 +66,12 @@ FocusScope {
65
66
66 property var applicationManager: ApplicationManagerWrapper {}
67 property var applicationManager: ApplicationManagerWrapper {}
67
68
69+ Binding {
70+ target: LauncherModel
71+ property: "applicationManager"
72+ value: ApplicationManager
73+ }
74+
68 Component.onCompleted: {
75 Component.onCompleted: {
69 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
76 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
70
77
@@ -75,6 +82,11 @@ FocusScope {
75 applicationManager.unfocusCurrentApplication();
82 applicationManager.unfocusCurrentApplication();
76 }
83 }
77
84
85+ readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
86+ || !!applicationManager.sideStageFocusedApplication
87+ // Used for autopilot testing.
88+ readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId
89+
78 readonly property bool fullscreenMode: {
90 readonly property bool fullscreenMode: {
79 if (greeter.shown || lockscreen.shown) {
91 if (greeter.shown || lockscreen.shown) {
80 return false;
92 return false;
@@ -96,15 +108,6 @@ FocusScope {
96 greeter.hide();
108 greeter.hide();
97 shell.activateApplication(desktopFile);
109 shell.activateApplication(desktopFile);
98 }
110 }
99−
100− onMainStageFocusedApplicationChanged: {
101− var app = applicationManager.mainStageFocusedApplication
102− if (app != null) {
103− LauncherModel.applicationFocused(app.desktopFile);
104− } else {
105− LauncherModel.applicationFocused("");
106− }
107− }
108 }
111 }
109
112
110 function activateApplication(desktopFile, argument) {
113 function activateApplication(desktopFile, argument) {
@@ -157,6 +160,10 @@ FocusScope {
157 }
160 }
158 }
161 }
159
162
163+ OSKController {
164+ anchors.fill: parent // as needs to know the geometry of the shell
165+ }
166+
160 VolumeControl {
167 VolumeControl {
161 id: volumeControl
168 id: volumeControl
162 }
169 }
@@ -255,7 +262,7 @@ FocusScope {
255 property bool fullyShown: shown && x == 0 && parent.x == 0
262 property bool fullyShown: shown && x == 0 && parent.x == 0
256 property bool fullyHidden: !shown && x == width
263 property bool fullyHidden: !shown && x == width
257 available: !greeter.shown
264 available: !greeter.shown
258− hides: [launcher, panel.indicators]
265+ hides: [panel.indicators]
259 shown: false
266 shown: false
260 opacity: 1.0
267 opacity: 1.0
261 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 }
@@ -456,6 +463,8 @@ FocusScope {
456 hides: [launcher, panel.indicators, hud]
463 hides: [launcher, panel.indicators, hud]
457 shown: true
464 shown: true
458
465
466+ defaultBackground: shell.background
467+
459 y: panel.panelHeight
468 y: panel.panelHeight
460 width: parent.width
469 width: parent.width
461 height: parent.height - panel.panelHeight
470 height: parent.height - panel.panelHeight
@@ -492,7 +501,8 @@ FocusScope {
492
501
493 InputFilterArea {
502 InputFilterArea {
494 anchors.fill: parent
503 anchors.fill: parent
495− blockInput: greeter.shown || lockscreen.shown
504+ blockInput: !applicationFocused || greeter.shown || lockscreen.shown || launcher.shown
505+ || panel.indicators.shown || hud.shown
496 }
506 }
497
507
498 Connections {
508 Connections {
@@ -504,10 +514,8 @@ FocusScope {
504
514
505 function setFocused(focused) {
515 function setFocused(focused) {
506 if (!focused) {
516 if (!focused) {
507− // FIXME: *FocusedApplication are not updated when unfocused, hence the need to check whether
508− // the stage was actually shown
509− if (mainStage.fullyShown) powerConnection.previousMainApp = applicationManager.mainStageFocusedApplication;
517+ powerConnection.previousMainApp = applicationManager.mainStageFocusedApplication;
510− if (sideStage.fullyShown) powerConnection.previousSideApp = applicationManager.sideStageFocusedApplication;
518+ powerConnection.previousSideApp = applicationManager.sideStageFocusedApplication;
511 applicationManager.unfocusCurrentApplication();
519 applicationManager.unfocusCurrentApplication();
512 } else {
520 } else {
513 if (powerConnection.previousMainApp) {
521 if (powerConnection.previousMainApp) {
@@ -540,6 +548,17 @@ FocusScope {
540 }
548 }
541 }
549 }
542
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+
543 Item {
562 Item {
544 id: overlay
563 id: overlay
545
564
@@ -558,8 +577,13 @@ FocusScope {
558 searchVisible: !greeter.shown && !lockscreen.shown
577 searchVisible: !greeter.shown && !lockscreen.shown
559
578
560 InputFilterArea {
579 InputFilterArea {
561− 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
562− blockInput: panel.indicators.shown
586+ blockInput: true
563 }
587 }
564 }
588 }
565
589
@@ -579,11 +603,6 @@ FocusScope {
579 onMainStageFocusedApplicationChanged: hud.hide()
603 onMainStageFocusedApplicationChanged: hud.hide()
580 onSideStageFocusedApplicationChanged: hud.hide()
604 onSideStageFocusedApplicationChanged: hud.hide()
581 }
605 }
582−
583− InputFilterArea {
584− anchors.fill: parent
585− blockInput: hud.shown
586− }
587 }
606 }
588
607
589 Revealer {
608 Revealer {
@@ -606,8 +625,7 @@ FocusScope {
606 theHud: hud
625 theHud: hud
607 anchors.fill: parent
626 anchors.fill: parent
608 enabled: !panel.indicators.shown
627 enabled: !panel.indicators.shown
609− applicationIsOnForeground: applicationManager.mainStageFocusedApplication
628+ applicationIsOnForeground: applicationFocused
610− || applicationManager.sideStageFocusedApplication
611 }
629 }
612
630
613 InputFilterArea {
631 InputFilterArea {
@@ -628,16 +646,7 @@ FocusScope {
628 width: parent.width
646 width: parent.width
629 dragAreaWidth: shell.edgeSize
647 dragAreaWidth: shell.edgeSize
630 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
648 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
631− onDashItemSelected: {
649+ onDashItemSelected: showHome()
632− greeter.hide()
633− // Animate if moving between application and dash
634− if (!stages.shown) {
635− dash.setCurrentScope("home.scope", true, false)
636− } else {
637− dash.setCurrentScope("home.scope", false, false)
638− }
639− stages.hide();
640− }
641 onDash: {
650 onDash: {
642 if (stages.shown) {
651 if (stages.shown) {
643 dash.setCurrentScope("applications.scope", true, false)
652 dash.setCurrentScope("applications.scope", true, false)
@@ -647,7 +656,7 @@ FocusScope {
647 }
656 }
648 onLauncherApplicationSelected:{
657 onLauncherApplicationSelected:{
649 greeter.hide()
658 greeter.hide()
650− shell.activateApplication(desktopFile)
659+ shell.activateApplication(appId)
651 }
660 }
652 onShownChanged: {
661 onShownChanged: {
653 if (shown) {
662 if (shown) {
@@ -745,4 +754,9 @@ FocusScope {
745 indicators: panel.indicators
754 indicators: panel.indicators
746 underlay: underlay
755 underlay: underlay
747 }
756 }
757+
758+ Connections {
759+ target: SessionBroadcast
760+ onShowHome: showHome()
761+ }
748}
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
debian/changelog+146
@@ -1,3 +1,149 @@
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+
1unity8 (7.81.3+13.10.20130905.2-0ubuntu1) saucy; urgency=low
147unity8 (7.81.3+13.10.20130905.2-0ubuntu1) saucy; urgency=low
2
148
3 [ Michael Zanetti ]
149 [ Michael Zanetti ]
debian/control+9−7
@@ -23,6 +23,7 @@ Build-Depends: cmake,
23 libunity-api-dev (>= 7.80.3),
23 libunity-api-dev (>= 7.80.3),
24 libunity-core-6.0-dev (<< 7.80),
24 libunity-core-6.0-dev (<< 7.80),
25 libunity-dev (>= 7.0.12),
25 libunity-dev (>= 7.0.12),
26+ libunity-mir-dev,
26 libunity-scopes-json-def-phone,
27 libunity-scopes-json-def-phone,
27 libupstart-dev,
28 libupstart-dev,
28 libusermetricsoutput1-dev,
29 libusermetricsoutput1-dev,
@@ -54,7 +55,7 @@ Vcs-Browser: https://bazaar.launchpad.net/~unity-team/unity8/trunk
54Package: indicators-client
55Package: indicators-client
55Architecture: amd64 armhf i386
56Architecture: amd64 armhf i386
56Depends: qmenumodel-qml (>= 0.2.7),
57Depends: qmenumodel-qml (>= 0.2.7),
57− qtdeclarative5-ubuntu-ui-toolkit-plugin | qt-components-ubuntu,
58+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.46+13.10.20130829.1-0ubuntu1),
58 unity8 (= ${binary:Version}),
59 unity8 (= ${binary:Version}),
59 ${misc:Depends},
60 ${misc:Depends},
60 ${shlibs:Depends},
61 ${shlibs:Depends},
@@ -65,13 +66,14 @@ Package: unity8
65Architecture: any
66Architecture: any
66Provides: indicator-renderer,
67Provides: indicator-renderer,
67Depends: gsettings-desktop-schemas,
68Depends: gsettings-desktop-schemas,
69+ libunity-mir1,
68 qmenumodel-qml (>= 0.2.7),
70 qmenumodel-qml (>= 0.2.7),
69 qtdeclarative5-dee-plugin,
71 qtdeclarative5-dee-plugin,
70 qtdeclarative5-gsettings1.0,
72 qtdeclarative5-gsettings1.0,
71− qtdeclarative5-ubuntu-ui-toolkit-plugin,
73+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.46+13.10.20130829.1-0ubuntu1),
72 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
74 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
73 qtdeclarative5-xmllistmodel-plugin,
75 qtdeclarative5-xmllistmodel-plugin,
74− unity-launcher-impl-2,
76+ unity-launcher-impl-3,
75 unity-notifications-impl-1,
77 unity-notifications-impl-1,
76 unity8-fake-env | qtubuntu-shell,
78 unity8-fake-env | qtubuntu-shell,
77 unity8-private (= ${binary:Version}),
79 unity8-private (= ${binary:Version}),
@@ -87,18 +89,18 @@ Description: Unity 8 shell
87
89
88Package: unity8-autopilot
90Package: unity8-autopilot
89Architecture: all
91Architecture: all
92+Depends: gir1.2-glib-2.0,
90−Depends: indicators-client (>= ${source:Version}),
93+ indicators-client (>= ${source:Version}),
91 libautopilot-qt,
94 libautopilot-qt,
92 libqt5test5,
95 libqt5test5,
93 libqt5widgets5,
96 libqt5widgets5,
94 python-evdev,
97 python-evdev,
98+ python-gi,
95 unity8 (>= ${source:Version}),
99 unity8 (>= ${source:Version}),
96 unity8-fake-env (>= ${source:Version}),
100 unity8-fake-env (>= ${source:Version}),
97 ${misc:Depends},
101 ${misc:Depends},
98 ${python:Depends},
102 ${python:Depends},
99 ${shlibs:Depends},
103 ${shlibs:Depends},
100−Recommends: gir1.2-glib-2.0,
101− python-gi,
102Description: Test package for Unity 8 shell
104Description: Test package for Unity 8 shell
103 Autopilot tests for the unity8 package
105 Autopilot tests for the unity8 package
104
106
@@ -120,7 +122,7 @@ Pre-Depends: ${misc:Pre-Depends},
120Depends: ${misc:Depends},
122Depends: ${misc:Depends},
121 ${shlibs:Depends},
123 ${shlibs:Depends},
122Provides: unity-launcher-impl,
124Provides: unity-launcher-impl,
123− unity-launcher-impl-2,
125+ unity-launcher-impl-3,
124Description: Unity 8 private libs
126Description: Unity 8 private libs
125 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.
126 .
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+73−7
@@ -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)
@@ -72,7 +76,7 @@ void resolveIconTheme() {
72}
76}
73} // namespace {
77} // namespace {
74
78
75−int main(int argc, char** argv)
79+int startShell(int argc, const char** argv, void* server)
76{
80{
77 /* Workaround Qt platform integration plugin not advertising itself
81 /* Workaround Qt platform integration plugin not advertising itself
78 as having the following capabilities:
82 as having the following capabilities:
@@ -82,12 +86,29 @@ int main(int argc, char** argv)
82 setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
86 setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
83 setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
87 setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
84
88
89+ const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
90+
85 QGuiApplication::setApplicationName("Unity 8");
91 QGuiApplication::setApplicationName("Unity 8");
86− 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+ }
87
108
88 resolveIconTheme();
109 resolveIconTheme();
89
110
90− QStringList args = application.arguments();
111+ QStringList args = application->arguments();
91 ApplicationArguments qmlArgs(args);
112 ApplicationArguments qmlArgs(args);
92
113
93 // 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.
@@ -123,8 +144,7 @@ int main(int argc, char** argv)
123 MouseTouchAdaptor *mouseTouchAdaptor = 0;
144 MouseTouchAdaptor *mouseTouchAdaptor = 0;
124 if (args.contains(QLatin1String("-mousetouch"))) {
145 if (args.contains(QLatin1String("-mousetouch"))) {
125 mouseTouchAdaptor = new MouseTouchAdaptor;
146 mouseTouchAdaptor = new MouseTouchAdaptor;
126− mouseTouchAdaptor->setTargetWindow(view);
127− application.installNativeEventFilter(mouseTouchAdaptor);
147+ application->installNativeEventFilter(mouseTouchAdaptor);
128 }
148 }
129
149
130 QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface();
150 QPlatformNativeInterface* nativeInterface = QGuiApplication::platformNativeInterface();
@@ -139,18 +159,64 @@ int main(int argc, char** argv)
139 QUrl source("Shell.qml");
159 QUrl source("Shell.qml");
140 prependImportPaths(view->engine(), ::overrideImportPaths());
160 prependImportPaths(view->engine(), ::overrideImportPaths());
141 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+
142 view->setSource(source);
169 view->setSource(source);
143 view->setColor("transparent");
170 view->setColor("transparent");
144
171
145− if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || args.contains(QLatin1String("-fullscreen"))) {
172+ if (qgetenv("QT_QPA_PLATFORM") == "ubuntu" || isUbuntuMirServer || args.contains(QLatin1String("-fullscreen"))) {
146 view->showFullScreen();
173 view->showFullScreen();
147 } else {
174 } else {
148 view->show();
175 view->show();
149 }
176 }
150
177
151− int result = application.exec();
178+ int result = application->exec();
152
179
153 delete mouseTouchAdaptor;
180 delete mouseTouchAdaptor;
181+ delete application;
154
182
155 return result;
183 return result;
156}
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/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/Indicators/rootactionstate.cpp+8−8
@@ -148,15 +148,15 @@ static QString iconUri(GIcon *icon)
148 QString uri;
148 QString uri;
149
149
150 if (G_IS_THEMED_ICON (icon)) {
150 if (G_IS_THEMED_ICON (icon)) {
151−
152 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));
153− guint index = 0;
152+
153+ QStringList iconNameList;
154− while(iconNames[index] != NULL) {
154+ for (uint index = 0; iconNames[index] != NULL; index++) {
155− if (QIcon::hasThemeIcon(iconNames[index])) {
155+ iconNameList << iconNames[index];
156+ }
157+
158+ if (!iconNameList.empty()) {
156− uri = QString("image://theme/%1").arg(iconNames[index]);
159+ uri = QString("image://theme/%1").arg(iconNameList.join(","));
157− break;
158− }
159− index++;
160 }
160 }
161 }
161 }
162 else if (G_IS_FILE_ICON (icon)) {
162 else if (G_IS_FILE_ICON (icon)) {
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/Launcher/CMakeLists.txt+2−1
@@ -1,5 +1,6 @@
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)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4
5
5add_definitions(-DSM_BUSNAME=systemBus)
6add_definitions(-DSM_BUSNAME=systemBus)
plugins/Unity/Launcher/backend/launcherbackend.cpp+11−7
@@ -132,11 +132,7 @@ QString LauncherBackend::icon(const QString &appId) const
132 }
132 }
133 }
133 }
134
134
135− QFileInfo info(iconName);
136− if (info.exists()) {
137− return info.absoluteFilePath();
138− }
139− return "image://theme/" + iconName;
135+ return iconName;
140}
136}
141
137
142bool LauncherBackend::isPinned(const QString &appId) const
138bool LauncherBackend::isPinned(const QString &appId) const
@@ -296,11 +292,19 @@ LauncherBackendItem* LauncherBackend::parseDesktopFile(const QString &desktopFil
296{
292{
297 QSettings settings(desktopFile, QSettings::IniFormat);
293 QSettings settings(desktopFile, QSettings::IniFormat);
298
294
299−
300 LauncherBackendItem* item = new LauncherBackendItem();
295 LauncherBackendItem* item = new LauncherBackendItem();
301 item->desktopFile = desktopFile;
296 item->desktopFile = desktopFile;
302 item->displayName = settings.value("Desktop Entry/Name").toString();
297 item->displayName = settings.value("Desktop Entry/Name").toString();
298+
303− item->icon = settings.value("Desktop Entry/Icon").toString();
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+ }
304 item->pinned = false;
308 item->pinned = false;
305 return item;
309 return item;
306}
310}
plugins/Unity/Launcher/launcheritem.cpp+1−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),
@@ -48,11 +47,6 @@ QString LauncherItem::appId() const
48 return m_appId;
47 return m_appId;
49}
48}
50
49
51−QString LauncherItem::desktopFile() const
52−{
53− return m_desktopFile;
54−}
55−
56QString LauncherItem::name() const
50QString LauncherItem::name() const
57{
51{
58 return m_name;
52 return m_name;
plugins/Unity/Launcher/launcheritem.h+1−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
@@ -65,7 +64,6 @@ Q_SIGNALS:
65
64
66private:
65private:
67 QString m_appId;
66 QString m_appId;
68− QString m_desktopFile;
69 QString m_name;
67 QString m_name;
70 QString m_icon;
68 QString m_icon;
71 bool m_pinned;
69 bool m_pinned;
plugins/Unity/Launcher/launchermodel.cpp+106−49
@@ -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:
@@ -113,9 +115,11 @@ void LauncherModel::move(int oldIndex, int newIndex)
113 m_list.move(oldIndex, newIndex);
115 m_list.move(oldIndex, newIndex);
114 endMoveRows();
116 endMoveRows();
115
117
116− storeAppList();
118+ if (!m_list.at(newIndex)->pinned()) {
117−
118− pin(m_list.at(newIndex)->appId());
119+ pin(m_list.at(newIndex)->appId());
120+ } else {
121+ storeAppList();
122+ }
119}
123}
120
124
121void LauncherModel::pin(const QString &appId, int index)
125void LauncherModel::pin(const QString &appId, int index)
@@ -137,7 +141,6 @@ void LauncherModel::pin(const QString &appId, int index)
137 }
141 }
138 beginInsertRows(QModelIndex(), index, index);
142 beginInsertRows(QModelIndex(), index, index);
139 LauncherItem *item = new LauncherItem(appId,
143 LauncherItem *item = new LauncherItem(appId,
140− m_backend->desktopFile(appId),
141 m_backend->displayName(appId),
144 m_backend->displayName(appId),
142 m_backend->icon(appId));
145 m_backend->icon(appId));
143 item->setPinned(true);
146 item->setPinned(true);
@@ -145,6 +148,8 @@ void LauncherModel::pin(const QString &appId, int index)
145 m_list.insert(index, item);
148 m_list.insert(index, item);
146 endInsertRows();
149 endInsertRows();
147 }
150 }
151+
152+ storeAppList();
148}
153}
149
154
150void LauncherModel::requestRemove(const QString &appId)
155void LauncherModel::requestRemove(const QString &appId)
@@ -154,6 +159,11 @@ void LauncherModel::requestRemove(const QString &appId)
154 return;
159 return;
155 }
160 }
156
161
162+ if (m_appManager->findApplication(appId)) {
163+ m_list.at(index)->setPinned(false);
164+ return;
165+ }
166+
157 beginRemoveRows(QModelIndex(), index, index);
167 beginRemoveRows(QModelIndex(), index, index);
158 m_list.takeAt(index)->deleteLater();
168 m_list.takeAt(index)->deleteLater();
159 endRemoveRows();
169 endRemoveRows();
@@ -193,45 +203,21 @@ void LauncherModel::setUser(const QString &username)
193 m_backend->setUser(username);
203 m_backend->setUser(username);
194}
204}
195
205
196−void LauncherModel::applicationFocused(const QString &appId)
206+ApplicationManagerInterface *LauncherModel::applicationManager() const
197{
207{
198− // Unfocus all apps
199− for(int i = 0; i < m_list.count(); ++i) {
200− LauncherItem *item = m_list.at(i);
201− if (item->focused()) {
202− item->setFocused(false);
203− Q_EMIT dataChanged(this->index(i), this->index(i), QVector<int>() << RoleFocused);
204− }
205− }
206−
207− if (appId.isEmpty()) {
208− return;
208+ return m_appManager;
209− }
209+}
210−
211− // FIXME: drop this once we get real appIds from the AppManager
212− QString helper = appId.split('/').last();
213− if (helper.endsWith(".desktop")) {
214− helper.chop(8);
215− }
216−
217− int index = findApplication(helper);
218− if (index >= 0) {
219− m_list.at(index)->setFocused(true);
220− Q_EMIT dataChanged(this->index(index), this->index(index), QVector<int>() << RoleFocused);
221− } else {
222− // Add app to recent apps
223− QString desktopFile = m_backend->desktopFile(helper);
224− QString appName = m_backend->displayName(helper);
225− QString icon = m_backend->icon(helper);
226−
227− LauncherItem *item = new LauncherItem(helper, desktopFile, appName, icon);
228− item->setRecent(true);
229− item->setFocused(true);
230− beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
231− m_list.append(item);
232− endInsertRows();
233
210
234− // Clean up old recent apps
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
235 QList<int> recentAppIndices;
221 QList<int> recentAppIndices;
236 for (int i = 0; i < m_list.count(); ++i) {
222 for (int i = 0; i < m_list.count(); ++i) {
237 if (m_list.at(i)->recent()) {
223 if (m_list.at(i)->recent()) {
@@ -239,22 +225,33 @@ void LauncherModel::applicationFocused(const QString &appId)
239 }
225 }
240 }
226 }
241 int run = 0;
227 int run = 0;
242− while (recentAppIndices.count() > 5) {
228+ while (recentAppIndices.count() > 0) {
243 beginRemoveRows(QModelIndex(), recentAppIndices.first() - run, recentAppIndices.first() - run);
229 beginRemoveRows(QModelIndex(), recentAppIndices.first() - run, recentAppIndices.first() - run);
244 m_list.takeAt(recentAppIndices.first() - run)->deleteLater();
230 m_list.takeAt(recentAppIndices.first() - run)->deleteLater();
245 endRemoveRows();
231 endRemoveRows();
246 recentAppIndices.takeFirst();
232 recentAppIndices.takeFirst();
247 ++run;
233 ++run;
248 }
234 }
249− storeAppList();
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);
250 }
246 }
251}
247}
252
248
249+
253void LauncherModel::storeAppList()
250void LauncherModel::storeAppList()
254{
251{
255 QStringList appIds;
252 QStringList appIds;
256 Q_FOREACH(LauncherItem *item, m_list) {
253 Q_FOREACH(LauncherItem *item, m_list) {
257− if (item->pinned() || item->recent()) {
254+ if (item->pinned()) {
258 appIds << item->appId();
255 appIds << item->appId();
259 }
256 }
260 }
257 }
@@ -292,3 +289,63 @@ void LauncherModel::countChanged(const QString &appId, int count)
292 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
289 Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
293 }
290 }
294}
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−1
@@ -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{
@@ -49,7 +51,9 @@ public:
49 Q_INVOKABLE void requestRemove(const QString &appId);
51 Q_INVOKABLE void requestRemove(const QString &appId);
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− Q_INVOKABLE void applicationFocused(const QString &appId);
54+
55+ unity::shell::application::ApplicationManagerInterface* applicationManager() const;
56+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
53
57
54private:
58private:
55 void storeAppList();
59 void storeAppList();
@@ -59,9 +63,14 @@ private Q_SLOTS:
59 void progressChanged(const QString &appId, int progress);
63 void progressChanged(const QString &appId, int progress);
60 void countChanged(const QString &appId, int count);
64 void countChanged(const QString &appId, int count);
61
65
66+ void applicationAdded(const QModelIndex &parent, int row);
67+ void applicationRemoved(const QModelIndex &parent, int row);
68+ void focusedAppIdChanged();
69+
62private:
70private:
63 QList<LauncherItem*> m_list;
71 QList<LauncherItem*> m_list;
64 LauncherBackend *m_backend;
72 LauncherBackend *m_backend;
73+ ApplicationManagerInterface *m_appManager;
65};
74};
66
75
67#endif // LAUNCHERMODEL_H
76#endif // LAUNCHERMODEL_H
plugins/Utils/qsortfilterproxymodelqml.cpp+13−1
@@ -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 }
@@ -140,4 +152,4 @@ QSortFilterProxyModelQML::mapToSource(int row)
140 } else {
152 } else {
141 return -1;
153 return -1;
142 }
154 }
143−}
155+}
plugins/Utils/qsortfilterproxymodelqml.h+1
@@ -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 */
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/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/mocks/AccountsService/AccountsService.cpp+9−2
@@ -19,7 +19,8 @@
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
@@ -45,5 +46,11 @@ void AccountsService::setDemoEdges(bool demoEdges)
45
46
46QString AccountsService::getBackgroundFile()
47QString AccountsService::getBackgroundFile()
47{
48{
48− return TOP_SRCDIR "/graphics/phone_background.jpg";
49+ return m_backgroundFile;
50+}
51+
52+void AccountsService::setBackgroundFile(const QString &backgroundFile)
53+{
54+ m_backgroundFile = backgroundFile;
55+ backgroundFileChanged();
49}
56}
tests/mocks/AccountsService/AccountsService.h+5
@@ -37,6 +37,7 @@ class AccountsService: public QObject
37 NOTIFY demoEdgesChanged)
37 NOTIFY demoEdgesChanged)
38 Q_PROPERTY (QString backgroundFile
38 Q_PROPERTY (QString backgroundFile
39 READ getBackgroundFile
39 READ getBackgroundFile
40+ WRITE setBackgroundFile // only available in mock
40 NOTIFY backgroundFileChanged)
41 NOTIFY backgroundFileChanged)
41
42
42public:
43public:
@@ -47,11 +48,15 @@ public:
47 bool getDemoEdges();
48 bool getDemoEdges();
48 void setDemoEdges(bool demoEdges);
49 void setDemoEdges(bool demoEdges);
49 QString getBackgroundFile();
50 QString getBackgroundFile();
51+ void setBackgroundFile(const QString &backgroundFile);
50
52
51Q_SIGNALS:
53Q_SIGNALS:
52 void userChanged();
54 void userChanged();
53 void demoEdgesChanged();
55 void demoEdgesChanged();
54 void backgroundFileChanged();
56 void backgroundFileChanged();
57+
58+private:
59+ QString m_backgroundFile;
55};
60};
56
61
57#endif
62#endif
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+1
@@ -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)
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/MockLauncherModel.cpp+7−4
@@ -71,8 +71,6 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
71 LauncherItemInterface *item = m_list.at(index.row());
71 LauncherItemInterface *item = m_list.at(index.row());
72 switch(role)
72 switch(role)
73 {
73 {
74− case RoleDesktopFile:
75− return item->desktopFile();
76 case RoleName:
74 case RoleName:
77 return item->name();
75 return item->name();
78 case RoleIcon:
76 case RoleIcon:
@@ -185,7 +183,12 @@ void MockLauncherModel::setUser(const QString &username)
185 // TODO: implement this...
183 // TODO: implement this...
186}
184}
187
185
188−void MockLauncherModel::applicationFocused(const QString &appId)
186+void MockLauncherModel::setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager)
189{
187{
190− Q_UNUSED(appId)
188+ Q_UNUSED(applicationManager)
189+}
190+
191+unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
192+{
193+ return nullptr;
191}
194}
tests/mocks/Unity/Launcher/MockLauncherModel.h+3−1
@@ -44,7 +44,9 @@ public:
44 Q_INVOKABLE void requestRemove(const QString &appId);
44 Q_INVOKABLE void requestRemove(const QString &appId);
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− Q_INVOKABLE void applicationFocused(const QString &appId);
47+
48+ unity::shell::application::ApplicationManagerInterface *applicationManager() const;
49+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
48
50
49Q_SIGNALS:
51Q_SIGNALS:
50 void quickListTriggered(const QString &appId, int index);
52 void quickListTriggered(const QString &appId, int index);
tests/plugins/Unity/Launcher/CMakeLists.txt+13−1
@@ -1,5 +1,6 @@
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
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)
3
4
4include_directories(
5include_directories(
5 ${CMAKE_CURRENT_BINARY_DIR}
6 ${CMAKE_CURRENT_BINARY_DIR}
@@ -45,6 +46,8 @@ add_executable(launchermodeltestExec
45 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
46 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
46 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
47 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
47 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.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
48 )
51 )
49target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
52target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
50qt5_use_modules(launchermodeltestExec Test Core DBus)
53qt5_use_modules(launchermodeltestExec Test Core DBus)
@@ -57,3 +60,12 @@ foreach(DESKTOP_FILE ${DESKTOP_FILES})
57 DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
60 DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
58 )
61 )
59endforeach()
62endforeach()
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+33
@@ -26,6 +26,15 @@ class LauncherBackendTest : public QObject
26 Q_OBJECT
26 Q_OBJECT
27
27
28private 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+
29 void testFileNames()
38 void testFileNames()
30 {
39 {
31 LauncherBackend backend(false);
40 LauncherBackend backend(false);
@@ -54,6 +63,30 @@ private Q_SLOTS:
54 QCOMPARE(backend.isPinned("rel-icon"), true);
63 QCOMPARE(backend.isPinned("rel-icon"), true);
55 QCOMPARE(backend.isPinned("no-name"), false); // doesn't exist anymore!
64 QCOMPARE(backend.isPinned("no-name"), false); // doesn't exist anymore!
56 }
65 }
66+
67+ void testIcon_data() {
68+ QTest::addColumn<QString>("appId");
69+ QTest::addColumn<QString>("expectedIcon");
70+
71+ // Needs to expand a relative icon to the absolute path
72+ QTest::newRow("relative icon path") << "rel-icon" << QDir::currentPath() + "/rel-icon.svg";
73+
74+ // In case an icon is not found on disk, it needs to fallback on image://theme/ for it
75+ QTest::newRow("fallback on theme") << "abs-icon" << "image://theme//path/to/icon.png";
76+
77+ // Click packages have a relative icon path but an absolute path as a separate entry
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);
84+
85+ LauncherBackend backend(false);
86+ backend.setStoredApplications(QStringList() << appId);
87+
88+ QCOMPARE(backend.icon(appId), expectedIcon);
89+ }
57};
90};
58
91
59QTEST_GUILESS_MAIN(LauncherBackendTest)
92QTEST_GUILESS_MAIN(LauncherBackendTest)
tests/plugins/Unity/Launcher/launchermodeltest.cpp+148−19
@@ -19,38 +19,116 @@
19
19
20// unity-api
20// unity-api
21#include <unity/shell/launcher/LauncherModelInterface.h>
21#include <unity/shell/launcher/LauncherModelInterface.h>
22+#include <unity/shell/application/ApplicationInfoInterface.h>
22
23
23#include "launcheritem.h"
24#include "launcheritem.h"
24#include "launchermodel.h"
25#include "launchermodel.h"
25
26
26#include <QtTest>
27#include <QtTest>
27
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+
28class LauncherModelTest : public QObject
100class LauncherModelTest : public QObject
29{
101{
30 Q_OBJECT
102 Q_OBJECT
31
103
32private:
104private:
33 LauncherModel *launcherModel;
105 LauncherModel *launcherModel;
106+ MockAppManager *appManager;
34
107
35private Q_SLOTS:
108private Q_SLOTS:
36
109
37 void initTestCase() {
110 void initTestCase() {
38 launcherModel = new LauncherModel(this);
111 launcherModel = new LauncherModel(this);
39 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
112 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
113+
114+ appManager = new MockAppManager(this);
115+ launcherModel->setApplicationManager(appManager);
40 }
116 }
41
117
118+ // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
42 void init() {
119 void init() {
43− qDebug() << "init";
120+ appManager->addApplication(new MockApp("abs-icon"));
44− launcherModel->applicationFocused("abs-icon");
45 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
121 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
46
122
47− launcherModel->applicationFocused("no-icon.desktop");
123+ appManager->addApplication(new MockApp("no-icon"));
48 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
124 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
49−
50− launcherModel->applicationFocused(QString());
51 }
125 }
52
126
127+ // Removing apps from appmanager and launcher as pinned ones would stick
53 void cleanup() {
128 void cleanup() {
129+ while (appManager->count() > 0) {
130+ appManager->removeApplication(0);
131+ }
54 while (launcherModel->rowCount(QModelIndex()) > 0) {
132 while (launcherModel->rowCount(QModelIndex()) > 0) {
55 launcherModel->requestRemove(launcherModel->get(0)->appId());
133 launcherModel->requestRemove(launcherModel->get(0)->appId());
56 }
134 }
@@ -80,9 +158,54 @@ private Q_SLOTS:
80 QCOMPARE(launcherModel->get(1)->pinned(), false);
158 QCOMPARE(launcherModel->get(1)->pinned(), false);
81 }
159 }
82
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+
83 void testRemove() {
171 void testRemove() {
172+ QFETCH(bool, pinned);
173+ QFETCH(bool, running);
174+
175+ // In the beginning we always have two items
84 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
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
85− launcherModel->requestRemove(launcherModel->get(0)->appId());
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
86 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
209 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
87 }
210 }
88
211
@@ -110,6 +233,13 @@ private Q_SLOTS:
110
233
111 // quicklist needs to transform to remove item. trigger it and check it item goes away
234 // quicklist needs to transform to remove item. trigger it and check it item goes away
112 launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
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
113 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
243 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
114 }
244 }
115
245
@@ -118,27 +248,26 @@ private Q_SLOTS:
118 QCOMPARE(launcherModel->get(0)->focused(), false);
248 QCOMPARE(launcherModel->get(0)->focused(), false);
119 QCOMPARE(launcherModel->get(1)->focused(), false);
249 QCOMPARE(launcherModel->get(1)->focused(), false);
120
250
121− launcherModel->applicationFocused("abs-icon");
251+ appManager->focusApplication("abs-icon");
122 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
252 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
123 QCOMPARE(launcherModel->get(0)->focused(), true);
253 QCOMPARE(launcherModel->get(0)->focused(), true);
124 QCOMPARE(launcherModel->get(1)->focused(), false);
254 QCOMPARE(launcherModel->get(1)->focused(), false);
125
255
126− launcherModel->applicationFocused("no-icon");
256+ appManager->focusApplication("no-icon");
127 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
257 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
128 QCOMPARE(launcherModel->get(0)->focused(), false);
258 QCOMPARE(launcherModel->get(0)->focused(), false);
129 QCOMPARE(launcherModel->get(1)->focused(), true);
259 QCOMPARE(launcherModel->get(1)->focused(), true);
260+ }
130
261
262+ void testClosingApps() {
263+ // At the start there are 2 items. Let's pin one.
131− launcherModel->applicationFocused("rel-icon");
264+ launcherModel->pin("abs-icon");
132− QCOMPARE(launcherModel->rowCount(QModelIndex()), 3);
265+ while (appManager->count() > 0) {
133− QCOMPARE(launcherModel->get(0)->focused(), false);
266+ appManager->removeApplication(0);
134− QCOMPARE(launcherModel->get(1)->focused(), false);
267+ }
135− QCOMPARE(launcherModel->get(2)->focused(), true);
268+ // The pinned one needs to stay, the other needs to disappear
136−
137− launcherModel->applicationFocused(QString());
138− QCOMPARE(launcherModel->rowCount(QModelIndex()), 3);
269+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
139− QCOMPARE(launcherModel->get(0)->focused(), false);
270+ QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
140− QCOMPARE(launcherModel->get(1)->focused(), false);
141− QCOMPARE(launcherModel->get(2)->focused(), false);
142 }
271 }
143};
272};
144
273
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/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+2−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()
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);