on xenial help sign in
merge launcher-items
commitc47c68bfc42b2699f9df2d4c89ad896cec1d5dac authorMichael Zanetti <michael.zanetti@canonical.com> dateWed 28 Aug 2013, 16:56 (13 years ago) parent81d910b508d51d
149 files changed+3657−858
Components/EdgeDemo.qml+228 Components/DemoOverlay.qml → Components/EdgeDemoOverlay.qml+61−44 Dash/Apps/FrequentlyUsedAppsModel.qml+1−1 Dash/GenericScopeView.qml+6 Launcher/FoldingLauncherDelegate.qml+35−24 Launcher/Launcher.qml+5−3 Launcher/LauncherDelegate.qml+2−2 Launcher/LauncherPanel.qml+59−17 Launcher/QuicklistForegroundStyle.qml+23 Launcher/graphics/quicklist_tooltip@30.png Panel/Indicators/NetworkIndicatorPage2.qml+82 Panel/IndicatorsDataModel.qml+7−9 Shell.qml+32−138 build+7−3 debian/changelog+171 debian/control+2−1 debian/rules+5 main.cpp−1 plugins/AccountsService/AccountsService.cpp+8−4 plugins/AccountsService/AccountsService.h+7−9 plugins/AccountsService/CMakeLists.txt+1−3 plugins/AccountsService/com.canonical.unity.AccountsService.xml+2−2 plugins/AccountsService/plugin.cpp+3−1 plugins/AccountsService/plugin.h+2−2 plugins/HudClient/qmldir+1 plugins/LightDM/plugin.cpp+1−1 plugins/Powerd/Powerd.cpp+2−2 plugins/Powerd/Powerd.h+15−1 plugins/Powerd/plugin.cpp+1−1 plugins/Ubuntu/DownloadDaemonListener/plugin.cpp+1−1 plugins/Unity/Indicators/CMakeLists.txt+1 plugins/Unity/Indicators/Network/CMakeLists.txt+45 plugins/Unity/Indicators/Network/networkagent.cpp+146 plugins/Unity/Indicators/Network/networkagent.h+65 plugins/Unity/Indicators/Network/plugin.cpp+31 plugins/Unity/Indicators/Network/plugin.h+32 plugins/Unity/Indicators/Network/qml/AccessPoint.qml+72 plugins/Unity/Indicators/Network/qml/CMakeLists.txt+5 plugins/Unity/Indicators/Network/qml/PasswordPage.qml+88 plugins/Unity/Indicators/Network/qml/PasswordTextField.qml+68 plugins/Unity/Indicators/Network/qml/WifiSection.qml+34 plugins/Unity/Indicators/Network/qml/qmldir+8 plugins/Unity/Indicators/Network/secret-agent.c+382 plugins/Unity/Indicators/Network/secret-agent.h+102 plugins/Unity/Indicators/indicatorsmodel.cpp+2−2 plugins/Unity/Indicators/plugin.cpp+1−1 plugins/Unity/Indicators/qml/BaseMenuItem.qml−4 plugins/Unity/Indicators/qml/ButtonMenuItem.qml+4−9 plugins/Unity/Indicators/qml/FramedMenuItem.qml−2 plugins/Unity/Indicators/qml/IndicatorPage.qml+4−9 plugins/Unity/Indicators/qml/MenuAction.qml−59 plugins/Unity/Indicators/qml/MenuItemFactory.qml+143−5 plugins/Unity/Indicators/qml/ProgressMenuItem.qml+1−8 plugins/Unity/Indicators/qml/SectionMenuItem.qml+3−3 plugins/Unity/Indicators/qml/SliderMenuItem.qml+8−23 plugins/Unity/Indicators/qml/StandardMenuItem.qml+5−10 plugins/Unity/Indicators/qml/SwitchMenuItem.qml+3−8 plugins/Unity/Indicators/qml/qmldir−1 plugins/Unity/IndicatorsLegacy/Network/CMakeLists.txt+8−8 plugins/Unity/IndicatorsLegacy/Network/plugin.cpp+1−1 plugins/Unity/IndicatorsLegacy/Network/qml/qmldir+1−1 plugins/Unity/IndicatorsLegacy/plugin.cpp+1−1 plugins/Unity/Launcher/backend/launcherbackend.cpp+91−87 plugins/Unity/Launcher/backend/launcherbackend.h+13−2 plugins/Unity/Launcher/launchermodel.cpp+28 plugins/Unity/Launcher/launchermodel.h+4 plugins/Unity/Launcher/plugin.cpp+1−1 plugins/Unity/categories.cpp+6 plugins/Unity/categories.h+1 plugins/Unity/genericoptionsmodel.cpp+2−2 plugins/Unity/genericoptionsmodel.h+1−1 plugins/Unity/plugin.cpp+4−1 plugins/Utils/plugin.cpp+1−1 plugins/Utils/qlimitproxymodelqml.cpp+90−4 plugins/Utils/qlimitproxymodelqml.h+2 po/ar.po+2−2 po/az.po+2−2 po/bg.po+2−2 po/ca.po+2−2 po/de.po+2−2 po/el.po+2−2 po/en_AU.po+2−2 po/en_GB.po+2−2 po/eo.po+2−2 po/es.po+2−2 po/fi.po+13−13 po/fo.po+2−2 po/fr.po+2−2 po/gl.po+2−2 po/he.po+2−2 po/hr.po+2−2 po/hu.po+2−2 po/hy.po+2−2 po/id.po+2−2 po/it.po+2−2 po/ja.po+2−2 po/kn.po+2−2 po/ko.po+2−2 po/ky.po+2−2 po/lv.po+2−2 po/my.po+2−2 po/nb.po+2−2 po/nl.po+2−2 po/pl.po+2−2 po/pt_BR.po+2−2 po/ro.po+2−2 po/ru.po+2−2 po/si.po+2−2 po/sl.po+2−2 po/sr.po+2−2 po/sv.po+2−2 po/te.po+2−2 po/tr.po+2−2 po/ug.po+2−2 po/uk.po+2−2 po/unity8.pot+51−43 po/zh_CN.po+2−2 po/zh_TW.po+2−2 src/Panel/Indicators/servicefiles/CMakeLists.txt−1 src/Panel/Indicators/servicefiles/com.canonical.settings.network−10 tests/autopilot/unity8/shell/emulators/create_interactive_notification.py+102 tests/autopilot/unity8/shell/tests/__init__.py+20−20 tests/autopilot/unity8/shell/tests/test_notifications.py+737 tests/mocks/AccountsService/AccountsService.h+5−6 tests/mocks/AccountsService/plugin.cpp+1−1 tests/mocks/AccountsService/plugin.h+2−2 tests/mocks/GSettings.1.0/fake_gsettings.h+1−1 tests/mocks/GSettings.1.0/plugin.cpp+1−1 tests/mocks/Powerd/Powerd.h+15−2 tests/mocks/Powerd/plugin.cpp+1−1 tests/mocks/Ubuntu/DownloadDaemonListener/plugin.cpp−1 tests/mocks/Unity/Indicators/fakeplugin.cpp+1−1 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+6 tests/mocks/Unity/Launcher/MockLauncherModel.h+1 tests/mocks/Unity/fake_unity_plugin.cpp+2−1 tests/plugins/AccountsService/AccountsServer.h+4−4 tests/plugins/AccountsService/PropertiesServer.h+4−4 tests/plugins/AccountsService/client.cpp+2−2 tests/plugins/AccountsService/server.cpp+1−1 tests/plugins/Unity/Launcher/launcherbackendtest.cpp+2−8 tests/plugins/Utils/qlimitproxymodeltest.cpp+133−8 tests/qmltests/CMakeLists.txt+1−1 tests/qmltests/Components/tst_DemoOverlay.qml−99 tests/qmltests/Components/tst_EdgeDemoOverlay.qml+123 tests/qmltests/Greeter/tst_Clock.qml+1−1 tests/qmltests/Launcher/tst_Launcher.qml+38−14 tests/qmltests/tst_Shell.qml+7−3 tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+1−1 tests/utils/modules/Unity/Test/UnityTestCase.qml+21−2
Components/EdgeDemo.qmlnew file+228
@@ -0,0 +1,228 @@
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 AccountsService 0.1
18+import LightDM 0.1 as LightDM
19+import QtQuick 2.0
20+
21+Item {
22+ id: demo
23+
24+ property Item greeter
25+ property Item dash
26+ property Item launcher
27+ property Item indicators
28+ property Item overlay
29+
30+ property bool launcherEnabled: true
31+ property bool dashEnabled: true
32+ property bool panelEnabled: true
33+
34+ property bool paused: false
35+
36+ onPausedChanged: {
37+ if (d.rightEdgeDemo) d.rightEdgeDemo.paused = paused
38+ if (d.topEdgeDemo) d.topEdgeDemo.paused = paused
39+ if (d.bottomEdgeDemo) d.bottomEdgeDemo.paused = paused
40+ if (d.leftEdgeDemo) d.leftEdgeDemo.paused = paused
41+ if (d.finalEdgeDemo) d.finalEdgeDemo.paused = paused
42+ }
43+
44+ function hideEdgeDemoInShell() {
45+ var user = LightDM.Users.data(greeter.currentIndex, LightDM.UserRoles.NameRole);
46+ AccountsService.setUserProperty(user, "demo-edges", false);
47+ d.showEdgeDemo = false;
48+ stopDemo();
49+ }
50+
51+ function hideEdgeDemoInGreeter() {
52+ // TODO: AccountsService.setUserProperty("lightdm", "demo-edges", false);
53+ d.showEdgeDemoInGreeter = false;
54+ }
55+
56+ function hideEdgeDemos() {
57+ hideEdgeDemoInGreeter();
58+ hideEdgeDemoInShell();
59+ }
60+
61+ function stopDemo() {
62+ launcherEnabled = true
63+ dashEnabled = true
64+ panelEnabled = true
65+ if (d.rightEdgeDemo) d.rightEdgeDemo.destroy()
66+ if (d.topEdgeDemo) d.topEdgeDemo.destroy()
67+ if (d.bottomEdgeDemo) d.bottomEdgeDemo.destroy()
68+ if (d.leftEdgeDemo) d.leftEdgeDemo.destroy()
69+ if (d.finalEdgeDemo) d.finalEdgeDemo.destroy()
70+ }
71+
72+ QtObject {
73+ id: d
74+ property Component overlay
75+ property QtObject rightEdgeDemo
76+ property QtObject topEdgeDemo
77+ property QtObject bottomEdgeDemo
78+ property QtObject leftEdgeDemo
79+ property QtObject finalEdgeDemo
80+ property bool showEdgeDemo: false
81+ property bool showEdgeDemoInGreeter: d.showEdgeDemo // TODO: AccountsService.getUserProperty("lightdm", "demo-edges")
82+
83+ onShowEdgeDemoInGreeterChanged: {
84+ if (!d.overlay && d.showEdgeDemoInGreeter) {
85+ d.overlay = Qt.createComponent("EdgeDemoOverlay.qml")
86+ startRightEdgeDemo()
87+ }
88+ }
89+ }
90+
91+ function startRightEdgeDemo() {
92+ launcherEnabled = false;
93+ demo.dashEnabled = false;
94+ demo.panelEnabled = false;
95+ if (demo.greeter) {
96+ d.rightEdgeDemo = d.overlay.createObject(demo.greeter, {
97+ "edge": "right",
98+ "title": i18n.tr("Right edge"),
99+ "text": i18n.tr("Try swiping from the right edge to unlock the phone"),
100+ "anchors.fill": demo.greeter,
101+ });
102+ }
103+ if (d.rightEdgeDemo) {
104+ d.rightEdgeDemo.onSkip.connect(demo.hideEdgeDemos)
105+ } else {
106+ stopDemo();
107+ }
108+ }
109+
110+ Connections {
111+ target: demo.greeter
112+
113+ function hide() {
114+ if (d.rightEdgeDemo && d.rightEdgeDemo.active) {
115+ d.rightEdgeDemo.hide()
116+ hideEdgeDemoInGreeter()
117+ startTopEdgeDemo()
118+ }
119+ }
120+
121+ onUnlocked: hide()
122+ onShownChanged: if (!greeter.shown) hide()
123+
124+ onSelected: {
125+ var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole)
126+ d.showEdgeDemo = AccountsService.getUserProperty(user, "demo-edges")
127+ }
128+ }
129+
130+ function startTopEdgeDemo() {
131+ demo.panelEnabled = true;
132+ if (demo.dash) {
133+ d.topEdgeDemo = d.overlay.createObject(demo.dash, {
134+ "edge": "top",
135+ "title": i18n.tr("Top edge"),
136+ "text": i18n.tr("Try swiping from the top edge to access the indicators"),
137+ "anchors.fill": demo.dash,
138+ });
139+ }
140+ if (d.topEdgeDemo) {
141+ d.topEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
142+ } else {
143+ stopDemo();
144+ }
145+ }
146+
147+ Connections {
148+ target: demo.indicators
149+ onFullyOpenedChanged: {
150+ if (d.topEdgeDemo && d.topEdgeDemo.active && demo.indicators.fullyOpened) {
151+ d.topEdgeDemo.hideNow()
152+ startBottomEdgeDemo()
153+ }
154+ }
155+ }
156+
157+ function startBottomEdgeDemo() {
158+ if (demo.indicators) {
159+ d.bottomEdgeDemo = d.overlay.createObject(demo.indicators, {
160+ "edge": "bottom",
161+ "title": i18n.tr("Close"),
162+ "text": i18n.tr("Swipe up again to close the settings screen"),
163+ "anchors.fill": demo.indicators,
164+ });
165+ }
166+ if (d.bottomEdgeDemo) {
167+ d.bottomEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
168+ } else {
169+ stopDemo();
170+ }
171+ }
172+
173+ Connections {
174+ target: demo.indicators
175+ onPartiallyOpenedChanged: {
176+ if (d.bottomEdgeDemo && d.bottomEdgeDemo.active && !demo.indicators.partiallyOpened && !demo.indicators.fullyOpened) {
177+ d.bottomEdgeDemo.hideNow()
178+ startLeftEdgeDemo()
179+ }
180+ }
181+ }
182+
183+ function startLeftEdgeDemo() {
184+ demo.panelEnabled = false;
185+ demo.launcherEnabled = true;
186+ if (demo.dash) {
187+ d.leftEdgeDemo = d.overlay.createObject(demo.dash, {
188+ "edge": "left",
189+ "title": i18n.tr("Left edge"),
190+ "text": i18n.tr("Swipe from the left to reveal the launcher for quick access to apps"),
191+ "anchors.fill": demo.dash,
192+ });
193+ }
194+ if (d.leftEdgeDemo) {
195+ d.leftEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
196+ } else {
197+ stopDemo();
198+ }
199+ }
200+
201+ Connections {
202+ target: demo.launcher
203+ onStateChanged: {
204+ if (d.leftEdgeDemo && d.leftEdgeDemo.active && launcher.state == "visible") {
205+ d.leftEdgeDemo.hide()
206+ launcher.hide()
207+ startFinalEdgeDemo()
208+ }
209+ }
210+ }
211+
212+ function startFinalEdgeDemo() {
213+ demo.launcherEnabled = false;
214+ if (demo.dash) {
215+ d.finalEdgeDemo = d.overlay.createObject(demo.overlay, {
216+ "edge": "none",
217+ "title": i18n.tr("Well done"),
218+ "text": i18n.tr("You have now mastered the edge gestures and can start using the phone"),
219+ "anchors.fill": demo.overlay,
220+ });
221+ }
222+ if (d.finalEdgeDemo) {
223+ d.finalEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
224+ } else {
225+ stopDemo();
226+ }
227+ }
228+}
Components/DemoOverlay.qml → Components/EdgeDemoOverlay.qmlrenamed+61−44
@@ -14,12 +14,13 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17+import Powerd 0.1
1718import QtQuick 2.0
1819import QtGraphicalEffects 1.0
1920import Ubuntu.Application 0.1
2021import Ubuntu.Components 0.1
2122
22−Item {
23+Showable {
2324 id: overlay
2425
2526 /*
@@ -30,38 +31,69 @@ Item {
3031 /*
3132 * This is the header displayed, like "Right edge".
3233 */
33− property string title
34+ property alias title: titleLabel.text
3435
3536 /*
3637 * This is the block of text displayed below the header.
3738 */
38− property string text
39+ property alias text: textLabel.text
3940
4041 /*
4142 * This is the text for the skip button.
4243 */
43− property string skipText: i18n.tr("Skip intro")
44+ property alias skipText: skipLabel.text
4445
4546 /*
46− * Whether this demo is allowed to run.
47+ * Whether this demo is running currently.
4748 */
48− property bool enabled: true
49+ readonly property bool active: available && visible
4950
5051 /*
51− * Whether this demo is running currently.
52+ * Whether animations are paused.
5253 */
53− readonly property bool active: enabled && visible
54+ property alias paused: wholeAnimation.paused
5455
55− property color __orange: Qt.hsla(16.0/360.0, 0.83, 0.47, 1.0)
56− property color __orange_transparent: Qt.hsla(16.0/360.0, 0.83, 0.47, 0.4)
57− property int __edge_margin: units.gu(4)
58− property int __text_margin: units.gu(3)
59− property int __anim_running: wholeAnimation.running // for testing
56+ /*
57+ * Whether animations are running.
58+ */
59+ readonly property alias running: wholeAnimation.running
6060
6161 signal skip()
6262
63+ function doSkip() {
64+ d.skipOnHide = true;
65+ hide();
66+ }
67+
68+ function hideNow() {
69+ overlay.visible = false;
70+ overlay.available = false;
71+ if (d.skipOnHide) {
72+ overlay.skip();
73+ }
74+ }
75+
76+ showAnimation: StandardAnimation {
77+ property: "opacity"
78+ to: 1
79+ onRunningChanged: if (running) overlay.visible = true
80+ }
81+ hideAnimation: StandardAnimation {
82+ property: "opacity"
83+ to: 0
84+ duration: UbuntuAnimation.BriskDuration
85+ onRunningChanged: if (!running) overlay.hideNow()
86+ }
87+
88+ QtObject {
89+ id: d
90+ property bool skipOnHide: false
91+ property int edgeMargin: units.gu(4)
92+ }
93+
6394 Rectangle {
64− id: backgroundShade
95+ objectName: "backgroundShade"
96+
6597 anchors.fill: parent
6698 color: "black"
6799 opacity: 0.8
@@ -70,7 +102,7 @@ Item {
70102 MouseArea {
71103 anchors.fill: parent
72104 enabled: overlay.edge == "none" && overlay.opacity == 1.0
73− onClicked: fadeOutAnimation.running = true
105+ onClicked: overlay.doSkip()
74106 }
75107 }
76108
@@ -85,10 +117,10 @@ Item {
85117 Column {
86118 id: labelGroup
87119 layer.enabled: true // otherwise the underlining on "Skip intro" jumps
88− spacing: overlay.__text_margin
120+ spacing: units.gu(3)
89121
90122 anchors {
91− margins: overlay.__edge_margin
123+ margins: d.edgeMargin
92124 left: parent.left
93125 top: overlay.edge == "bottom" ? undefined : parent.top
94126 bottom: overlay.edge == "bottom" ? parent.bottom : undefined
@@ -96,7 +128,6 @@ Item {
96128
97129 Label {
98130 id: titleLabel
99− text: overlay.title
100131 fontSize: "x-large"
101132 width: units.gu(25)
102133 wrapMode: Text.WordWrap
@@ -104,7 +135,6 @@ Item {
104135
105136 Label {
106137 id: textLabel
107− text: overlay.text
108138 width: units.gu(25)
109139 wrapMode: Text.WordWrap
110140 }
@@ -112,14 +142,14 @@ Item {
112142 Label {
113143 id: skipLabel
114144 objectName: "skipLabel"
115− text: overlay.skipText
116− color: overlay.__orange
145+ text: i18n.tr("Skip intro")
146+ color: UbuntuColors.orange
117147 fontSize: "small"
118148 font.underline: true
119149
120150 MouseArea {
121151 anchors.fill: parent
122− onClicked: fadeOutAnimation.running = true
152+ onClicked: overlay.doSkip()
123153 }
124154 }
125155 }
@@ -132,11 +162,11 @@ Item {
132162 gradient: Gradient {
133163 GradientStop {
134164 position: 0.0
135− color: overlay.__orange_transparent
165+ color: Qt.hsla(16.0/360.0, 0.83, 0.47, 0.4) // UbuntuColors.orange, but transparent
136166 }
137167 GradientStop {
138168 position: 1.0
139− color: "#00000000"
169+ color: "transparent"
140170 }
141171 }
142172 anchors.fill: parent
@@ -165,22 +195,9 @@ Item {
165195 }
166196 }
167197
168− PropertyAnimation {
169− id: fadeOutAnimation
170− target: overlay
171− property: "opacity"
172− to: 0
173− duration: 250
174−
175− onStopped: {
176− overlay.enabled = false;
177− overlay.visible = false;
178− skip()
179− }
180− }
181−
182198 SequentialAnimation {
183199 id: wholeAnimation
200+ objectName: "wholeAnimation"
184201 running: overlay.active
185202
186203 ParallelAnimation {
@@ -199,20 +216,20 @@ Item {
199216 }
200217 from: {
201218 if (overlay.edge == "right") {
202− return overlay.__edge_margin + units.gu(3)
219+ return d.edgeMargin + units.gu(3)
203220 } else {
204− return overlay.__edge_margin - units.gu(3)
221+ return d.edgeMargin - units.gu(3)
205222 }
206223 }
207− to: overlay.__edge_margin
208− duration: overlay.edge == "none" ? 0 : 1000
224+ to: d.edgeMargin
225+ duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
209226 }
210227 StandardAnimation {
211228 target: overlay
212229 property: "opacity"
213230 from: 0.0
214231 to: 1.0
215− duration: 1000
232+ duration: UbuntuAnimation.SleepyDuration
216233 }
217234 }
218235
@@ -222,7 +239,7 @@ Item {
222239 property string prop: (overlay.edge == "left" || overlay.edge == "right") ? "x" : "y"
223240 property double endVal: units.dp(5) * ((overlay.edge == "left" || overlay.edge == "top") ? 1 : -1)
224241 property double maxGlow: units.dp(20)
225− property int duration: overlay.edge == "none" ? 0 : 1000
242+ property int duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
226243
227244 ParallelAnimation {
228245 StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: 0; to: hintAnimation.endVal; duration: hintAnimation.duration }
Dash/Apps/FrequentlyUsedAppsModel.qml+1−1
@@ -38,7 +38,7 @@ ListModel {
3838 metadata: ""
3939 }
4040 ListElement {
41− uri: "application:///usr/share/applications/galery-app.desktop"
41+ uri: "application:///usr/share/applications/gallery-app.desktop"
4242 icon: "../../graphics/applicationIcons/gallery.png"
4343 category: 0
4444 mimetype: "application/x-desktop"
Dash/GenericScopeView.qml+6
@@ -80,6 +80,12 @@ ScopeView {
8080 item.model = results
8181 }
8282 item.objectName = categoryId
83+ if (item.expandable) {
84+ var shouldFilter = index != categoryView.expandedIndex;
85+ if (shouldFilter != item.filter) {
86+ item.filter = shouldFilter;
87+ }
88+ }
8389 }
8490
8591 Connections {
Launcher/FoldingLauncherDelegate.qml+35−24
@@ -26,21 +26,21 @@ LauncherDelegate {
2626 if (index == 0 || index == priv.listView.count-1) {
2727 if (priv.distanceFromEdge < 0) {
2828 // proportion equation: distanceFromTopEdge : angle = totalUnfoldedHeight/2 : maxAngle
29− return Math.max(-maxAngle, priv.distanceFromEdge * maxAngle / (priv.listView.foldingAreaHeight)) * priv.orientationFlag
29+ return Math.max(-maxAngle, priv.distanceFromEdge * maxAngle / (priv.foldingAreaHeight)) * priv.orientationFlag
3030 }
3131 return 0; // Don't fold first/last item as long as inside the view
3232 }
3333
34− // Are we in the already completely outside the flickable? Fold for the last 5 degrees
35− if (priv.distanceFromEdge < 0) {
36− // proportion equation: -distanceFromTopEdge : angle = totalUnfoldedHeight : 5
37− return Math.max(-maxAngle, (priv.distanceFromEdge * 5 / priv.listView.foldingAreaHeight) - (maxAngle-5)) * priv.orientationFlag
34+ // We reached the folded area... fold the last 5 degrees
35+ if (priv.overlapWithFoldedArea > 0) {
36+ // proportion equation: overlap : x = height : 5
37+ return ((maxAngle - 5) + (priv.overlapWithFoldedArea * 5 / priv.foldingAreaHeight)) * -priv.orientationFlag
3838 }
3939
4040 // We are overlapping with the folding area, fold the icon to maxAngle - 5 degrees
4141 if (priv.overlapWithFoldingArea > 0) {
4242 // proportion equation: overlap: totalHeight = angle : (maxAngle - 5)
43− return -priv.overlapWithFoldingArea * (maxAngle -5) / priv.listView.foldingAreaHeight * priv.orientationFlag;
43+ return -priv.overlapWithFoldingArea * (maxAngle -5) / priv.foldingAreaHeight * priv.orientationFlag;
4444 }
4545 return 0;
4646 }
@@ -61,9 +61,9 @@ LauncherDelegate {
6161 return (-priv.distanceFromEdge - (root.height - effectiveHeight)) * priv.orientationFlag;
6262 }
6363
64− // We're touching the edge, move slower than the actual flicking speed.
65− if (priv.distanceFromEdge < 0) {
66− return (Math.abs(priv.distanceFromEdge) * priv.totalEffectiveHeight / priv.totalUnfoldedHeight) * priv.orientationFlag
64+ // We stopped folding, move slower than the actual flicking speed.
65+ if (priv.overlapWithFoldedArea > 0) {
66+ return (priv.overlapWithFoldedArea * priv.totalEffectiveHeight / (priv.totalUnfoldedHeight + priv.listView.foldingStopHeight)) * priv.orientationFlag;
6767 }
6868 return 0;
6969 }
@@ -72,22 +72,23 @@ LauncherDelegate {
7272 // First/last items are special
7373 if (index == 0 || index == priv.listView.count-1) {
7474 if (priv.distanceFromEdge < 0) {
75− // Fade from 1 to 0 in the distance of 3 * foldingAreaHeight (which is when the next item reaches the edge)
76− return 1.0 - (-priv.distanceFromEdge / (priv.listView.foldingAreaHeight * 3))
75+ // Fade from 1 to 0 while traversing a distance of 2*foldingAreaHeight
76+ // proportion equation: 0.5 : x = -2*foldingAreaHeight : distance
77+ return 1 + (priv.distanceFromEdge / (priv.foldingAreaHeight * 2))
7778 }
7879 return 1; // Don't make first/last item transparent as long as inside the view
7980 }
8081
81− // Are we already completely outside the flickable? Fade from 0.75 to 0 in 2 items height
82− if (priv.distanceFromEdge < 0) {
83− // proportion equation: -distanceFromEdge : 1-opacity = totalUnfoldedHeight : 0.75
84− return 0.75 - (-priv.distanceFromEdge * 0.75 / (priv.totalUnfoldedHeight*2))
82+ // Did we stop folding? Fade out to 0 in 2*foldingAreaHeight
83+ if (priv.overlapWithFoldedArea > 0) {
84+ // overlap : foldingAreaHeight = opacity : 0.75
85+ return 0.75 - (priv.overlapWithFoldedArea * 0.75 / (priv.foldingAreaHeight * 2));
8586 }
8687
87− // We are overlapping with the folding area, fade out to 0.75
88+ // We are overlapping with the folding area, fade out to 0.75 transparency
8889 if (priv.overlapWithFoldingArea > 0) {
8990 // proportion equation: overlap : totalHeight = 1-opacity : 0.25
90− return 1 - (priv.overlapWithFoldingArea * 0.25 / priv.listView.foldingAreaHeight)
91+ return 1 - (priv.overlapWithFoldingArea * 0.25 / priv.foldingAreaHeight)
9192 }
9293 return 1;
9394 }
@@ -95,19 +96,27 @@ LauncherDelegate {
9596 brightness: {
9697 // First/last items are special
9798 if (index == 0 || index == priv.listView.count-1) {
99+
100+ // Traversed one foldingAreaHeight. Stop at 0.3
101+ if (priv.distanceFromEdge < -priv.foldingAreaHeight) {
102+ return -0.3
103+ }
104+
105+ // We started moving, fade to 0.3
98106 if (priv.distanceFromEdge < 0) {
99− return -(-priv.distanceFromEdge / (priv.listView.foldingAreaHeight * 3))
107+ return -0.3 * (-priv.distanceFromEdge / (priv.foldingAreaHeight))
100108 }
101109 return 0;
102110 }
103− // Are we already completely outside the flickable? Fade from 0.7 to 0 in 2 items height
104− if (priv.distanceFromEdge < 0) {
105− return -0.3 - (-priv.distanceFromEdge * 0.1 / (priv.totalUnfoldedHeight*2))
111+
112+ // We stopped folding? Stop brightness change at 0.3
113+ if (priv.overlapWithFoldedArea > 0) {
114+ return -0.3;
106115 }
107116
108− // We are overlapping with the folding area, fade out to 0.7
117+ // We are overlapping with the folding area, fade out to 0.3
109118 if (priv.overlapWithFoldingArea > 0) {
110− return - (priv.overlapWithFoldingArea * 0.3 / priv.listView.foldingAreaHeight)
119+ return - (priv.overlapWithFoldingArea * 0.3 / priv.listView.foldingStartHeight);
111120 }
112121 return 0;
113122 }
@@ -127,7 +136,9 @@ LauncherDelegate {
127136 property real distanceFromEdge: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? distanceFromBottomEdge : distanceFromTopEdge
128137 property real orientationFlag: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? -1 : 1
129138
130− property real overlapWithFoldingArea: listView.foldingAreaHeight - distanceFromEdge
139+ property real overlapWithFoldingArea: listView.foldingStartHeight - distanceFromEdge
140+ property real overlapWithFoldedArea: listView.foldingStopHeight - distanceFromEdge
141+ property real foldingAreaHeight: listView.foldingStartHeight - listView.foldingStopHeight
131142 }
132143
133144}
Launcher/Launcher.qml+5−3
@@ -26,9 +26,11 @@ Item {
2626
2727 property bool available: true // can be used to disable all interactions
2828
29− property int panelWidth: units.gu(8.5)
29+ property int panelWidth: units.gu(8)
3030 property int dragAreaWidth: units.gu(1)
31− property real progress: dragArea.dragging && dragArea.touchX > panel.width ? dragArea.touchX : 0
31+ property int minimizeDistance: panelWidth * 2.5
32+ property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
33+ (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
3234
3335 readonly property bool shown: panel.x > -panel.width
3436
@@ -207,7 +209,7 @@ Item {
207209 if (!dragging) {
208210 if (distance > panel.width / 2) {
209211 root.switchToNextState("visible")
210− if (distance > panel.width * 2) {
212+ if (distance > minimizeDistance) {
211213 root.dash()
212214 }
213215 } else {
Launcher/LauncherDelegate.qml+2−2
@@ -80,7 +80,7 @@ Item {
8080 top: parent.top
8181 margins: -units.dp(1)
8282 }
83− width: Math.min(units.gu(7.5), Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
83+ width: Math.min(root.itemWidth, Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
8484 height: units.gu(3)
8585 source: "graphics/notification.sci"
8686 visible: root.count > 0
@@ -89,7 +89,7 @@ Item {
8989 id: countLabel
9090 text: root.count
9191 anchors.centerIn: parent
92− width: units.gu(6)
92+ width: root.itemWidth - units.gu(1)
9393 horizontalAlignment: Text.AlignHCenter
9494 elide: Text.ElideRight
9595 color: "white"
Launcher/LauncherPanel.qml+59−17
@@ -62,7 +62,7 @@ Item {
6262 z: 1
6363 Image {
6464 objectName: "dashItem"
65− width: units.gu(5.5)
65+ width: units.gu(5)
6666 height: width
6767 anchors.centerIn: parent
6868 source: "graphics/home.png"
@@ -113,14 +113,22 @@ Item {
113113 property int extensionSize: itemHeight * 3
114114
115115 // The height of the area where icons start getting folded
116− property int foldingAreaHeight: itemHeight * 0.75
117− property int itemWidth: width
118− property int itemHeight: width * 7.5 / 8
116+ property int foldingStartHeight: units.gu(6.5)
117+ // The height of the area where the items reach the final folding angle
118+ property int foldingStopHeight: foldingStartHeight - itemHeight - spacing
119+ property int itemWidth: units.gu(7)
120+ property int itemHeight: units.gu(6.5)
119121 property int clickFlickSpeed: units.gu(60)
120122 property int draggedIndex: dndArea.draggedIndex
121123 property real realContentY: contentY - originY + topMargin
122124 property int realItemHeight: itemHeight + spacing
123125
126+ // In case the start dragging transition is running, we need to delay the
127+ // move because the displaced transition would clash with it and cause items
128+ // to be moved to wrong places
129+ property bool draggingTransitionRunning: false
130+ property int scheduledMoveTo: -1
131+
124132 displaced: Transition {
125133 NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
126134 }
@@ -136,9 +144,8 @@ Item {
136144 count: model.count
137145 progress: model.progress
138146 inverted: root.inverted
139− highlighted: dragging && index === root.highlightIndex
140147 z: -Math.abs(offset)
141− maxAngle: 60
148+ maxAngle: 55
142149 property bool dragging: false
143150
144151 ThinDivider {
@@ -202,10 +209,26 @@ Item {
202209 UbuntuNumberAnimation { properties: "angle,offset" }
203210 },
204211 Transition {
212+ id: draggingTransition
205213 from: "selected"
206214 to: "dragging"
207− UbuntuNumberAnimation { properties: "height" }
208− NumberAnimation { target: dropIndicator; properties: "opacity"; duration: UbuntuAnimation.FastDuration }
215+ SequentialAnimation {
216+ PropertyAction { target: launcherListView; property: "draggingTransitionRunning"; value: true }
217+ ParallelAnimation {
218+ UbuntuNumberAnimation { properties: "height" }
219+ NumberAnimation { target: dropIndicator; properties: "opacity"; duration: UbuntuAnimation.FastDuration }
220+ }
221+ ScriptAction {
222+ script: {
223+ if (launcherListView.scheduledMoveTo > -1) {
224+ launcherListView.model.move(dndArea.draggedIndex, launcherListView.scheduledMoveTo)
225+ dndArea.draggedIndex = launcherListView.scheduledMoveTo
226+ launcherListView.scheduledMoveTo = -1
227+ }
228+ }
229+ }
230+ PropertyAction { target: launcherListView; property: "draggingTransitionRunning"; value: false }
231+ }
209232 },
210233 Transition {
211234 from: "dragging"
@@ -213,7 +236,9 @@ Item {
213236 NumberAnimation { target: dropIndicator; properties: "opacity"; duration: UbuntuAnimation.FastDuration }
214237 NumberAnimation { properties: "itemOpacity"; duration: UbuntuAnimation.BriskDuration }
215238 SequentialAnimation {
239+ ScriptAction { script: if (index == launcherListView.count-1) launcherListView.flick(0, -launcherListView.clickFlickSpeed); }
216240 UbuntuNumberAnimation { properties: "height" }
241+ ScriptAction { script: if (index == launcherListView.count-1) launcherListView.flick(0, -launcherListView.clickFlickSpeed); }
217242 PropertyAction { target: dndArea; property: "postDragging"; value: false }
218243 PropertyAction { target: dndArea; property: "draggedIndex"; value: -1 }
219244 }
@@ -235,7 +260,7 @@ Item {
235260 property int draggedIndex: -1
236261 property var selectedItem
237262 property bool preDragging: false
238− property bool dragging: selectedItem !== undefined && selectedItem.dragging
263+ property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging
239264 property bool postDragging: false
240265 property int startX
241266 property int startY
@@ -243,13 +268,17 @@ Item {
243268
244269 onPressed: {
245270 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
246− selectedItem.highlighted = true
247271 }
248272
249273 onClicked: {
250274 var index = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
251275 var clickedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
252276
277+ // Check if we actually clicked an item or only at the spacing in between
278+ if (clickedItem === null) {
279+ return;
280+ }
281+
253282 // First/last item do the scrolling at more than 12 degrees
254283 if (index == 0 || index == launcherListView.count - 1) {
255284 if (clickedItem.angle > 12) {
@@ -273,7 +302,6 @@ Item {
273302 }
274303
275304 onCanceled: {
276− selectedItem.highlighted = false
277305 selectedItem = undefined;
278306 preDragging = false;
279307 postDragging = false;
@@ -287,7 +315,10 @@ Item {
287315 draggedIndex = -1;
288316 }
289317
290− selectedItem.highlighted = false
318+ if (!selectedItem) {
319+ return;
320+ }
321+
291322 selectedItem.dragging = false;
292323 selectedItem = undefined;
293324 preDragging = false;
@@ -299,6 +330,9 @@ Item {
299330 if (droppedIndex >= launcherListView.count - 2 && postDragging) {
300331 launcherListView.flick(0, -launcherListView.clickFlickSpeed);
301332 }
333+ if (droppedIndex == 0 && postDragging) {
334+ launcherListView.flick(0, launcherListView.clickFlickSpeed);
335+ }
302336 }
303337
304338 onPressAndHold: {
@@ -376,8 +410,12 @@ Item {
376410 }
377411
378412 if (newIndex >= 0 && newIndex < launcherListView.count) {
379− launcherListView.model.move(draggedIndex, newIndex)
380− draggedIndex = newIndex
413+ if (launcherListView.draggingTransitionRunning) {
414+ launcherListView.scheduledMoveTo = newIndex
415+ } else {
416+ launcherListView.model.move(draggedIndex, newIndex)
417+ draggedIndex = newIndex
418+ }
381419 }
382420 }
383421 }
@@ -414,8 +452,7 @@ Item {
414452 height: itemHeight
415453 width: itemWidth
416454 rotation: root.rotation
417− highlighted: true
418− itemOpacity: 0.8
455+ itemOpacity: 0.9
419456
420457 function flatten() {
421458 fakeDragItemAnimation.start();
@@ -439,6 +476,7 @@ Item {
439476 property var model
440477 property string appId
441478 contentWidth: quickListColumn.width
479+ foregroundStyle: QuicklistForegroundStyle {}
442480
443481 // FIXME: There's a bug in the Popover positioning that it covers the item in case it is rotated.
444482 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1204470
@@ -454,6 +492,7 @@ Item {
454492 height: childrenRect.height
455493
456494 Repeater {
495+ id: popoverRepeater
457496 model: popover.model
458497
459498 ListItems.Standard {
@@ -464,8 +503,11 @@ Item {
464503 if (!model.clickable) {
465504 return;
466505 }
467− LauncherModel.quickListActionInvoked(appId, index);
468506 PopupUtils.close(popover);
507+ // Unsetting model to prevent showing changing entries during fading out
508+ // that may happen because of triggering an action.
509+ LauncherModel.quickListActionInvoked(appId, index);
510+ popoverRepeater.model = undefined;
469511 }
470512 }
471513 }
Launcher/QuicklistForegroundStyle.qmlnew file+23
@@ -0,0 +1,23 @@
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 Ubuntu.Components.Themes.Ambiance 0.1
18+
19+PopoverForegroundStyle {
20+ bubbleColor: "#221e1c"
21+ bubbleOpacity: 0.9
22+ arrowSource: "graphics/quicklist_tooltip.png"
23+}
Launcher/graphics/quicklist_tooltip@30.pngnew file

Binary file not shown.

Panel/Indicators/NetworkIndicatorPage2.qmlnew file+82
@@ -0,0 +1,82 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Unity.Indicators 0.1 as Indicators
22+import Unity.Indicators.Network 0.1 as ICNetwork
23+import Ubuntu.Components 0.1
24+
25+PageStack {
26+ id: networkPage
27+ // Stops the search for a parent tree node.
28+ // We don't want it going up to the indicator tabs.
29+ // FIXME: need a better way of doing this.
30+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
31+ __propagated: null
32+
33+ property alias title: _mainPage.title
34+ property alias emptyText: pluginItem.emptyText
35+ property alias busName: pluginItem.busName
36+ property alias actionsObjectPath : pluginItem.actionsObjectPath
37+ property alias menuObjectPaths : pluginItem.menuObjectPaths
38+
39+ anchors.fill: parent
40+
41+ Page {
42+ id: _mainPage
43+
44+ Indicators.IndicatorPage {
45+ id: pluginItem
46+ anchors.fill: parent
47+ }
48+ }
49+
50+ Component {
51+ id: passwordPageComponent
52+
53+ ICNetwork.PasswordPage {
54+ agent: networkAgent
55+ }
56+ }
57+
58+ ICNetwork.NetworkAgent {
59+ id: networkAgent
60+
61+ onSecretRequested: {
62+ networkPage.push(passwordPageComponent, {"token": token});
63+ }
64+ }
65+
66+ function start()
67+ {
68+ push(_mainPage);
69+ pluginItem.start();
70+ }
71+
72+ function stop()
73+ {
74+ clear();
75+ pluginItem.stop();
76+ }
77+
78+ function reset()
79+ {
80+ pluginItem.reset();
81+ }
82+}
Panel/IndicatorsDataModel.qml+7−9
@@ -27,6 +27,12 @@ Indicators.IndicatorsModel {
2727 Component.onCompleted: load()
2828
2929 indicatorData : {
30+ 'indicator-location' : {
31+ 'title': 'Location',
32+ 'description' : '',
33+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
34+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
35+ },
3036 'indicator-bluetooth' : {
3137 'title': 'Bluetooth',
3238 'description' : '',
@@ -49,7 +55,7 @@ Indicators.IndicatorsModel {
4955 'title': 'Networks',
5056 'description' : '',
5157 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
52− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
58+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage2.qml'
5359 },
5460 'indicator-power' : {
5561 'title': 'Battery',
@@ -68,14 +74,6 @@ Indicators.IndicatorsModel {
6874 'description' : '',
6975 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
7076 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
71− },
72−
73− // OLD FORMAT INDICATORS
74− 'chewie-network' : {
75− 'title': 'Networks',
76− 'description' : '',
77− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorWidget.qml',
78− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage.qml'
7977 }
8078 }
8179}
Shell.qml+32−138
@@ -21,7 +21,6 @@ import Ubuntu.Components 0.1
2121import Ubuntu.Gestures 0.1
2222import Unity.Launcher 0.1
2323import LightDM 0.1 as LightDM
24−import AccountsService 0.1
2524import Powerd 0.1
2625import "Dash"
2726import "Greeter"
@@ -61,19 +60,6 @@ FocusScope {
6160 }
6261 }
6362
64− property bool showEdgeDemo: false
65− property bool showEdgeDemoInGreeter: showEdgeDemo //AccountsService.getUserProperty("lightdm", "demo-edges")
66−
67− function hideEdgeDemo() {
68− var user = LightDM.Users.data(greeter.currentIndex, LightDM.UserRoles.NameRole);
69− AccountsService.setUserProperty(user, "demo-edges", false);
70− shell.showEdgeDemo = false;
71− }
72− function hideEdgeDemoInGreeter() {
73− //AccountsService.setUserProperty("lightdm", "demo-edges", false);
74− shell.showEdgeDemoInGreeter = false;
75− }
76−
7763 property ListModel searchHistory: SearchHistoryModel {}
7864
7965 property var applicationManager: ApplicationManagerWrapper {}
@@ -185,10 +171,14 @@ FocusScope {
185171 // Whether the underlay is fully covered by opaque UI elements.
186172 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
187173
174+ readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
175+ || (sideStage.applications && sideStage.applications.count > 0))
176+
188177 // Whether the user should see the topmost application surface (if there's one at all).
189− property bool applicationSurfaceShouldBeSeen:
190− (mainStage.applications && mainStage.applications.count > 0)
191− && (!stages.fullyHidden && !mainStage.usingScreenshots)
178+ readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
179+ && !mainStage.usingScreenshots // but want sideStage animating over app surface
180+
181+
192182
193183 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
194184 // through the translucent parts of the shell surface.
@@ -215,7 +205,7 @@ FocusScope {
215205 available: !greeter.shown && !lockscreen.shown
216206 hides: [stages, launcher, panel.indicators]
217207 shown: disappearingAnimationProgress !== 1.0
218− enabled: disappearingAnimationProgress === 0.0 && !leftEdgeDemo.active && !topEdgeDemo.active && !finalEdgeDemo.active
208+ enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
219209 // FIXME: unfocus all applications when going back to the dash
220210 onEnabledChanged: {
221211 if (enabled) {
@@ -242,50 +232,6 @@ FocusScope {
242232 // greeterRevealer.animatedProgress are not animated
243233 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
244234 }
245−
246− DemoOverlay {
247− id: topEdgeDemo
248−
249− edge: "top"
250− title: i18n.tr("Top edge")
251− text: i18n.tr("Try swiping from the top edge to access the indicators")
252− anchors.fill: dash
253− visible: false
254− enabled: shell.showEdgeDemo
255− onSkip: shell.hideEdgeDemo()
256−
257− Connections {
258− target: greeter
259− onShownChanged: if (!greeter.shown) topEdgeDemo.visible = true
260− }
261−
262− Connections {
263− target: panel.indicators
264− onFullyOpenedChanged: if (panel.indicators.fullyOpened) topEdgeDemo.enabled = false
265− }
266− }
267−
268− DemoOverlay {
269− id: leftEdgeDemo
270−
271− edge: "left"
272− title: i18n.tr("Left edge")
273− text: i18n.tr("Swipe from the left to reveal the launcher for quick access to apps")
274− anchors.fill: dash
275− visible: false
276− enabled: shell.showEdgeDemo
277− onSkip: shell.hideEdgeDemo()
278−
279− Connections {
280− target: bottomEdgeDemo
281− onActiveChanged: if (!bottomEdgeDemo.active) leftEdgeDemo.visible = true
282− }
283−
284− Connections {
285− target: launcher
286− onProgressChanged: if (launcher.progress >= 1.0) leftEdgeDemo.enabled = false
287− }
288− }
289235 }
290236
291237 Item {
@@ -529,9 +475,6 @@ FocusScope {
529475
530476 onUnlocked: greeter.hide()
531477 onSelected: {
532− // Update edge demo hint
533− var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole)
534− shell.showEdgeDemo = AccountsService.getUserProperty(user, "demo-edges")
535478 // Update launcher items for new user
536479 LauncherModel.setUser(user);
537480 }
@@ -541,34 +484,6 @@ FocusScope {
541484 launcher.tease();
542485 }
543486 }
544−
545− DemoOverlay {
546− id: rightEdgeDemo
547−
548− edge: "right"
549− title: i18n.tr("Right edge")
550− text: i18n.tr("Try swiping from the right edge to unlock the phone")
551− anchors.fill: parent
552− visible: shell.showEdgeDemoInGreeter
553− onSkip: {
554− shell.hideEdgeDemoInGreeter()
555− shell.hideEdgeDemo()
556− }
557−
558− Connections {
559− target: greeter
560−
561− function hide() {
562− if (rightEdgeDemo.active) {
563− rightEdgeDemo.enabled = false
564− shell.hideEdgeDemoInGreeter()
565− }
566− }
567−
568− onUnlocked: hide()
569− onShownChanged: if (!greeter.shown) hide()
570− }
571− }
572487 }
573488
574489 InputFilterArea {
@@ -602,13 +517,22 @@ FocusScope {
602517 }
603518 }
604519
605− onPowerStateChange: {
606− if (state == 0) { // suspend
520+ onDisplayPowerStateChange: {
521+ // We ignore any display-off signals when the proximity sensor
522+ // is active. This usually indicates something like a phone call.
523+ if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
607524 powerConnection.setFocused(false);
608525 greeter.show();
609− } else if (state == 1) { // active
526+ } else if (status == Powerd.On) {
610527 powerConnection.setFocused(true);
611528 }
529+
530+ // No reason to chew demo CPU when user isn't watching
531+ if (status == Powerd.Off) {
532+ edgeDemo.paused = true;
533+ } else if (status == Powerd.On) {
534+ edgeDemo.paused = false;
535+ }
612536 }
613537 }
614538
@@ -623,7 +547,7 @@ FocusScope {
623547 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
624548 indicators {
625549 hides: [launcher]
626− available: !rightEdgeDemo.active && !leftEdgeDemo.active && !finalEdgeDemo.active
550+ available: edgeDemo.panelEnabled
627551 }
628552 fullscreenMode: shell.fullscreenMode
629553 searchVisible: !greeter.shown && !lockscreen.shown
@@ -632,24 +556,6 @@ FocusScope {
632556 anchors.fill: parent
633557 blockInput: panel.indicators.shown
634558 }
635−
636− DemoOverlay {
637− id: bottomEdgeDemo
638−
639− edge: "bottom"
640− title: i18n.tr("Close")
641− text: i18n.tr("Swipe up again to close the settings screen")
642− anchors.fill: panel.indicators
643− visible: false
644− enabled: shell.showEdgeDemo
645− onSkip: shell.hideEdgeDemo()
646−
647− Connections {
648− target: panel.indicators
649− onFullyOpenedChanged: if (panel.indicators.fullyOpened) bottomEdgeDemo.visible = true
650− onPartiallyOpenedChanged: if (!panel.indicators.partiallyOpened && !panel.indicators.fullyOpened) bottomEdgeDemo.enabled = false
651− }
652− }
653559 }
654560
655561 Hud {
@@ -658,7 +564,7 @@ FocusScope {
658564 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
659565 height: parent.height
660566
661− available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown && !topEdgeDemo.active
567+ available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown && edgeDemo.dashEnabled
662568 shown: false
663569 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
664570 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
@@ -716,7 +622,7 @@ FocusScope {
716622 anchors.bottom: parent.bottom
717623 width: parent.width
718624 dragAreaWidth: shell.edgeSize
719− available: (!greeter.shown || greeter.narrowMode) && !rightEdgeDemo.active && !topEdgeDemo.active && !bottomEdgeDemo.active && !finalEdgeDemo.active
625+ available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
720626 onDashItemSelected: {
721627 greeter.hide()
722628 // Animate if moving between application and dash
@@ -774,27 +680,6 @@ FocusScope {
774680 }
775681 }
776682
777− DemoOverlay {
778− id: finalEdgeDemo
779−
780− edge: "none"
781− title: i18n.tr("Well done")
782− text: i18n.tr("You have now mastered the edge gestures and can start using the phone")
783− skipText: i18n.tr("Finish")
784− anchors.fill: overlay
785− visible: false
786− enabled: shell.showEdgeDemo
787− onSkip: {
788− launcher.hide();
789− shell.hideEdgeDemo();
790− }
791−
792− Connections {
793− target: leftEdgeDemo
794− onActiveChanged: if (!leftEdgeDemo.active) finalEdgeDemo.visible = true
795− }
796− }
797−
798683 focus: true
799684
800685 InputFilterArea {
@@ -846,4 +731,13 @@ FocusScope {
846731 rotation: -45
847732 scale: Math.min(parent.width, parent.height) / width
848733 }
734+
735+ EdgeDemo {
736+ id: edgeDemo
737+ greeter: greeter
738+ launcher: launcher
739+ dash: dash
740+ indicators: panel.indicators
741+ overlay: overlay
742+ }
849743}
build+7−3
@@ -40,11 +40,14 @@ install_dependencies() {
4040 echo "Installing Unity 8 dependencies.."
4141 sudo apt-get install devscripts \
4242 equivs \
43+ gsettings-desktop-schemas \
4344 qmenumodel-qml \
44− qtdeclarative5-ubuntu-ui-toolkit-plugin \
4545 qtdeclarative5-dee-plugin \
46+ qtdeclarative5-gsettings1.0 \
47+ qtdeclarative5-ubuntu-ui-toolkit-plugin \
48+ unity-notifications-impl \
49+ qtdeclarative5-xmllistmodel-plugin \
4650 ubuntu-mobile-icons \
47− unity-notifications-impl-1 \
4851 unity-scope-home \
4952 unity-lens-applications \
5053 || exit 5
@@ -56,7 +59,8 @@ mk_build_deps() {
5659
5760if [ -f "/usr/bin/ninja" ] ; then
5861 GENERATOR="-G Ninja"
59− BUILD_COMMAND="ninja -j$NUM_JOBS"
62+ # Ninja does not need -j, it parallelizes automatically.
63+ BUILD_COMMAND="ninja"
6064else
6165 GENERATOR=
6266 BUILD_COMMAND="make -j$NUM_JOBS"
debian/changelog+171
@@ -1,3 +1,174 @@
1+unity8 (7.81.3+13.10.20130827.1-0ubuntu1) saucy; urgency=low
2+
3+ [ Michael Zanetti ]
4+ * unset model in quicklist before closing it.
5+
6+ [ Ubuntu daily release ]
7+ * Automatic snapshot from revision 256
8+
9+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 27 Aug 2013 10:49:17 +0000
10+
11+unity8 (7.81.3+13.10.20130827-0ubuntu1) saucy; urgency=low
12+
13+ [ Michael Zanetti ]
14+ * Theme the Quicklist Popover.
15+
16+ [ Albert Astals ]
17+ * Apply expandedIndex on delegate creation bug #1213033. (LP:
18+ #1213033)
19+
20+ [ Ubuntu daily release ]
21+ * Automatic snapshot from revision 254
22+
23+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 27 Aug 2013 02:10:43 +0000
24+
25+unity8 (7.81.3+13.10.20130826.5-0ubuntu1) saucy; urgency=low
26+
27+ [ Michael Zanetti ]
28+ * tweak launcher folding and visuals according to feedback from
29+ design: - increase foldingStartHeight - Introduces a
30+ foldingStopHeight. Folding only happens between the
31+ foldingStartHeight and the foldingStopHeight. - Only change
32+ brightness while folding - remove highlight of pressed icon -
33+ decrease launcher's width by half a grid unit.
34+
35+ [ Ubuntu daily release ]
36+ * Automatic snapshot from revision 251
37+
38+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 22:09:43 +0000
39+
40+unity8 (7.81.3+13.10.20130826.4-0ubuntu1) saucy; urgency=low
41+
42+ [ Michał Sawicz ]
43+ * Add module entry in HudClient's qmldir.
44+
45+ [ Jussi Pakkanen ]
46+ * Let Ninja parallelize itself.
47+
48+ [ Nick Dedekind ]
49+ * Abstraction of indicator menu item properties prior to move into
50+ common components library.
51+
52+ [ Christopher Lee ]
53+ * Added autopilot-tests for ephemeral, interactive and snap-decision
54+ notifications.
55+
56+ [ Mirco Müller ]
57+ * Added autopilot-tests for ephemeral, interactive and snap-decision
58+ notifications.
59+
60+ [ Michael Zanetti ]
61+ * only search visible children in findChild, add findInvisibleChild
62+ for others.
63+ * Added autopilot-tests for ephemeral, interactive and snap-decision
64+ notifications.
65+
66+ [ Thomi Richards ]
67+ * Added autopilot-tests for ephemeral, interactive and snap-decision
68+ notifications.
69+
70+ [ Ubuntu daily release ]
71+ * Automatic snapshot from revision 249
72+
73+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 18:34:44 +0000
74+
75+unity8 (7.81.3+13.10.20130826.2-0ubuntu1) saucy; urgency=low
76+
77+ [ Albert Astals ]
78+ * Don't include QtQML It includes LOTS of files we don't need.
79+
80+ [ Ubuntu daily release ]
81+ * Automatic snapshot from revision 243
82+
83+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 10:08:52 +0000
84+
85+unity8 (7.81.3+13.10.20130826.1-0ubuntu1) saucy; urgency=low
86+
87+ * Automatic snapshot from revision 241
88+
89+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 06:08:33 +0000
90+
91+unity8 (7.81.3+13.10.20130826-0ubuntu1) saucy; urgency=low
92+
93+ [ Michael Zanetti ]
94+ * delay move operations if the start dragging operation is running
95+ This prevents items to left in wrong places when transitions clash.
96+ * add empty setUser to allow compiling in jenkins again until the
97+ branch that matches unity-api is ready to land. .
98+ * add count and progress overlay information to real model too.
99+
100+ [ Pawel Stolowski ]
101+ * Changed the type of setActive argument from
102+ QVector<AbstractFilterOption *>::size_type to unsigned int.
103+
104+ [ Ubuntu daily release ]
105+ * Automatic snapshot from revision 240
106+
107+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 02:31:42 +0000
108+
109+unity8 (7.81.3+13.10.20130821.2-0ubuntu1) saucy; urgency=low
110+
111+ [ Michael Terry ]
112+ * Listen to display-power-changed rather than system-power-changed
113+ signals when showing the greeter; ignore such signals when the
114+ proximity sensor is active. (LP: #1214477)
115+
116+ [ Pawel Stolowski ]
117+ * Added a role for accessing progress-source property of categories.
118+
119+ [ Ubuntu daily release ]
120+ * Automatic snapshot from revision 233
121+
122+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 22:09:53 +0000
123+
124+unity8 (7.81.3+13.10.20130821.1-0ubuntu1) saucy; urgency=low
125+
126+ [ Michael Zanetti ]
127+ * allow testing at 11:13. Old code failed because the text actually
128+ says "11:13 AM".
129+ * add some checks if we actually clicked an item or in the spacing
130+ between them gets rid of some warnings printed by the launcher .
131+
132+ [ Michał Sawicz ]
133+ * Update runtime deps in the build script.
134+
135+ [ Nick Dedekind ]
136+ * Added/Updated legacy network indicator components to use with new
137+ indicator backend.
138+
139+ [ Albert Astals ]
140+ * Fix insertions/removals on the qlimitproxymodel . (LP: #1213959)
141+
142+ [ Ubuntu daily release ]
143+ * Automatic snapshot from revision 230
144+
145+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 15:04:59 +0000
146+
147+unity8 (7.81.3+13.10.20130821-0ubuntu1) saucy; urgency=low
148+
149+ [ Michael Zanetti ]
150+ * added support for count emblems and progress overlays on the
151+ launcher.
152+
153+ [ Pawel Stolowski ]
154+ * Add role for getting filter options model. Add method to activate
155+ option based on index or id.
156+
157+ [ Ted Gould ]
158+ * Mark indicators-client as providing an indicator-renderer.
159+
160+ [ Nick Dedekind ]
161+ * Added flatmenuproxymodel pass-through signals from qmenumodel.
162+ * Renamed indicator-messaging to indicator-messages.
163+
164+ [ Gerry Boland ]
165+ * Typo fix in FrequentlyUsedAppsModel.
166+
167+ [ Ubuntu daily release ]
168+ * Automatic snapshot from revision 224
169+
170+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 08:05:13 +0000
171+
1172unity8 (7.81.3+13.10.20130820.2-0ubuntu1) saucy; urgency=low
2173
3174 [ Michael Zanetti ]
debian/control+2−1
@@ -21,7 +21,7 @@ Build-Depends: cmake,
2121 libsigc++-2.0-dev,
2222 libunity-api-dev (>= 7.80.3),
2323 libunity-core-6.0-dev (<< 7.80),
24− libunity-dev (>= 7.0.3),
24+ libunity-dev (>= 7.0.12),
2525 libunity-scopes-json-def-phone,
2626 libupstart-dev,
2727 libusermetricsoutput1-dev,
@@ -62,6 +62,7 @@ Description: Indicators client test application
6262
6363Package: unity8
6464Architecture: any
65+Provides: indicator-renderer,
6566Depends: gsettings-desktop-schemas,
6667 qmenumodel-qml (>= 0.2.7),
6768 qtdeclarative5-dee-plugin,
debian/rules+5
@@ -16,6 +16,11 @@ override_dh_auto_clean:
1616 dh_auto_clean
1717 rm -rf builddir
1818
19+override_dh_auto_configure:
20+ # Debian defines CMAKE_INSTALL_LOCALSTATEDIR as /usr/var, which is wrong.
21+ # So until Debian bug 719148 is fixed, do it ourselves.
22+ dh_auto_configure -- -DCMAKE_INSTALL_LOCALSTATEDIR="/var"
23+
1924override_dh_install:
2025 # install autopilot tests
2126 cd tests/autopilot; \
main.cpp−1
@@ -18,7 +18,6 @@
1818 */
1919
2020// Qt
21−#include <QtQml>
2221#include <QtQuick/QQuickView>
2322#include <QtGui/QIcon>
2423#include <QtGui/QGuiApplication>
plugins/AccountsService/AccountsService.cpp+8−4
@@ -17,19 +17,23 @@
1717 */
1818
1919#include "AccountsService.h"
20−#include <QtDBus/QDBusConnection>
21−#include <QtDBus/QDBusMessage>
22−#include <QtDBus/QDBusVariant>
20+#include <QDBusConnection>
21+#include <QDBusConnectionInterface>
22+#include <QDBusMessage>
23+#include <QDBusVariant>
2324
2425AccountsService::AccountsService(QObject* parent)
2526 : QObject(parent),
2627 accounts_manager(NULL),
2728 users()
2829{
30+ auto connection = QDBusConnection::SM_BUSNAME();
31+ auto interface = connection.interface();
32+ interface->startService("org.freedesktop.Accounts");
2933 accounts_manager = new QDBusInterface("org.freedesktop.Accounts",
3034 "/org/freedesktop/Accounts",
3135 "org.freedesktop.Accounts",
32− QDBusConnection::SM_BUSNAME(), this);
36+ connection, this);
3337}
3438
3539QVariant AccountsService::getUserProperty(const QString &user, const QString &property)
plugins/AccountsService/AccountsService.h+7−9
@@ -19,10 +19,10 @@
1919#ifndef UNITY_ACCOUNTSSERVICE_H
2020#define UNITY_ACCOUNTSSERVICE_H
2121
22−#include <QtDBus/QDBusInterface>
23−#include <QtCore/QMap>
24−#include <QtCore/QObject>
25−#include <QtCore/QString>
22+#include <QDBusInterface>
23+#include <QMap>
24+#include <QObject>
25+#include <QString>
2626
2727class AccountsService: public QObject
2828{
@@ -31,14 +31,12 @@ class AccountsService: public QObject
3131public:
3232 explicit AccountsService(QObject *parent = 0);
3333
34−public Q_SLOTS:
35− QVariant getUserProperty(const QString &user, const QString &property);
36− void setUserProperty(const QString &user, const QString &property, const QVariant &value);
34+ Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &property);
35+ Q_INVOKABLE void setUserProperty(const QString &user, const QString &property, const QVariant &value);
3736
38−private Q_SLOTS:
37+private:
3938 QDBusInterface *getUserInterface(const QString &user);
4039
41−private:
4240 QDBusInterface *accounts_manager;
4341 QMap<QString, QDBusInterface *> users;
4442};
plugins/AccountsService/CMakeLists.txt+1−3
@@ -12,9 +12,7 @@ qt5_use_modules(AccountsService-qml DBus Qml)
1212export_qmlfiles(AccountsService AccountsService)
1313export_qmlplugin(AccountsService 0.1 AccountsService TARGETS AccountsService-qml)
1414
15−# Debian defines CMAKE_INSTALL_LOCALSTATEDIR as /usr/var, which is wrong.
16−# So until Debian bug 719148 is fixed, hardcode it.
17−set(POLKIT_LIB_DIR "/var/lib/polkit-1")
15+set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1")
1816set(POLKIT_DATA_DIR "${CMAKE_INSTALL_PREFIX}/share/polkit-1")
1917set(DBUS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/dbus-1/interfaces")
2018set(ACCOUNTS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/accountsservice/interfaces")
plugins/AccountsService/com.canonical.unity.AccountsService.xml+2−2
@@ -16,8 +16,8 @@
1616 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
1717 </property>
1818
19− <property name="launcher-items" type="as" access="readwrite">
20− <annotation name="org.freedesktop.Accounts.DefaultValue" value="[]"/>
19+ <property name="launcher-items" type="aa{sv}" access="readwrite">
20+ <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
2121 </property>
2222
2323 </interface>
plugins/AccountsService/plugin.cpp+3−1
@@ -19,7 +19,8 @@
1919#include "plugin.h"
2020#include "AccountsService.h"
2121
22−#include <QtQml/QtQml>
22+#include <QDBusMetaType>
23+#include <QtQml>
2324
2425static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
2526{
@@ -31,5 +32,6 @@ static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3132void AccountsServicePlugin::registerTypes(const char *uri)
3233{
3334 Q_ASSERT(uri == QLatin1String("AccountsService"));
35+ qDBusRegisterMetaType<QList<QVariantMap>>();
3436 qmlRegisterSingletonType<AccountsService>(uri, 0, 1, "AccountsService", service_provider);
3537}
plugins/AccountsService/plugin.h+2−2
@@ -19,8 +19,8 @@
1919#ifndef ACCOUNTSSERVICE_PLUGIN_H
2020#define ACCOUNTSSERVICE_PLUGIN_H
2121
22−#include <QtQml/QQmlEngine>
23−#include <QtQml/QQmlExtensionPlugin>
22+#include <QQmlEngine>
23+#include <QQmlExtensionPlugin>
2424
2525class AccountsServicePlugin : public QQmlExtensionPlugin
2626{
plugins/HudClient/qmldir+1
@@ -1,2 +1,3 @@
1+module HudClient
12plugin HudClientQml
23typeinfo plugin.qmltypes
plugins/LightDM/plugin.cpp+1−1
@@ -25,7 +25,7 @@
2525#include <QLightDM/UsersModel>
2626
2727#include <QtCore/QAbstractItemModel>
28−#include <QtQml>
28+#include <QtQml/qqml.h>
2929
3030static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3131{
plugins/Powerd/Powerd.cpp+2−2
@@ -30,7 +30,7 @@ Powerd::Powerd(QObject* parent)
3030 powerd->connection().connect("com.canonical.powerd",
3131 "/com/canonical/powerd",
3232 "com.canonical.powerd",
33− "SysPowerStateChange",
33+ "DisplayPowerStateChange",
3434 this,
35− SIGNAL(powerStateChange(int)));
35+ SIGNAL(displayPowerStateChange(int, unsigned int)));
3636}
plugins/Powerd/Powerd.h+15−1
@@ -26,12 +26,26 @@
2626class Powerd: public QObject
2727{
2828 Q_OBJECT
29+ Q_ENUMS(Status)
30+ Q_FLAGS(DisplayFlag DisplayFlags)
2931
3032public:
33+ enum DisplayFlag {
34+ UseProximity = 1, // Use proximity sensor to override screen state
35+ DisableAutoBrightness = 2, // Force autobrightness to be disabled
36+ Bright = 4, // Request the screen to stay bright
37+ };
38+ Q_DECLARE_FLAGS(DisplayFlags, DisplayFlag)
39+
40+ enum Status {
41+ Off,
42+ On,
43+ };
44+
3145 explicit Powerd(QObject *parent = 0);
3246
3347Q_SIGNALS:
34− void powerStateChange(int state);
48+ void displayPowerStateChange(int status, unsigned int flags);
3549
3650private:
3751 QDBusInterface *powerd;
plugins/Powerd/plugin.cpp+1−1
@@ -20,7 +20,7 @@
2020#include "plugin.h"
2121#include "Powerd.h"
2222
23−#include <QtQml/QtQml>
23+#include <QtQml/qqml.h>
2424
2525static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
2626{
plugins/Ubuntu/DownloadDaemonListener/plugin.cpp+1−1
@@ -22,7 +22,7 @@
2222#include "plugin.h"
2323#include "DownloadTracker.h"
2424
25−#include <QtQml>
25+#include <QtQml/qqml.h>
2626#include <QtQml/QQmlContext>
2727
2828
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -12,6 +12,7 @@ find_package(Qt5Core REQUIRED)
1212find_package(Qt5Quick REQUIRED)
1313
1414add_subdirectory(qml)
15+add_subdirectory(Network)
1516
1617include_directories(
1718 ${CMAKE_CURRENT_SOURCE_DIR}
plugins/Unity/Indicators/Network/CMakeLists.txtnew file+45
@@ -0,0 +1,45 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+project(IndicatorsNetworkQml)
5+
6+pkg_check_modules(GLIB glib-2.0>=2.32)
7+pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
8+pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
9+
10+if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
11+ set(INDICATORS_NETWORK_SRC
12+ networkagent.cpp
13+ plugin.cpp
14+ secret-agent.c
15+ )
16+
17+ add_library(IndicatorsNetworkQml MODULE
18+ ${INDICATORS_NETWORK_SRC}
19+ )
20+
21+ qt5_use_modules(IndicatorsNetworkQml Core Qml Widgets)
22+
23+ set_target_properties(IndicatorsNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
24+
25+ include_directories(
26+ ${CMAKE_CURRENT_SOURCE_DIR}
27+ ${GLIB_INCLUDE_DIRS}
28+ ${LIBNM_GLIB_INCLUDE_DIRS}
29+ ${LIBNM_UTIL_INCLUDE_DIRS}
30+ )
31+
32+ target_link_libraries(IndicatorsNetworkQml
33+ ${GLIB_LIBRARIES}
34+ ${LIBNM_GLIB_LIBRARIES}
35+ ${LIBNM_UTIL_LIBRARIES}
36+ )
37+
38+ # export the qmltypes and plugin files
39+ export_qmlplugin(Unity.Indicators.Network 0.1 Unity/Indicators/Network TARGETS IndicatorsNetworkQml)
40+
41+else()
42+ message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
43+endif()
44+
45+add_subdirectory(qml)
plugins/Unity/Indicators/Network/networkagent.cppnew file+146
@@ -0,0 +1,146 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#include "networkagent.h"
22+
23+#include <QVariant>
24+#include <QApplication>
25+#include <QDebug>
26+
27+class NetworkAgentToken
28+{
29+public:
30+ NMConnection *connection;
31+ NMSettingWirelessSecurity *wisec;
32+ guint id;
33+ QByteArray keyMgmt;
34+
35+ NetworkAgentToken(NMConnection *connection,
36+ NMSettingWirelessSecurity *wisec,
37+ guint id,
38+ QByteArray keyMgmt)
39+ : connection(connection),
40+ wisec(wisec),
41+ id(id),
42+ keyMgmt(keyMgmt)
43+ {
44+ if (connection) {
45+ g_object_ref(connection);
46+ } else {
47+ qWarning() << "invalid connection object";
48+ }
49+ }
50+
51+ ~NetworkAgentToken()
52+ {
53+ if (connection) {
54+ g_object_unref(connection);
55+ }
56+ }
57+};
58+
59+NetworkAgent::NetworkAgent(QObject *parent)
60+ : QObject(parent)
61+{
62+ m_agent = unity_settings_secret_agent_new();
63+ g_signal_connect(G_OBJECT(m_agent),
64+ UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
65+ G_CALLBACK(onSecretRequested),
66+ this);
67+
68+ g_signal_connect(G_OBJECT(m_agent),
69+ UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
70+ G_CALLBACK(onSecretRequestCancelled),
71+ this);
72+}
73+
74+NetworkAgent::~NetworkAgent()
75+{
76+ nm_secret_agent_unregister(NM_SECRET_AGENT(m_agent));
77+ g_object_unref(m_agent);
78+}
79+
80+void NetworkAgent::authenticate(const QVariant &token, const QString &key)
81+{
82+ NetworkAgentToken *pToken = (NetworkAgentToken *) token.value<void *>();
83+ if (pToken == NULL) {
84+ return;
85+ }
86+
87+ if ((pToken->keyMgmt == "wpa-none") || (pToken->keyMgmt == "wpa-psk")) {
88+ g_object_set(G_OBJECT(pToken->wisec),
89+ NM_SETTING_WIRELESS_SECURITY_PSK, qPrintable(key),
90+ NULL);
91+ } else if (pToken->keyMgmt == "none") {
92+ g_object_set(G_OBJECT(pToken->wisec),
93+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, qPrintable(key),
94+ NULL);
95+ }
96+
97+ GHashTable *settings = nm_connection_to_hash(pToken->connection,
98+ NM_SETTING_HASH_FLAG_ALL);
99+
100+ unity_settings_secret_agent_provide_secret(m_agent,
101+ pToken->id, settings);
102+ g_hash_table_unref(settings);
103+ delete pToken;
104+}
105+
106+void NetworkAgent::cancel(const QVariant &token)
107+{
108+ NetworkAgentToken *pToken = (NetworkAgentToken *) token.value<void *>();
109+ if (pToken != NULL) {
110+ unity_settings_secret_agent_cancel_request(m_agent, pToken->id);
111+ delete pToken;
112+ }
113+}
114+
115+void NetworkAgent::onSecretRequested(UnitySettingsSecretAgent* /*agent*/,
116+ guint id,
117+ NMConnection* connection,
118+ const char* /*setting_name*/,
119+ const char** /*hints*/,
120+ NMSecretAgentGetSecretsFlags /*flags*/,
121+ NetworkAgent* self)
122+{
123+ NetworkAgentToken *token = new NetworkAgentToken(connection,
124+ NULL,
125+ id,
126+ NULL);
127+
128+ token->wisec = nm_connection_get_setting_wireless_security(connection);
129+ if (token->wisec) {
130+ token->keyMgmt = nm_setting_wireless_security_get_key_mgmt(token->wisec);
131+ }
132+
133+ QVariant varToken = qVariantFromValue((void *) token);
134+ Q_EMIT self->secretRequested(varToken);
135+}
136+
137+void NetworkAgent::onSecretRequestCancelled(UnitySettingsSecretAgent* /*agent*/,
138+ guint /*id*/,
139+ NMConnection* /*connection*/,
140+ const char* /*setting_name*/,
141+ const char** /*hints*/,
142+ NMSecretAgentGetSecretsFlags /*flags*/,
143+ NetworkAgent* /*self*/)
144+{
145+ //Do nothing for now
146+}
plugins/Unity/Indicators/Network/networkagent.hnew file+65
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#ifndef NETWORKAGENT_H
22+#define NETWORKAGENT_H
23+
24+#include "secret-agent.h"
25+
26+#include <QObject>
27+
28+class NetworkAgentToken;
29+
30+class NetworkAgent : public QObject
31+{
32+ Q_OBJECT
33+public:
34+ NetworkAgent(QObject *parent=0);
35+ ~NetworkAgent();
36+
37+ Q_INVOKABLE void authenticate(const QVariant &token, const QString &key);
38+ Q_INVOKABLE void cancel(const QVariant &token);
39+
40+Q_SIGNALS:
41+ void secretRequested(const QVariant &token);
42+ void secretRequestCancelled();
43+
44+private:
45+ UnitySettingsSecretAgent *m_agent;
46+
47+ static void onSecretRequested(UnitySettingsSecretAgent *agent,
48+ guint id,
49+ NMConnection *connection,
50+ const char *setting_name,
51+ const char **hints,
52+ NMSecretAgentGetSecretsFlags flags,
53+ NetworkAgent *self);
54+
55+ static void onSecretRequestCancelled(UnitySettingsSecretAgent *agent,
56+ guint id,
57+ NMConnection *connection,
58+ const char *setting_name,
59+ const char **hints,
60+ NMSecretAgentGetSecretsFlags flags,
61+ NetworkAgent *self);
62+
63+};
64+
65+#endif
plugins/Unity/Indicators/Network/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2012 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: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+// Qt
20+#include <QtQml/qqml.h>
21+
22+// self
23+#include "plugin.h"
24+
25+// local
26+#include "networkagent.h"
27+
28+void IndicatorsNetworkPlugin::registerTypes(const char *uri)
29+{
30+ qmlRegisterType<NetworkAgent>(uri, 0, 1, "NetworkAgent");
31+}
plugins/Unity/Indicators/Network/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2012 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: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#ifndef INDICATORS_NETWORK_PLUGIN_H
20+#define INDICATORS_NETWORK_PLUGIN_H
21+
22+#include <QtQml/QQmlExtensionPlugin>
23+
24+class IndicatorsNetworkPlugin : public QQmlExtensionPlugin
25+{
26+ Q_OBJECT
27+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28+public:
29+ void registerTypes(const char *uri);
30+};
31+
32+#endif // INDICATORS_NETWORK_PLUGIN_H
plugins/Unity/Indicators/Network/qml/AccessPoint.qmlnew file+72
@@ -0,0 +1,72 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+import Ubuntu.Components.ListItems 0.1 as ListItem
23+import Unity.Indicators 0.1 as Indicators
24+
25+Indicators.FramedMenuItem {
26+ id: accessPoint
27+
28+ property bool checked: false
29+ property bool secure: false
30+ property bool adHoc: false
31+ property int signalStrength: 0
32+
33+ signal activate()
34+
35+ onCheckedChanged: {
36+ // Can't rely on binding. Checked is assigned on click.
37+ checkBoxActive.checked = checked;
38+ }
39+
40+ icon: {
41+ var imageName = "nm-signal-100"
42+
43+ if (adHoc) {
44+ imageName = "nm-adhoc";
45+ } else if (signalStrength == 0) {
46+ imageName = "nm-signal-00";
47+ } else if (signalStrength <= 25) {
48+ imageName = "nm-signal-25";
49+ } else if (signalStrength <= 50) {
50+ imageName = "nm-signal-50";
51+ } else if (signalStrength <= 75) {
52+ imageName = "nm-signal-75";
53+ }
54+
55+ if (secure) {
56+ imageName += "-secure";
57+ }
58+ return "image://gicon/" + imageName;
59+ }
60+
61+ iconFrame: false
62+ control: CheckBox {
63+ id: checkBoxActive
64+ height: units.gu(4)
65+ width: units.gu(4)
66+ anchors.centerIn: parent
67+
68+ onClicked: {
69+ accessPoint.activate();
70+ }
71+ }
72+}
plugins/Unity/Indicators/Network/qml/CMakeLists.txtnew file+5
@@ -0,0 +1,5 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+# export the qmldir, *.qml files.
5+export_qmlfiles(Unity.Indicators.Network Unity/Indicators/Network)
plugins/Unity/Indicators/Network/qml/PasswordPage.qmlnew file+88
@@ -0,0 +1,88 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+import Ubuntu.Components.ListItems 0.1 as ListItem
23+import Unity.Indicators 0.1 as Indicators
24+
25+Page {
26+ id: pagePassword
27+
28+ property variant agent
29+ property variant token
30+
31+ anchors.fill: parent
32+ title: "Network Authentication"
33+
34+ Column {
35+ anchors {
36+ left: parent.left
37+ right: parent.right
38+ }
39+
40+ Indicators.SectionMenuItem {
41+ text: "Authentication"
42+ }
43+
44+ Indicators.FramedMenuItem {
45+ implicitHeight: password.height + units.gu(1)
46+
47+ PasswordTextField {
48+ id: password
49+ anchors {
50+ left: parent.left
51+ right: parent.right
52+ margins: units.gu(3)
53+ verticalCenter: parent.verticalCenter
54+ }
55+ }
56+ }
57+
58+ Indicators.FramedMenuItem {
59+ Row {
60+ anchors {
61+ right: parent.right
62+ verticalCenter: parent.verticalCenter
63+ margins: units.gu(3)
64+ }
65+
66+ spacing: units.gu(1)
67+
68+ Button {
69+ text: "Cancel"
70+ width: units.gu(10)
71+ onClicked: {
72+ agent.cancel(token);
73+ pageStack.pop();
74+ }
75+ }
76+
77+ Button {
78+ text: "OK"
79+ width: units.gu(10)
80+ onClicked: {
81+ agent.authenticate(token, password.text);
82+ pageStack.pop();
83+ }
84+ }
85+ }
86+ }
87+ }
88+}
plugins/Unity/Indicators/Network/qml/PasswordTextField.qmlnew file+68
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+
23+Item {
24+ id: textMenu
25+
26+ property alias text: textField.text
27+
28+ height: contentColumn.height
29+
30+ Column {
31+ id: contentColumn
32+ spacing: units.gu(0.5)
33+ anchors {
34+ left: parent.left
35+ right: parent.right
36+ }
37+
38+ TextField {
39+ id: textField
40+
41+ anchors {
42+ left: parent.left
43+ right: parent.right
44+ }
45+
46+ placeholderText: "Password"
47+ echoMode: showPassword.checked ? TextInput.Normal : TextInput.Password
48+ }
49+
50+ Row {
51+ anchors {
52+ left: parent.left
53+ right: parent.right
54+ }
55+
56+ spacing: units.gu(1)
57+
58+ CheckBox {
59+ id: showPassword
60+ }
61+
62+ Label {
63+ text: "Show password"
64+ anchors.verticalCenter: showPassword.verticalCenter
65+ }
66+ }
67+ }
68+}
plugins/Unity/Indicators/Network/qml/WifiSection.qmlnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+import Unity.IndicatorsLegacy 0.1 as Indicators
23+
24+Indicators.SectionMenuItem {
25+ id: wifiSectionMenu
26+
27+ Indicators.MenuAction {
28+ id: busyAction
29+ actionGroup: wifiSectionMenu.actionGroup
30+ action: menu ? menu.extra.canonical_busy_action : ""
31+ }
32+
33+ busy: busyAction.valid ? busyAction.state : false
34+}
plugins/Unity/Indicators/Network/qml/qmldirnew file+8
@@ -0,0 +1,8 @@
1+module Unity.Indicators.Network
2+plugin IndicatorsNetworkQml
3+typeinfo plugin.qmltypes
4+
5+NetworkPage 0.1 NetworkPage.qml
6+AccessPoint 0.1 AccessPoint.qml
7+PasswordPage 0.1 PasswordPage.qml
8+PasswordTextField 0.1 PasswordTextField.qml
plugins/Unity/Indicators/Network/secret-agent.cnew file+382
@@ -0,0 +1,382 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+
22+#include <glib.h>
23+#include <glib-object.h>
24+#include <nm-secret-agent.h>
25+#include "secret-agent.h"
26+
27+#define UNITY_SETTINGS_TYPE_SECRET_AGENT (unity_settings_secret_agent_get_type ())
28+#define UNITY_SETTINGS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgent))
29+#define UNITY_SETTINGS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
30+#define UNITY_SETTINGS_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT))
31+#define UNITY_SETTINGS_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT))
32+#define UNITY_SETTINGS_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
33+#define UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentPrivate))
34+
35+#define AGENT_ID "com.canonical.settings.network.nm-agent"
36+
37+static gpointer unity_settings_secret_agent_parent_class = NULL;
38+
39+typedef struct _UnitySettingsSecretAgentPrivate UnitySettingsSecretAgentPrivate;
40+
41+struct _UnitySettingsSecretAgentPrivate {
42+ GQueue *requests;
43+};
44+
45+typedef struct _SecretRequest {
46+ gint id;
47+ NMSecretAgent *agent;
48+ NMConnection *connection;
49+ const char *connection_path;
50+ const char *setting_name;
51+ const char **hints;
52+ NMSecretAgentGetSecretsFlags flags;
53+ NMSecretAgentGetSecretsFunc callback;
54+ gpointer callback_data;
55+} SecretRequest;
56+
57+GType unity_settings_secret_agent_get_type (void) G_GNUC_CONST;
58+enum {
59+ UNITY_SETTINGS_SECRET_AGENT_DUMMY_PROPERTY
60+};
61+
62+enum {
63+ SECRET_REQUESTED,
64+ REQUEST_CANCELLED,
65+ LAST_SIGNAL
66+};
67+
68+static guint signals[LAST_SIGNAL] = { 0 };
69+
70+UnitySettingsSecretAgent* unity_settings_secret_agent_new (void);
71+UnitySettingsSecretAgent* unity_settings_secret_agent_construct (GType object_type);
72+
73+int
74+secret_request_find (SecretRequest *req,
75+ guint *id)
76+{
77+ if (req->id > *id)
78+ return -1;
79+
80+ if (req->id < *id)
81+ return 1;
82+
83+ return 0;
84+}
85+
86+void
87+unity_settings_secret_agent_provide_secret (UnitySettingsSecretAgent *agent,
88+ guint request,
89+ GHashTable *secrets)
90+{
91+ GList *iter;
92+ SecretRequest *req;
93+ UnitySettingsSecretAgentPrivate *priv = agent->priv;
94+
95+ iter = g_queue_find_custom (priv->requests,
96+ &request,
97+ (GCompareFunc)secret_request_find);
98+
99+ if (iter == NULL || iter->data == NULL)
100+ {
101+ g_warning ("Secret request with id <%d> was not found", (int)request);
102+ return;
103+ }
104+
105+ req = iter->data;
106+
107+ req->callback (NM_SECRET_AGENT (agent),
108+ req->connection,
109+ secrets,
110+ NULL,
111+ req->callback_data);
112+
113+ g_queue_remove_all (priv->requests, req);
114+ g_free (req);
115+ return;
116+}
117+
118+void
119+free_request (SecretRequest *req)
120+{
121+ g_object_unref (req->connection);
122+ g_free (req);
123+}
124+
125+void
126+unity_settings_secret_agent_cancel_request (UnitySettingsSecretAgent *agent,
127+ guint request)
128+{
129+ GList *iter;
130+ SecretRequest *req;
131+ UnitySettingsSecretAgentPrivate *priv = agent->priv;
132+ GError *error;
133+
134+ iter = g_queue_find_custom (priv->requests,
135+ &request,
136+ (GCompareFunc)secret_request_find);
137+
138+ if (iter == NULL || iter->data == NULL)
139+ {
140+ g_warning ("Secret request with id <%d> was not found", (int)request);
141+ return;
142+ }
143+
144+ req = iter->data;
145+ error = g_error_new (NM_SECRET_AGENT_ERROR,
146+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
147+ "This secret request was canceled by the user.");
148+
149+ req->callback (NM_SECRET_AGENT (agent),
150+ req->connection,
151+ NULL,
152+ error,
153+ req->callback_data);
154+
155+ g_queue_remove_all (priv->requests, req);
156+ free_request (req);
157+ return;
158+}
159+
160+static void
161+delete_secrets (NMSecretAgent *agent,
162+ NMConnection *connection,
163+ const char *connection_path,
164+ NMSecretAgentDeleteSecretsFunc callback,
165+ gpointer callback_data)
166+{
167+ g_debug ("delete secrets");
168+}
169+
170+/* If it returns G_MAXUINT it's considered an error */
171+static guint
172+find_available_id (UnitySettingsSecretAgentPrivate *priv)
173+{
174+ guint i = 0;
175+ guint candidate = 0;
176+
177+ if (g_queue_get_length (priv->requests) == G_MAXUINT)
178+ return G_MAXUINT;
179+
180+ while (i < g_queue_get_length (priv->requests))
181+ {
182+ SecretRequest *req = (SecretRequest*)g_queue_peek_nth (priv->requests, i);
183+
184+ if (req->id == candidate)
185+ {
186+ candidate++;
187+ i = 0;
188+ }
189+ else
190+ {
191+ i++;
192+ }
193+ }
194+
195+ return i;
196+}
197+
198+static void
199+get_secrets (NMSecretAgent *agent,
200+ NMConnection *connection,
201+ const char *connection_path,
202+ const char *setting_name,
203+ const char **hints,
204+ NMSecretAgentGetSecretsFlags flags,
205+ NMSecretAgentGetSecretsFunc callback,
206+ gpointer callback_data)
207+{
208+ guint id;
209+ UnitySettingsSecretAgentPrivate *priv = UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE (agent);
210+ SecretRequest *req = NULL;
211+
212+ if (flags == NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE)
213+ {
214+ GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
215+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
216+ "No password found for this connection.");
217+ callback (agent, connection, NULL, error, callback_data);
218+ g_error_free (error);
219+ return;
220+ }
221+
222+ id = find_available_id (priv);
223+ if (id == G_MAXUINT)
224+ {
225+ GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
226+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
227+ "Reached maximum number of requests.");
228+ callback (agent, connection, NULL, error, callback_data);
229+ g_error_free (error);
230+ return;
231+ }
232+
233+ /* Adding a request */
234+ req = (SecretRequest*) g_malloc0 (sizeof (SecretRequest));
235+ *req = ((SecretRequest)
236+ { id,
237+ agent,
238+ connection,
239+ connection_path,
240+ setting_name,
241+ hints,
242+ flags,
243+ callback,
244+ callback_data });
245+
246+ g_object_ref (connection);
247+
248+ g_queue_push_tail (priv->requests, req);
249+
250+ g_signal_emit_by_name (agent,
251+ UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
252+ id,
253+ connection,
254+ setting_name,
255+ hints,
256+ flags);
257+}
258+
259+static void
260+save_secrets (NMSecretAgent *agent,
261+ NMConnection *connection,
262+ const char *connection_path,
263+ NMSecretAgentSaveSecretsFunc callback,
264+ gpointer callback_data)
265+{
266+ g_debug ("save secrets");
267+}
268+
269+static void
270+cancel_get_secrets (NMSecretAgent *agent,
271+ const char *connection_path,
272+ const char *setting_name)
273+{
274+ g_debug ("cancel get secrets");
275+}
276+
277+UnitySettingsSecretAgent*
278+unity_settings_secret_agent_construct (GType object_type)
279+{
280+ UnitySettingsSecretAgent * self = NULL;
281+ self = (UnitySettingsSecretAgent*) g_object_new (object_type,
282+ NM_SECRET_AGENT_IDENTIFIER, AGENT_ID,
283+ NULL);
284+ return self;
285+}
286+
287+
288+UnitySettingsSecretAgent*
289+unity_settings_secret_agent_new (void)
290+{
291+ return unity_settings_secret_agent_construct (UNITY_SETTINGS_TYPE_SECRET_AGENT);
292+}
293+
294+static void
295+destroy_pending_request (gpointer data)
296+{
297+ SecretRequest* req = (SecretRequest*)data;
298+ /* Reporting the cancellation of all pending requests */
299+ g_signal_emit_by_name (req->agent,
300+ UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
301+ req->id);
302+
303+ free_request (req);
304+}
305+
306+static void
307+unity_settings_secret_agent_finalize (GObject *agent)
308+{
309+ UnitySettingsSecretAgentPrivate *priv = UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE (agent);
310+
311+ g_queue_free_full (priv->requests, destroy_pending_request);
312+}
313+
314+static void
315+unity_settings_secret_agent_class_init (UnitySettingsSecretAgentClass *klass)
316+{
317+ unity_settings_secret_agent_parent_class = g_type_class_peek_parent (klass);
318+ NMSecretAgentClass *parent_class = NM_SECRET_AGENT_CLASS (klass);
319+ parent_class->get_secrets = get_secrets;
320+ parent_class->save_secrets = save_secrets;
321+ parent_class->delete_secrets = delete_secrets;
322+ parent_class->cancel_get_secrets = cancel_get_secrets;
323+
324+ g_type_class_add_private (klass, sizeof(UnitySettingsSecretAgentPrivate));
325+ G_OBJECT_CLASS (klass)->finalize = unity_settings_secret_agent_finalize;
326+
327+
328+ signals[SECRET_REQUESTED] = g_signal_new (UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
329+ G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
330+ G_SIGNAL_RUN_FIRST,
331+ G_STRUCT_OFFSET (UnitySettingsSecretAgentClass, secret_requested),
332+ NULL, NULL, NULL,
333+ G_TYPE_NONE, 5,
334+ G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT);
335+
336+ signals[REQUEST_CANCELLED] = g_signal_new (UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
337+ G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
338+ G_SIGNAL_RUN_FIRST,
339+ G_STRUCT_OFFSET (UnitySettingsSecretAgentClass, request_cancelled),
340+ NULL, NULL, NULL,
341+ G_TYPE_NONE, 1,
342+ G_TYPE_UINT);
343+}
344+
345+
346+static void
347+unity_settings_secret_agent_instance_init (UnitySettingsSecretAgent *self)
348+{
349+ self->priv = UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE (self);
350+ self->priv->requests = g_queue_new ();
351+}
352+
353+GType
354+unity_settings_secret_agent_get_type (void)
355+{
356+ static volatile gsize unity_settings_secret_agent_type_id__volatile = 0;
357+ if (g_once_init_enter (&unity_settings_secret_agent_type_id__volatile))
358+ {
359+ static const GTypeInfo g_define_type_info =
360+ {
361+ sizeof (UnitySettingsSecretAgentClass),
362+ (GBaseInitFunc) NULL,
363+ (GBaseFinalizeFunc) NULL,
364+ (GClassInitFunc) unity_settings_secret_agent_class_init,
365+ (GClassFinalizeFunc) NULL,
366+ NULL,
367+ sizeof (UnitySettingsSecretAgent),
368+ 0,
369+ (GInstanceInitFunc) unity_settings_secret_agent_instance_init,
370+ NULL
371+ };
372+ GType unity_settings_secret_agent_type_id;
373+ unity_settings_secret_agent_type_id = g_type_register_static (NM_TYPE_SECRET_AGENT,
374+ "UnitySettingsSecretAgent",
375+ &g_define_type_info,
376+ 0);
377+ g_once_init_leave (&unity_settings_secret_agent_type_id__volatile,
378+ unity_settings_secret_agent_type_id);
379+ }
380+
381+ return unity_settings_secret_agent_type_id__volatile;
382+}
plugins/Unity/Indicators/Network/secret-agent.hnew file+102
@@ -0,0 +1,102 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#ifndef __SECRET_AGENT_H__
22+#define __SECRET_AGENT_H__
23+
24+#include <glib.h>
25+#include <glib-object.h>
26+#include <nm-secret-agent.h>
27+
28+/*
29+ * This class is a basic implementation of the NetworkManager SecretAgent base class.
30+ *
31+ * The purpose of this class is to handle credential requests from the network,
32+ * for example, from a WiFi hotspot or a VPN network.
33+ *
34+ * It queues requests objects (SecretRequest) on a GQueue in the private struct
35+ * of the class. And notifies the consumer of the class through the "secret-request"
36+ * and the "request-cancelled" signals with the following callback prototypes:
37+ *
38+ * void (*secret_requested) (UnitySettingsSecretAgent *self,
39+ * guint id,
40+ * NMConnection *connection,
41+ * const char *setting_name,
42+ * const char **hints,
43+ * NMSecretAgentGetSecretsFlags flags,
44+ * gpointer user_data);
45+ *
46+ * void (*request_cancelled) (UnitySettingsSecretAgent *self,
47+ * guint id,
48+ * gpointer user_data);
49+ *
50+ */
51+
52+
53+G_BEGIN_DECLS
54+
55+
56+#define UNITY_SETTINGS_TYPE_SECRET_AGENT (unity_settings_secret_agent_get_type ())
57+#define UNITY_SETTINGS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgent))
58+#define UNITY_SETTINGS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
59+#define UNITY_SETTINGS_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT))
60+#define UNITY_SETTINGS_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT))
61+#define UNITY_SETTINGS_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
62+
63+#define UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED "secret-requested"
64+#define UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED "request-cancelled"
65+
66+typedef struct _UnitySettingsSecretAgent UnitySettingsSecretAgent;
67+typedef struct _UnitySettingsSecretAgentClass UnitySettingsSecretAgentClass;
68+typedef struct _UnitySettingsSecretAgentPrivate UnitySettingsSecretAgentPrivate;
69+
70+struct _UnitySettingsSecretAgent {
71+ NMSecretAgent parent_instance;
72+ UnitySettingsSecretAgentPrivate * priv;
73+};
74+
75+struct _UnitySettingsSecretAgentClass {
76+ NMSecretAgentClass parent_class;
77+
78+ void (*secret_requested) (UnitySettingsSecretAgent *self,
79+ guint id,
80+ NMConnection *connection,
81+ const char *setting_name,
82+ const char **hints,
83+ NMSecretAgentGetSecretsFlags flags);
84+
85+ void (*request_cancelled) (UnitySettingsSecretAgent *self,
86+ guint id);
87+};
88+
89+
90+GType unity_settings_secret_agent_get_type (void) G_GNUC_CONST;
91+UnitySettingsSecretAgent* unity_settings_secret_agent_new (void);
92+UnitySettingsSecretAgent* unity_settings_secret_agent_construct (GType object_type);
93+
94+void unity_settings_secret_agent_provide_secret (UnitySettingsSecretAgent *agent,
95+ guint request,
96+ GHashTable *secrets);
97+void unity_settings_secret_agent_cancel_request (UnitySettingsSecretAgent *agent,
98+ guint request);
99+
100+G_END_DECLS
101+
102+#endif
plugins/Unity/Indicators/indicatorsmodel.cpp+2−2
@@ -229,9 +229,9 @@ QVariant IndicatorsModel::defaultData(Indicator::Ptr indicator, int role)
229229 case IndicatorsModelRole::Description:
230230 return "";
231231 case IndicatorsModelRole::WidgetSource:
232− return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorWidget.qml";
232+ return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorWidget2.qml";
233233 case IndicatorsModelRole::PageSource:
234− return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorPage.qml";
234+ return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorPage2.qml";
235235 }
236236 return QVariant();
237237}
plugins/Unity/Indicators/plugin.cpp+1−1
@@ -18,7 +18,7 @@
1818 */
1919
2020// Qt
21−#include <QtQml>
21+#include <QtQml/qqml.h>
2222
2323// self
2424#include "plugin.h"
plugins/Unity/Indicators/qml/BaseMenuItem.qml−4
@@ -25,14 +25,10 @@ ListItem.Standard {
2525 id: baseMenu
2626
2727 property bool menuActivated: false
28− property QtObject menu
2928
3029 signal selectMenu()
3130 signal deselectMenu()
3231
33− signal activate()
34− signal changeState(var value)
35−
3632 showDivider: false
3733 __foregroundColor: "#e8e1d0" // FIXME not in palette yet
3834
plugins/Unity/Indicators/qml/ButtonMenuItem.qml+4−9
@@ -24,14 +24,14 @@ import Unity.Indicators 0.1 as Indicators
2424
2525BaseMenuItem {
2626 id: menuItem
27− objectName: menuAction.name
2827 implicitHeight: units.gu(7)
29− enabled: menuAction.active
28+
29+ property alias text: button.text
30+
31+ signal activate()
3032
3133 Button {
3234 id: button
33− enabled: menuAction.enabled
34− text: menu ? menu.label : ""
3535 anchors.centerIn: parent
3636 height: units.gu(4)
3737 width: units.gu(16)
@@ -41,9 +41,4 @@ BaseMenuItem {
4141 menuItem.activate();
4242 }
4343 }
44−
45− Indicators.MenuAction {
46− id: menuAction
47− menu: menuItem.menu
48− }
4944}
plugins/Unity/Indicators/qml/FramedMenuItem.qml−2
@@ -23,10 +23,8 @@ import Ubuntu.Components 0.1
2323import Ubuntu.Components.ListItems 0.1 as ListItem
2424
2525BaseMenuItem {
26− text: menu && menu.label ? menu.label : ""
2726 implicitHeight: units.gu(7)
2827
29− icon: menu ? menu.icon : ""
3028 iconFrame: false
3129
3230 HLine {
plugins/Unity/Indicators/qml/IndicatorPage.qml+4−9
@@ -96,18 +96,13 @@ IndicatorBase {
9696 }
9797 if (item.hasOwnProperty("menu")) {
9898 item.menu = Qt.binding(function() { return model; });
99−
100− item.activate.connect(onActivateMenu);
101− item.changeState.connect(onChangeState);
10299 }
103100 }
104101
105− function onActivateMenu(value) {
106− mainMenu.model.activate(index, value);
107− }
108−
109− function onChangeState(value) {
110− mainMenu.model.changeState(index, value);
102+ Binding {
103+ target: item ? item : null
104+ property: "objectName"
105+ value: model.action
111106 }
112107 }
113108 }
plugins/Unity/Indicators/qml/MenuAction.qmldeleted−59
@@ -1,59 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU Lesser General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU Lesser General Public License for more details.
12− *
13− * You should have received a copy of the GNU Lesser General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Renato Araujo Oliveira Filho <renato@canonical.com>
18− * Nick Dedekind <nick.dedekind@canonical.com>
19− */
20−
21−import QtQuick 2.0
22−
23−/*!
24− \qmltype MenuAction
25− \inqmlmodule Indicators 0.1
26− \brief Helper class to connect to a qmenumodel action
27−
28− Example:
29− \qml
30− BaseMenuItem {
31− id: menuItem
32−
33− Switch {
34− checked: menuAction.state
35− }
36−
37− Indicators.MenuAction {
38− id: menuAction
39− menu: menuItem.menu
40− }
41− }
42− \endqml
43−*/
44−
45−Item {
46− id: menuAction
47−
48− /*!
49− \preliminary
50− The dbus action group object
51− */
52− readonly property string name: menu ? menu.action : ""
53−
54− property QtObject menu: null
55−
56− readonly property var state: menu ? menu.actionState : undefined
57−
58− readonly property bool active: menu ? menu.sensitive : false
59−}
plugins/Unity/Indicators/qml/MenuItemFactory.qml+143−5
@@ -19,6 +19,8 @@
1919
2020import QtQuick 2.0
2121import Unity.Indicators 0.1 as Indicators
22+import Unity.Indicators.Network 0.1 as ICNetwork
23+import QMenuModel 0.1 as QMenuModel
2224
2325Item {
2426 id: menuFactory
@@ -38,25 +40,161 @@ Item {
3840
3941 "com.canonical.unity.slider" : sliderMenu,
4042 "com.canonical.unity.switch" : switchMenu,
43+
44+ "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
45+ "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
4146 }
4247
4348 Component {
4449 id: sliderMenu;
4550 Indicators.SliderMenuItem {
51+ property QtObject menu: null
52+
53+ text: menu && menu.label ? menu.label : ""
54+ icon: menu ? menu.icon : ""
55+ minIcon: menu.ext.minIcon
56+ maxIcon: menu.ext.maxIcon
57+
58+ minimumValue: menu.ext.minValue ? menu.ext.minValue : 0.0
59+ maximumValue: {
60+ var maximum = menu.ext.maxValue ? menu.ext.maxValue : 1.0
61+ if (maximum <= minimumValue) {
62+ return minimumValue + 1;
63+ }
64+ return maximum;
65+ }
66+ value: menu ? menu.actionState : 0.0
67+ enabled: menu ? menu.sensitive : false
68+
4669 Component.onCompleted: {
4770 model.loadExtendedAttributes(modelIndex, {'min-value': 'double',
4871 'max-value': 'double',
4972 'min-icon': 'icon',
5073 'max-icon': 'icon'});
5174 }
75+
76+ // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
77+ // reaching the boundaries (0.0 or 1.0), the value is converted
78+ // to an integer when automatically wrapped in a variant when
79+ // passed to QStateAction::updateState(…). The server chokes on
80+ // those values, complaining that they’re not of the right type…
81+ onChangeState: {
82+ if (value == Math.round(value)) {
83+ if (value >= maximumValue) {
84+ model.changeState(modelIndex, maximumValue - 0.000001);
85+ } else if (value <= minimumValue) {
86+ model.changeState(modelIndex, minimumValue + 0.000001);
87+ } else {
88+ model.changeState(modelIndex, value * 1.000001);
89+ }
90+ } else {
91+ model.changeState(modelIndex, value);
92+ }
93+ }
5294 }
5395 }
54− Component { id: buttonMenu; Indicators.ButtonMenuItem {} }
96+
5597 Component { id: divMenu; Indicators.DivMenuItem {} }
56− Component { id: sectionMenu; Indicators.SectionMenuItem {} }
57− Component { id: progressMenu; Indicators.ProgressMenuItem {} }
58− Component { id: standardMenu; Indicators.StandardMenuItem {} }
59− Component { id: switchMenu; Indicators.SwitchMenuItem {} }
98+
99+ Component {
100+ id: buttonMenu;
101+ Indicators.ButtonMenuItem {
102+ property QtObject menu: null
103+
104+ text: menu && menu.label ? menu.label : ""
105+ enabled: menu ? menu.sensitive : false
106+
107+ onActivate: model.activate(modelIndex);
108+ }
109+ }
110+ Component {
111+ id: sectionMenu;
112+ Indicators.SectionMenuItem {
113+ property QtObject menu: null
114+
115+ text: menu && menu.label ? menu.label : ""
116+ }
117+ }
118+
119+ Component {
120+ id: progressMenu;
121+ Indicators.ProgressMenuItem {
122+ property QtObject menu: null
123+
124+ text: menu && menu.label ? menu.label : ""
125+ icon: menu ? menu.icon : ""
126+ value : menu ? menu.actionState : 0.0
127+ }
128+ }
129+
130+ Component {
131+ id: standardMenu;
132+ Indicators.StandardMenuItem {
133+ property QtObject menu: null
134+
135+ text: menu && menu.label ? menu.label : ""
136+ icon: menu ? menu.icon : ""
137+ checkable: menu ? (menu.isCheck || menu.isRadio) : false
138+ checked: checkable ? menu.isToggled : false
139+ enabled: menu ? menu.sensitive : false
140+
141+ onActivate: model.activate(modelIndex);
142+ }
143+ }
144+
145+ Component {
146+ id: switchMenu;
147+ Indicators.SwitchMenuItem {
148+ property QtObject menu: null
149+
150+ text: menu && menu.label ? menu.label : ""
151+ icon: menu ? menu.icon : ""
152+ checked: menu ? menu.isToggled : false
153+ enabled: menu ? menu.sensitive : false
154+
155+ onActivate: model.activate(modelIndex);
156+ }
157+ }
158+
159+ Component {
160+ id: wifiSection;
161+ Indicators.SectionMenuItem {
162+ property QtObject menu: null
163+
164+ text: menu && menu.label ? menu.label : ""
165+ busy: menu ? menu.ext.xCanonicalBusyAction : false
166+
167+ Component.onCompleted: {
168+ model.loadExtendedAttributes(modelIndex, {'x-canonical-busy-action': 'bool'});
169+ }
170+ }
171+ }
172+
173+ Component {
174+ id: accessPoint;
175+ ICNetwork.AccessPoint {
176+ property QtObject menu: null
177+// property var strenthAction: QMenuModel.UnityMenuAction {
178+// model: menuFactory.model ? menuFactory.model : null
179+// name: menu ? menu.ext.xCanonicalWifiApStrengthAction : ""
180+// }
181+
182+ text: menu && menu.label ? menu.label : ""
183+ icon: menu ? menu.icon : ""
184+ secure: menu ? menu.ext.xCanonicalWifiApIsSecure : false
185+ adHoc: menu ? menu.ext.xCanonicalWifiApIsAdhoc : false
186+ checked: menu ? menu.isToggled : false
187+// signalStrength: strenthAction.valid ? strenthAction.state : 0
188+ enabled: menu ? menu.sensitive : false
189+
190+ Component.onCompleted: {
191+ model.loadExtendedAttributes(modelIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
192+ 'x-canonical-wifi-ap-is-secure': 'bool',
193+ 'x-canonical-wifi-ap-strength-action': 'string'});
194+ }
195+ onActivate: model.activate(modelIndex);
196+ }
197+ }
60198
61199 function load(modelData) {
62200 if (modelData.type !== undefined) {
plugins/Unity/Indicators/qml/ProgressMenuItem.qml+1−8
@@ -23,10 +23,8 @@ import Unity.Indicators 0.1 as Indicators
2323
2424FramedMenuItem {
2525 id: menuItem
26− objectName: menuAction.name
27− enabled: menuAction.active
2826
29− property int value : menuAction.state
27+ property int value : 0.0
3028
3129 control: Label {
3230 id: progress
@@ -37,9 +35,4 @@ FramedMenuItem {
3735 fontSize: "medium"
3836 color: "#e8e1d0"
3937 }
40−
41− Indicators.MenuAction {
42− id: menuAction
43− menu: menuItem.menu
44− }
4538}
plugins/Unity/Indicators/qml/SectionMenuItem.qml+3−3
@@ -22,15 +22,15 @@ import Ubuntu.Components 0.1
2222import Ubuntu.Components.ListItems 0.1 as ListItem
2323
2424BaseMenuItem {
25− property string label: menu && menu.label ? menu.label : ""
25+ id: menuItem
26+ property alias text: header.text
2627 property bool busy: false
2728
28− implicitHeight: label !== "" ? header.height : 0
29+ implicitHeight: text !== "" ? header.height : 0
2930
3031 ListItem.Header {
3132 id: header
3233
33− text: label
3434 height: units.gu(4)
3535 anchors {
3636 left: parent.left
plugins/Unity/Indicators/qml/SliderMenuItem.qml+8−23
@@ -24,17 +24,20 @@ import Unity.Indicators 0.1 as Indicators
2424
2525FramedMenuItem {
2626 id: menuItem
27− objectName: menuAction.name
28− enabled: menuAction.active
2927
3028 property alias minimumValue: slider.minimumValue
3129 property alias maximumValue: slider.maximumValue
32− readonly property double value: menu ? menuAction.state : 0.0
30+ property double value: 0.0
31+
32+ property alias minIcon: leftImage.source
33+ property alias maxIcon: rightImage.source
3334
3435 property QtObject d: QtObject {
3536 property bool enableValueConnection: true
3637 }
3738
39+ signal changeState(real value)
40+
3841 onValueChanged: {
3942 // TODO: look into adding a component to manage bi-directional bindings.
4043 var oldEnable = d.enableValueConnection
@@ -58,7 +61,6 @@ FramedMenuItem {
5861 anchors.verticalCenter: row.verticalCenter
5962 height: units.gu(4)
6063 width: height
61− source: menu.ext.minIcon
6264 }
6365
6466 Components.Slider {
@@ -75,19 +77,8 @@ FramedMenuItem {
7577 value = menuItem.value
7678 }
7779
78− // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
79− // reaching the boundaries (0.0 or 1.0), the value is converted
80− // to an integer when automatically wrapped in a variant when
81− // passed to QStateAction::updateState(…). The server chokes on
82− // those values, complaining that they’re not of the right type…
83− minimumValue: menu.ext.minValue ? menu.ext.minValue * 1.000001 : 0.0000001
84− maximumValue: {
85− var maximum = menu.ext.maxValue ? menu.ext.maxValue * 1.000001 : 0.9999999
86− if (maximum <= minimumValue) {
87− return minimumValue + 1;
88− }
89− return maximum;
90− }
80+ minimumValue: 0.0
81+ maximumValue: 0.1
9182
9283 // FIXME - to be deprecated in Ubuntu.Components.
9384 // Use this to disable the label, since there is not way to do it on the component.
@@ -109,12 +100,6 @@ FramedMenuItem {
109100 anchors.verticalCenter: row.verticalCenter
110101 height: units.gu(4)
111102 width: height
112− source: menu.ext.maxIcon
113103 }
114104 }
115−
116− Indicators.MenuAction {
117− id: menuAction
118− menu: menuItem.menu
119− }
120105}
plugins/Unity/Indicators/qml/StandardMenuItem.qml+5−10
@@ -23,16 +23,16 @@ import Unity.Indicators 0.1 as Indicators
2323
2424FramedMenuItem {
2525 id: menuItem
26− objectName: menuAction.name
27− enabled: menuAction.active
2826
29− readonly property bool checkable: menu ? (menu.isCheck || menu.isRadio) : false
30− readonly property bool checked: checkable ? menu.isToggled : false
27+ property bool checkable: false
28+ property bool checked: false
29+
30+ signal activate()
3131
3232 // FIXME : need a radio button from sdk
3333 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211866
3434 onCheckableChanged: {
35− if (menu && checkable) {
35+ if (checkable) {
3636 icon = checkComponent.createObject(menuItem);
3737 }
3838 }
@@ -68,9 +68,4 @@ FramedMenuItem {
6868 }
6969 }
7070 }
71−
72− Indicators.MenuAction {
73− id: menuAction
74− menu: menuItem.menu
75− }
7671}
plugins/Unity/Indicators/qml/SwitchMenuItem.qml+3−8
@@ -24,10 +24,10 @@ import Unity.Indicators 0.1 as Indicators
2424
2525FramedMenuItem {
2626 id: menuItem
27− objectName: menuAction.name
28− enabled: menuAction.enabled
2927
30− readonly property bool checked: menu ? menu.isToggled : false
28+ property bool checked: false
29+
30+ signal activate()
3131
3232 onCheckedChanged: {
3333 // Can't rely on binding. Checked is assigned on click.
@@ -50,9 +50,4 @@ FramedMenuItem {
5050 menuItem.activate();
5151 }
5252 }
53−
54− Indicators.MenuAction {
55− id: menuAction
56− menu: menuItem.menu
57− }
5853}
plugins/Unity/Indicators/qml/qmldir−1
@@ -11,7 +11,6 @@ HLine 0.1 HLine.qml
1111IndicatorBase 0.1 IndicatorBase.qml
1212IndicatorPage 0.1 IndicatorPage.qml
1313IndicatorWidget 0.1 IndicatorWidget.qml
14−MenuAction 0.1 MenuAction.qml
1514MenuActionBinding 0.1 MenuActionBinding.qml
1615MenuItemFactory 0.1 MenuItemFactory.qml
1716ProgressMenuItem 0.1 ProgressMenuItem.qml
plugins/Unity/IndicatorsLegacy/Network/CMakeLists.txt+8−8
@@ -1,26 +1,26 @@
11# export_qmlplugin macro
22include(Plugins)
33
4−project(IndicatorsNetworkQml)
4+project(IndicatorsLegacyNetworkQml)
55
66pkg_check_modules(GLIB glib-2.0>=2.32)
77pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
88pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
99
1010if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
11− set(INDICATORS_NETWORK_SRC
11+ set(INDICATORS_LEGACY_NETWORK_SRC
1212 networkagent.cpp
1313 plugin.cpp
1414 secret-agent.c
1515 )
1616
17− add_library(IndicatorsNetworkQml MODULE
18− ${INDICATORS_NETWORK_SRC}
17+ add_library(IndicatorsLegacyNetworkQml MODULE
18+ ${INDICATORS_LEGACY_NETWORK_SRC}
1919 )
2020
21− qt5_use_modules(IndicatorsNetworkQml Core Qml Widgets)
21+ qt5_use_modules(IndicatorsLegacyNetworkQml Core Qml Widgets)
2222
23− set_target_properties(IndicatorsNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
23+ set_target_properties(IndicatorsLegacyNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
2424
2525 include_directories(
2626 ${CMAKE_CURRENT_SOURCE_DIR}
@@ -29,14 +29,14 @@ if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
2929 ${LIBNM_UTIL_INCLUDE_DIRS}
3030 )
3131
32− target_link_libraries(IndicatorsNetworkQml
32+ target_link_libraries(IndicatorsLegacyNetworkQml
3333 ${GLIB_LIBRARIES}
3434 ${LIBNM_GLIB_LIBRARIES}
3535 ${LIBNM_UTIL_LIBRARIES}
3636 )
3737
3838 # export the qmltypes and plugin files
39− export_qmlplugin(Unity.IndicatorsLegacy.Network 0.1 Unity/IndicatorsLegacy/Network TARGETS IndicatorsNetworkQml)
39+ export_qmlplugin(Unity.IndicatorsLegacy.Network 0.1 Unity/IndicatorsLegacy/Network TARGETS IndicatorsLegacyNetworkQml)
4040
4141else()
4242 message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
plugins/Unity/IndicatorsLegacy/Network/plugin.cpp+1−1
@@ -17,7 +17,7 @@
1717 */
1818
1919// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
2121
2222// self
2323#include "plugin.h"
plugins/Unity/IndicatorsLegacy/Network/qml/qmldir+1−1
@@ -1,5 +1,5 @@
11module Unity.IndicatorsLegacy.Network
2−plugin IndicatorsNetworkQml
2+plugin IndicatorsLegacyNetworkQml
33typeinfo plugin.qmltypes
44
55NetworkPage 0.1 NetworkPage.qml
plugins/Unity/IndicatorsLegacy/plugin.cpp+1−1
@@ -17,7 +17,7 @@
1717 */
1818
1919// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
2121
2222// self
2323#include "plugin.h"
plugins/Unity/Launcher/backend/launcherbackend.cpp+91−87
@@ -22,7 +22,9 @@
2222#include "launcherbackend.h"
2323
2424#include <QDir>
25+#include <QDBusArgument>
2526#include <QFileInfo>
27+#include <QDebug>
2628
2729LauncherBackend::LauncherBackend(bool useStorage, QObject *parent):
2830 QObject(parent),
@@ -41,8 +43,8 @@ LauncherBackend::~LauncherBackend()
4143
4244void LauncherBackend::clearItems()
4345{
44− for (int i = 0; i < m_storedApps.size(); i++) {
45− delete m_storedApps[i].settings;
46+ for (LauncherBackendItem &app: m_storedApps) {
47+ delete app.settings;
4648 }
4749 m_storedApps.clear();
4850}
@@ -50,80 +52,59 @@ void LauncherBackend::clearItems()
5052QStringList LauncherBackend::storedApplications() const
5153{
5254 auto files = QStringList();
53− for (int i = 0; i < m_storedApps.size(); i++) {
54− files << m_storedApps[i].settings->fileName();
55+ for (const LauncherBackendItem &app: m_storedApps) {
56+ files << app.settings->fileName();
5557 }
5658 return files;
5759}
5860
5961void LauncherBackend::setStoredApplications(const QStringList &appIds)
6062{
61− // Are we dropping any pinned apps?
62− auto needToSync = false;
63+ // Record all existing pinned apps, so we can notice them in new list
6364 auto pinnedItems = QStringList();
64− for (int i = 0; i < m_storedApps.size(); i++) {
65− if (m_storedApps[i].pinned) {
66− auto found = false;
67− for (int j = 0; j < appIds.size(); j++) {
68− auto fullAppId = resolveAppId(appIds[j]);
69− if (m_storedApps[i].settings->fileName() == fullAppId) {
70− found = true;
71− break;
72− }
73− }
74− if (!found) {
75− needToSync = true;
76− }
77− pinnedItems.append(m_storedApps[i].settings->fileName());
65+ for (LauncherBackendItem &app: m_storedApps) {
66+ if (app.pinned) {
67+ pinnedItems.append(app.settings->fileName());
7868 }
7969 }
8070
8171 clearItems();
8272
83− for (auto appId: appIds) {
84− loadDesktopFile(appId, pinnedItems.contains(resolveAppId(appId)));
73+ for (const QString &appId: appIds) {
74+ loadApp(makeAppDetails(appId, pinnedItems.contains(desktopFile(appId))));
8575 }
8676
87− if (needToSync) {
88− syncToAccounts();
89− }
77+ syncToAccounts();
9078}
9179
9280QString LauncherBackend::desktopFile(const QString &appId) const
9381{
94− auto index = findItem(appId);
95− if (index < 0) {
96− return "";
97− } else {
98− return resolveAppId(appId);
99− }
82+ QFileInfo fileInfo(QDir("/usr/share/applications"), appId);
83+ return fileInfo.absoluteFilePath();
10084}
10185
10286QString LauncherBackend::displayName(const QString &appId) const
10387{
104− auto index = findItem(appId);
105− if (index < 0) {
106− return "";
107− } else {
108− return m_storedApps[index].settings->value("Desktop Entry/Name").toString();
109− }
88+ auto desktopFile = parseDesktopFile(appId);
89+ auto displayName = desktopFile->value("Desktop Entry/Name").toString();
90+ delete desktopFile;
91+ return displayName;
11092}
11193
11294QString LauncherBackend::icon(const QString &appId) const
11395{
114− auto index = findItem(appId);
115− if (index < 0) {
116− return "";
117− } else {
118− auto iconName = m_storedApps[index].settings->value("Desktop Entry/Icon").toString();
119− if (!iconName.isEmpty()) {
120− QFileInfo iconFileInfo(iconName);
121− if (iconFileInfo.isRelative()) {
122− iconName = "image://gicon/" + iconName;
123− }
96+ auto desktopFile = parseDesktopFile(appId);
97+ auto iconName = desktopFile->value("Desktop Entry/Icon").toString();
98+ delete desktopFile;
99+
100+ if (!iconName.isEmpty()) {
101+ QFileInfo iconFileInfo(iconName);
102+ if (iconFileInfo.isRelative()) {
103+ iconName = "image://gicon/" + iconName;
124104 }
125− return iconName;
126105 }
106+
107+ return iconName;
127108}
128109
129110bool LauncherBackend::isPinned(const QString &appId) const
@@ -184,71 +165,94 @@ void LauncherBackend::triggerQuickListAction(const QString &appId, const QString
184165 Q_UNUSED(quickListId)
185166}
186167
187−QString LauncherBackend::resolveAppId(const QString &appId) const
188−{
189− QFileInfo fileInfo(QDir("/usr/share/applications"), appId);
190− return fileInfo.absoluteFilePath();
191−}
192−
193168void LauncherBackend::syncFromAccounts()
194169{
195− auto appIds = QStringList();
170+ QList<QVariantMap> apps;
171+ bool defaults = true;
172+
173+ clearItems();
196174
197175 if (m_user != "" && m_accounts != nullptr) {
198− appIds = m_accounts->getUserProperty(m_user, "launcher-items").toStringList();
176+ auto variant = m_accounts->getUserProperty(m_user, "launcher-items");
177+ apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
178+ defaults = isDefaultsItem(apps);
199179 }
200180
201181 // TODO: load default pinned ones from default config, instead of hardcoding here...
202− if (appIds.isEmpty()) {
203− appIds <<
204− "phone-app.desktop" <<
205− "camera-app.desktop" <<
206− "gallery-app.desktop" <<
207− "facebook-webapp.desktop" <<
208− "webbrowser-app.desktop" <<
209− "twitter-webapp.desktop" <<
210− "gmail-webapp.desktop" <<
211− "ubuntu-weather-app.desktop" <<
212− "notes-app.desktop" <<
213− "calendar-app.desktop";
182+ if (defaults) {
183+ qDebug() << "loading default applications for launcher";
184+ apps <<
185+ makeAppDetails("phone-app.desktop", true) <<
186+ makeAppDetails("camera-app.desktop", true) <<
187+ makeAppDetails("gallery-app.desktop", true) <<
188+ makeAppDetails("facebook-webapp.desktop", true) <<
189+ makeAppDetails("webbrowser-app.desktop", true) <<
190+ makeAppDetails("twitter-webapp.desktop", true) <<
191+ makeAppDetails("gmail-webapp.desktop", true) <<
192+ makeAppDetails("ubuntu-weather-app.desktop", true) <<
193+ makeAppDetails("notes-app.desktop", true) <<
194+ makeAppDetails("calendar-app.desktop", true);
214195 }
215196
216− setStoredApplications(appIds);
217−
218− // Mark all apps as pinned (since we just refreshed the set)
219− for (int i = 0; i < m_storedApps.size(); i++) {
220− m_storedApps[i].pinned = true;
197+ for (const QVariant &app: apps) {
198+ loadApp(app.toMap());
221199 }
222200}
223201
224202void LauncherBackend::syncToAccounts()
225203{
226− auto pinnedItems = QStringList();
227− for (int i = 0; i < m_storedApps.size(); i++) {
228− if (m_storedApps[i].pinned) {
229− pinnedItems.append(m_storedApps[i].settings->fileName());
204+ if (m_user != "" && m_accounts != nullptr) {
205+ QList<QVariantMap> items;
206+
207+ for (LauncherBackendItem &app: m_storedApps) {
208+ items << makeAppDetails(app.settings->fileName(), app.pinned);
230209 }
231− }
232210
233− if (m_user != "" && m_accounts != nullptr) {
234− m_accounts->setUserProperty(m_user, "launcher-items", QVariant(pinnedItems));
211+ m_accounts->setUserProperty(m_user, "launcher-items", QVariant::fromValue(items));
235212 }
236213}
237214
238−bool LauncherBackend::loadDesktopFile(const QString &appId, bool isPinned)
215+QSettings *LauncherBackend::parseDesktopFile(const QString &appId) const
239216{
240− auto item = LauncherBackendItem();
241− auto fullAppId = resolveAppId(appId);
242− item.settings = new QSettings(fullAppId, QSettings::IniFormat);
217+ auto fullAppId = desktopFile(appId);
218+ return new QSettings(fullAppId, QSettings::IniFormat);
219+}
220+
221+void LauncherBackend::loadApp(const QVariantMap &details)
222+{
223+ auto appId = details.value("id").toString();
224+ auto isPinned = details.value("is-pinned").toBool();
225+
226+ if (appId.isEmpty()) {
227+ return;
228+ }
229+
230+ LauncherBackendItem item;
231+ item.settings = parseDesktopFile(appId);
243232 item.pinned = isPinned;
244233 m_storedApps.append(item);
245− return true; // QSettings doesn't really indicate a failure mode right now
234+}
235+
236+QVariantMap LauncherBackend::makeAppDetails(const QString &appId, bool pinned) const
237+{
238+ QVariantMap details;
239+ details.insert("id", appId);
240+ details.insert("is-pinned", pinned);
241+ return details;
242+}
243+
244+bool LauncherBackend::isDefaultsItem(const QList<QVariantMap> &apps) const
245+{
246+ // To differentiate between an empty list and a list that hasn't been set
247+ // yet (and should thus be populated with the defaults), we use a special
248+ // list of one item with the 'defaults' field set to true.
249+ return (apps.size() == 1 && apps[0].value("defaults").toBool());
246250}
247251
248252int LauncherBackend::findItem(const QString &appId) const
249253{
250− auto fullAppId = resolveAppId(appId);
251− for (int i = 0; i < m_storedApps.size(); i++) {
254+ auto fullAppId = desktopFile(appId);
255+ for (int i = 0; i < m_storedApps.size(); ++i) {
252256 if (m_storedApps[i].settings->fileName() == fullAppId) {
253257 return i;
254258 }
plugins/Unity/Launcher/backend/launcherbackend.h+13−2
@@ -59,12 +59,18 @@ public:
5959
6060 /**
6161 * @brief Get the full path to the .desktop file.
62+ *
63+ * The application does not need to be in the list of stored applications.
64+ *
6265 * @returns The full path to the .dekstop file.
6366 */
6467 QString desktopFile(const QString &appId) const;
6568
6669 /**
6770 * @brief Get the user friendly name of an application.
71+ *
72+ * The application does not need to be in the list of stored applications.
73+ *
6874 * @param appId The ID of the application.
6975 * @returns The user friendly name of the application.
7076 */
@@ -72,6 +78,9 @@ public:
7278
7379 /**
7480 * @brief Get the icon of an application.
81+ *
82+ * The application does not need to be in the list of stored applications.
83+ *
7584 * @param appId The ID of the application.
7685 * @returns The full path to the icon for the application.
7786 */
@@ -132,9 +141,11 @@ Q_SIGNALS:
132141 void countChanged(const QString &appId, int count);
133142
134143private:
135− QString resolveAppId(const QString &appId) const;
136− bool loadDesktopFile(const QString &appId, bool isPinned);
144+ QSettings *parseDesktopFile(const QString &appId) const;
145+ QVariantMap makeAppDetails(const QString &appId, bool pinned) const;
146+ void loadApp(const QVariantMap &details);
137147 int findItem(const QString &appId) const;
148+ bool isDefaultsItem(const QList<QVariantMap> &apps) const;
138149 void syncFromAccounts();
139150 void syncToAccounts();
140151 void clearItems();
plugins/Unity/Launcher/launchermodel.cpp+28
@@ -25,6 +25,9 @@ LauncherModel::LauncherModel(QObject *parent):
2525 LauncherModelInterface(parent),
2626 m_backend(new LauncherBackend(true, this))
2727{
28+ connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
29+ connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
30+
2831 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
2932 LauncherItem *item = new LauncherItem(entry,
3033 m_backend->desktopFile(entry),
@@ -67,6 +70,10 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
6770 return item->icon();
6871 case RolePinned:
6972 return item->pinned();
73+ case RoleCount:
74+ return item->count();
75+ case RoleProgress:
76+ return item->progress();
7077 }
7178
7279 return QVariant();
@@ -228,3 +235,24 @@ int LauncherModel::findApplication(const QString &appId)
228235 }
229236 return -1;
230237}
238+
239+void LauncherModel::progressChanged(const QString &appId, int progress)
240+{
241+ int idx = findApplication(appId);
242+ if (idx >= 0) {
243+ LauncherItem *item = m_list.at(idx);
244+ item->setProgress(progress);
245+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
246+ }
247+}
248+
249+
250+void LauncherModel::countChanged(const QString &appId, int count)
251+{
252+ int idx = findApplication(appId);
253+ if (idx >= 0) {
254+ LauncherItem *item = m_list.at(idx);
255+ item->setCount(count);
256+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
257+ }
258+}
plugins/Unity/Launcher/launchermodel.h+4
@@ -55,6 +55,10 @@ private:
5555 void storeAppList();
5656 int findApplication(const QString &appId);
5757
58+private Q_SLOTS:
59+ void progressChanged(const QString &appId, int progress);
60+ void countChanged(const QString &appId, int count);
61+
5862private:
5963 QList<LauncherItem*> m_list;
6064 LauncherBackend *m_backend;
plugins/Unity/Launcher/plugin.cpp+1−1
@@ -18,7 +18,7 @@
1818 */
1919
2020// Qt
21−#include <QtQml>
21+#include <QtQml/qqml.h>
2222
2323// self
2424#include "plugin.h"
plugins/Unity/categories.cpp+6
@@ -39,6 +39,7 @@ Categories::Categories(QObject* parent)
3939 m_roles[Categories::RoleIcon] = "icon";
4040 m_roles[Categories::RoleRenderer] = "renderer";
4141 m_roles[Categories::RoleContentType] = "contentType";
42+ m_roles[Categories::RoleProgressSource] = "progressSource";
4243 m_roles[Categories::RoleHints] = "hints";
4344 m_roles[Categories::RoleResults] = "results";
4445 m_roles[Categories::RoleCount] = "count";
@@ -198,6 +199,11 @@ Categories::data(const QModelIndex& index, int role) const
198199 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
199200 return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
200201 }
202+ case RoleProgressSource:
203+ {
204+ auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
205+ return hints.contains("progress-source") ? hints["progress-source"] : QVariant();
206+ }
201207 case RoleHints:
202208 return DeeListModel::data(index, CategoryColumn::HINTS);
203209 case RoleResults:
plugins/Unity/categories.h+1
@@ -47,6 +47,7 @@ public:
4747 RoleIcon,
4848 RoleRenderer,
4949 RoleContentType,
50+ RoleProgressSource,
5051 RoleHints,
5152 RoleResults,
5253 RoleCount,
plugins/Unity/genericoptionsmodel.cpp+2−2
@@ -76,9 +76,9 @@ int GenericOptionsModel::rowCount(const QModelIndex& /* parent */) const
7676 return m_options.size ();
7777}
7878
79−void GenericOptionsModel::setActive(QVector<AbstractFilterOption *>::size_type idx, bool value)
79+void GenericOptionsModel::setActive(int idx, bool value)
8080{
81− if (idx < m_options.size())
81+ if (idx >= 0 && idx < m_options.size())
8282 {
8383 m_options[idx]->setActive(value);
8484 }
plugins/Unity/genericoptionsmodel.h+1−1
@@ -49,7 +49,7 @@ public:
4949 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
5050 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
5151
52− Q_INVOKABLE void setActive(QVector<AbstractFilterOption *>::size_type idx, bool value);
52+ Q_INVOKABLE void setActive(int idx, bool value);
5353 Q_INVOKABLE void setActive(const QString &id, bool value);
5454
5555 AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
plugins/Unity/plugin.cpp+4−1
@@ -17,8 +17,9 @@
1717 */
1818
1919// Qt
20−#include <QtQml>
2120#include <QDBusConnection>
21+#include <QQmlContext>
22+#include <qqml.h>
2223
2324// self
2425#include "plugin.h"
@@ -33,6 +34,7 @@
3334#include "categories.h"
3435#include "categoryresults.h"
3536#include "bottombarvisibilitycommunicatorshell.h"
37+#include "genericoptionsmodel.h"
3638
3739// libqtdee
3840#include "deelistmodel.h"
@@ -47,6 +49,7 @@ void UnityPlugin::registerTypes(const char *uri)
4749 qmlRegisterUncreatableType<PreviewAction>(uri, 0, 1, "PreviewAction", "Can't create PreviewAction object in QML.");
4850 qmlRegisterUncreatableType<PreviewInfoHint>(uri, 0, 1, "PreviewInfoHint", "Can't create PreviewInfoHint object in QML.");
4951 qmlRegisterUncreatableType<SocialPreviewComment>(uri, 0, 1, "SocialPreviewComment", "Can't create SocialPreviewComment object in QML.");
52+ qmlRegisterUncreatableType<GenericOptionsModel>(uri, 0, 1, "GenericOptionsModel", "Can't create options model in QML.");
5053 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
5154 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
5255 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
plugins/Utils/plugin.cpp+1−1
@@ -17,7 +17,7 @@
1717 */
1818
1919// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
2121#include <QtQuick/QQuickWindow>
2222
2323// self
plugins/Utils/qlimitproxymodelqml.cpp+90−4
@@ -25,6 +25,8 @@ QLimitProxyModelQML::QLimitProxyModelQML(QObject *parent)
2525 , m_limit(-1)
2626 , m_sourceInserting(false)
2727 , m_sourceRemoving(false)
28+ , m_dataChangedBegin(-1)
29+ , m_dataChangedEnd(-1)
2830{
2931 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
3032 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
@@ -130,8 +132,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeInserted(const QModelIndex &parent, int
130132 beginInsertRows(mapFromSource(parent), start, end);
131133 m_sourceInserting = true;
132134 } else if (start < m_limit) {
133− beginInsertRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
134− m_sourceInserting = true;
135+ const int nSourceAddedItems = end - start + 1;
136+ const int currentCount = QIdentityProxyModel::rowCount();
137+ if (currentCount + nSourceAddedItems <= m_limit) {
138+ // After Inserting items we will be under the limit
139+ // so just proceed with the insertion normally
140+ beginInsertRows(mapFromSource(parent), start, end);
141+ m_sourceInserting = true;
142+ } else if (currentCount >= m_limit) {
143+ // We are already over the limit so to our users we are not adding items, just
144+ // changing it's data, i.e we had something like
145+ // A B C D E
146+ // with a limit of 5
147+ // after inserting (let's say three 'F' at position 1) we will have
148+ // A F F F B
149+ // so we just need to signal a dataChanged from 1 to 4
150+ m_dataChangedBegin = start;
151+ m_dataChangedEnd = m_limit - 1;
152+ } else { // currentCount < m_limit && currentCount + nSourceAddedItems > m_limit
153+ // We have less items than the limit but after adding them we will be over
154+ // To our users this means we need to insert some items and change the
155+ // data of some others, i.e we had something like
156+ // A B C
157+ // with a limit of 5
158+ // after inserting (let's say three 'F' at position 1) we will have
159+ // A F F F B
160+ // so we need to signal an insetion from position 1 to 2, instead of from
161+ // position 1 to 3 and a after that a data changed from 3 to 4
162+ const int nItemsToInsert = m_limit - currentCount;
163+ beginInsertRows(mapFromSource(parent), start, start + nItemsToInsert - 1);
164+ m_sourceInserting = true;
165+ m_dataChangedBegin = start + nItemsToInsert;
166+ m_dataChangedEnd = m_limit - 1;
167+ if (m_dataChangedBegin > m_dataChangedEnd) {
168+ // Just in case we were empty and insert 6 items with a limit of 5
169+ // We don't want to signal a dataChanged from 5 to 4
170+ m_dataChangedBegin = -1;
171+ m_dataChangedEnd = -1;
172+ }
173+ }
135174 }
136175}
137176
@@ -142,8 +181,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int s
142181 beginRemoveRows(mapFromSource(parent), start, end);
143182 m_sourceRemoving = true;
144183 } else if (start < m_limit) {
145− beginRemoveRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
146− m_sourceRemoving = true;
184+ const int nSourceRemovedItems = end - start + 1;
185+ const int currentCount = QIdentityProxyModel::rowCount();
186+ if (currentCount <= m_limit) {
187+ // We are already under the limit so
188+ // so just proceed with the removal normally
189+ beginRemoveRows(mapFromSource(parent), start, end);
190+ m_sourceRemoving = true;
191+ } else if (currentCount - nSourceRemovedItems >= m_limit) {
192+ // Even after removing items we will be at or over the limit
193+ // So to our users we are not removing anything, just changing the data
194+ // i.e. we had a internal model with
195+ // A B C D E F G H
196+ // and a limit of 5, our users just see
197+ // A B C D E
198+ // so if we remove 3 items starting at 1 we have to expose
199+ // A E F G H
200+ // that is, a dataChanged from 1 to 4
201+ m_dataChangedBegin = start;
202+ m_dataChangedEnd = m_limit - 1;
203+ } else { // currentCount > m_limit && currentCount - nSourceRemovedItems < m_limit
204+ // We have more items than the limit but after removing we will be below it
205+ // So to our users we both removing and changing the data
206+ // i.e. we had a internal model with
207+ // A B C D E F G
208+ // and a limit of 5, our users just see
209+ // A B C D E
210+ // so if we remove items from 1 to 3 we have to expose
211+ // A E F G
212+ // that is, a remove from 4 to 4 and a dataChanged from 1 to 3
213+ const int nItemsToRemove = m_limit - (currentCount - nSourceRemovedItems);
214+ beginRemoveRows(mapFromSource(parent), m_limit - nItemsToRemove, m_limit - 1);
215+ m_sourceRemoving = true;
216+ m_dataChangedBegin = start;
217+ m_dataChangedEnd = m_limit - nItemsToRemove - 1;
218+ if (m_dataChangedBegin > m_dataChangedEnd) {
219+ m_dataChangedBegin = -1;
220+ m_dataChangedEnd = -1;
221+ }
222+ }
147223 }
148224}
149225
@@ -154,6 +230,11 @@ QLimitProxyModelQML::sourceRowsInserted(const QModelIndex & /*parent*/, int /*st
154230 endInsertRows();
155231 m_sourceInserting = false;
156232 }
233+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
234+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
235+ m_dataChangedBegin = -1;
236+ m_dataChangedEnd = -1;
237+ }
157238}
158239
159240void
@@ -163,4 +244,9 @@ QLimitProxyModelQML::sourceRowsRemoved(const QModelIndex & /*parent*/, int /*sta
163244 endRemoveRows();
164245 m_sourceRemoving = false;
165246 }
247+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
248+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
249+ m_dataChangedBegin = -1;
250+ m_dataChangedEnd = -1;
251+ }
166252}
plugins/Utils/qlimitproxymodelqml.h+2
@@ -55,6 +55,8 @@ private:
5555 int m_limit;
5656 bool m_sourceInserting;
5757 bool m_sourceRemoving;
58+ int m_dataChangedBegin;
59+ int m_dataChangedEnd;
5860};
5961
6062#endif // QLIMITPROXYMODELQML_H
po/ar.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
1818"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/az.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/bg.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/ca.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/de.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/el.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/en_AU.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/en_GB.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/eo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/es.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/fi.po+13−13
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11−"PO-Revision-Date: 2013-08-12 17:45+0000\n"
12−"Last-Translator: Aleksi Kinnunen <Unknown>\n"
11+"PO-Revision-Date: 2013-08-23 16:15+0000\n"
12+"Last-Translator: Timo Jyrinki <timo.jyrinki@canonical.com>\n"
1313"Language-Team: Finnish <fi@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-24 05:59+0000\n"
19+"X-Generator: Launchpad (build 16738)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -94,35 +94,35 @@ msgstr "Viimeisimmät haut"
9494#: Dash/Apps/AppInfo.qml:72
9595#, qt-format
9696msgid "(%1)"
97−msgstr ""
97+msgstr "(%1)"
9898
9999#. TRANSLATORS: Number of persons who wrote reviews for this app
100100#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101101#, qt-format
102102msgid "%1 review"
103103msgid_plural "%1 reviews"
104−msgstr[0] ""
105−msgstr[1] ""
104+msgstr[0] "%1 arvostelu"
105+msgstr[1] "%1 arvostelua"
106106
107107#: Dash/Apps/AppPreview.qml:181
108108msgid "Rate this"
109−msgstr ""
109+msgstr "Arvioi tämä"
110110
111111#: Dash/Apps/AppReviews.qml:72
112112msgid "Add a review"
113−msgstr ""
113+msgstr "Lisää arvostelu"
114114
115115#: Dash/Apps/AppReviews.qml:84
116116msgid "Review"
117−msgstr ""
117+msgstr "Arvostelu"
118118
119119#: Dash/Apps/AppReviews.qml:118
120120msgid "Send"
121−msgstr ""
121+msgstr "Lähetä"
122122
123123#: Dash/Apps/AppReviews.qml:136
124124msgid "Comments:"
125−msgstr ""
125+msgstr "Kommentit:"
126126
127127#: Dash/DashApps.qml:65
128128msgid "Running apps"
@@ -244,4 +244,4 @@ msgstr "Hakuun täsmääviä tuloksia ei löydy."
244244#: Shell.qml:399
245245#, qt-format
246246msgid "Please enter %1"
247−msgstr ""
247+msgstr "Kirjoita %1"
po/fo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/fr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/gl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/he.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/hr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/hu.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/hy.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/id.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/it.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ja.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/kn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ko.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ky.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/lv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/my.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/nb.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/nl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/pl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
1818"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/pt_BR.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ro.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
1818"== 0) && (n != 0))) ? 2: 1));\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/ru.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/si.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/sl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
1818"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/sr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/sv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/te.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/tr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ug.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/uk.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/unity8.pot+51−43
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11+"POT-Creation-Date: 2013-08-27 13:36-0400\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -86,25 +86,69 @@ msgstr ""
8686msgid "YouTube"
8787msgstr ""
8888
89+#: Components/EdgeDemoOverlay.qml:145
90+msgid "Skip intro"
91+msgstr ""
92+
93+#: Components/EdgeDemo.qml:98
94+msgid "Right edge"
95+msgstr ""
96+
97+#: Components/EdgeDemo.qml:99
98+msgid "Try swiping from the right edge to unlock the phone"
99+msgstr ""
100+
101+#: Components/EdgeDemo.qml:135
102+msgid "Top edge"
103+msgstr ""
104+
105+#: Components/EdgeDemo.qml:136
106+msgid "Try swiping from the top edge to access the indicators"
107+msgstr ""
108+
109+#: Components/EdgeDemo.qml:161
110+msgid "Close"
111+msgstr ""
112+
113+#: Components/EdgeDemo.qml:162
114+msgid "Swipe up again to close the settings screen"
115+msgstr ""
116+
117+#: Components/EdgeDemo.qml:189
118+msgid "Left edge"
119+msgstr ""
120+
121+#: Components/EdgeDemo.qml:190
122+msgid "Swipe from the left to reveal the launcher for quick access to apps"
123+msgstr ""
124+
125+#: Components/EdgeDemo.qml:217
126+msgid "Well done"
127+msgstr ""
128+
129+#: Components/EdgeDemo.qml:218
130+msgid "You have now mastered the edge gestures and can start using the phone"
131+msgstr ""
132+
89133#: Components/PageHeader.qml:298
90134msgid "Recent searches"
91135msgstr ""
92136
93137#. TRANSLATORS: Number of persons who rated this app
94−#: Dash/Apps/AppInfo.qml:72
138+#: Dash/Apps/AppInfo.qml:73
95139#, qt-format
96140msgid "(%1)"
97141msgstr ""
98142
99143#. TRANSLATORS: Number of persons who wrote reviews for this app
100−#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
144+#: Dash/Apps/AppInfo.qml:85
101145#, qt-format
102146msgid "%1 review"
103147msgid_plural "%1 reviews"
104148msgstr[0] ""
105149msgstr[1] ""
106150
107−#: Dash/Apps/AppPreview.qml:181
151+#: Dash/Apps/AppPreview.qml:175
108152msgid "Rate this"
109153msgstr ""
110154
@@ -124,42 +168,6 @@ msgstr ""
124168msgid "Comments:"
125169msgstr ""
126170
127−#: Dash/DashApps.qml:65
128−msgid "Running apps"
129−msgstr ""
130−
131−#: Dash/DashApps.qml:68
132−msgid "Frequently used"
133−msgstr ""
134−
135−#: Dash/DashApps.qml:71
136−msgid "Installed"
137−msgstr ""
138−
139−#: Dash/DashApps.qml:74
140−msgid "Available for download"
141−msgstr ""
142−
143−#: Dash/DashApps.qml:168
144−msgid "Apps"
145−msgstr ""
146−
147−#: Dash/DashHome.qml:41
148−msgid "Frequent Apps"
149−msgstr ""
150−
151−#: Dash/DashHome.qml:44
152−msgid "Recent Music"
153−msgstr ""
154−
155−#: Dash/DashHome.qml:47
156−msgid "Videos Popular Online"
157−msgstr ""
158−
159−#: Dash/DashHome.qml:151
160−msgid "Home"
161−msgstr ""
162−
163171#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
164172msgid "Featured"
165173msgstr ""
@@ -200,7 +208,7 @@ msgstr ""
200208msgid "Author:"
201209msgstr ""
202210
203−#: Greeter/Lockscreen.qml:133
211+#: Greeter/Lockscreen.qml:134
204212msgid "Emergency Call"
205213msgstr ""
206214
@@ -237,11 +245,11 @@ msgstr ""
237245msgid "Search"
238246msgstr ""
239247
240−#: plugins/Unity/scope.cpp:298
248+#: plugins/Unity/scope.cpp:317
241249msgid "Sorry, there is nothing that matches your search."
242250msgstr ""
243251
244−#: Shell.qml:399
252+#: Shell.qml:429
245253#, qt-format
246254msgid "Please enter %1"
247255msgstr ""
po/zh_CN.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/zh_TW.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
src/Panel/Indicators/servicefiles/CMakeLists.txt−1
@@ -1,6 +1,5 @@
11file(GLOB INDICATOR_SERVICE_FILES
22 ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.messages
3− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.settings.network
43)
54
65# copy indicators files into build directory for shadow builds
src/Panel/Indicators/servicefiles/com.canonical.settings.networkdeleted−10
@@ -1,10 +0,0 @@
1−[Indicator Service]
2−Name=chewie-network
3−ObjectPath=/com/canonical/settings/network
4−Position=69
5−
6−[phone]
7−ObjectPath=/com/canonical/settings/network/phone
8−
9−[desktop]
10−ObjectPath=/com/canonical/settings/network/desktop
tests/autopilot/unity8/shell/emulators/create_interactive_notification.pynew file+102
@@ -0,0 +1,102 @@
1+#!/usr/bin/env python
2+
3+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4+#
5+# Unity Autopilot Test Suite
6+# Copyright (C) 2012-2013 Canonical
7+#
8+# This program is free software: you can redistribute it and/or modify
9+# it under the terms of the GNU General Public License as published by
10+# the Free Software Foundation, either version 3 of the License, or
11+# (at your option) any later version.
12+#
13+# This program is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+# GNU General Public License for more details.
17+#
18+# You should have received a copy of the GNU General Public License
19+# along with this program. If not, see <http://www.gnu.org/licenses/>.
20+#
21+
22+import argparse
23+from gi.repository import GLib, Notify
24+import signal
25+
26+
27+def action_callback(notification, action_id, data):
28+ print action_id
29+
30+
31+def quit_callback(notification):
32+ loop.quit()
33+
34+
35+if __name__ == '__main__':
36+ parser = argparse.ArgumentParser(
37+ description='Create an interactive notification'
38+ )
39+
40+ parser.add_argument(
41+ '--summary',
42+ help='summary text of the notification',
43+ default='Summary'
44+ )
45+ parser.add_argument(
46+ '--body',
47+ help='body text of the notification',
48+ default='Body'
49+ )
50+ parser.add_argument(
51+ '--icon',
52+ help='path to the icon to display',
53+ default=None
54+ )
55+ parser.add_argument(
56+ '--action',
57+ help='id and label for the callback in the format: id,label',
58+ action='append',
59+ default=[]
60+ )
61+ parser.add_argument(
62+ '--urgency',
63+ help='LOW, NORMAL, CRITICAL',
64+ choices=['LOW', 'NORMAL', 'CRITICAL'],
65+ default='NORMAL'
66+ )
67+ parser.add_argument(
68+ '--hints',
69+ help='list of comma sep options',
70+ action='append',
71+ default=[]
72+ )
73+
74+ args = parser.parse_args()
75+
76+ Notify.init('Interactive Notifications')
77+
78+ notification = Notify.Notification.new(args.summary, args.body, args.icon)
79+
80+ for hint in args.hints:
81+ key, value = hint.split(',', 1)
82+ notification.set_hint_string(key, value)
83+
84+ for action in args.action:
85+ action_id, action_label = action.split(',', 1)
86+ notification.add_action(
87+ action_id,
88+ action_label,
89+ action_callback,
90+ None,
91+ None
92+ )
93+
94+ def signal_handler(signam, frame):
95+ loop.quit()
96+
97+ signal.signal(signal.SIGINT, signal_handler)
98+
99+ loop = GLib.MainLoop.new(None, False)
100+ notification.connect('closed', quit_callback)
101+ notification.show()
102+ loop.run()
tests/autopilot/unity8/shell/tests/__init__.py+20−20
@@ -51,26 +51,25 @@ UNITYSHELL_GSETTINGS_PATH = "/org/compiz/profiles/unity/plugins/unityshell/"
5151UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
5252UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
5353
54−def _get_device_emulation_scenarios():
54+def _get_device_emulation_scenarios(devices='All'):
55+ nexus4 = ('Desktop Nexus 4',
56+ dict(app_width=768, app_height=1280, grid_unit_px=18))
57+ nexus10 = ('Desktop Nexus 10',
58+ dict(app_width=2560, app_height=1600, grid_unit_px=20))
59+ native = ('Native Device',
60+ dict(app_width=0, app_height=0, grid_unit_px=0))
61+
5562 if model() == 'Desktop':
56− return [
57− (
58− 'Desktop Nexus 4',
59− dict(app_width=768, app_height=1280, grid_unit_px=18)
60− ),
61− (
62− 'Desktop Nexus 10',
63− dict(app_width=2560, app_height=1600, grid_unit_px=20)
64− ),
65− ]
63+ if devices == 'All':
64+ return [nexus4, nexus10]
65+ elif devices == 'Nexus4':
66+ return [nexus4]
67+ elif devices == 'Nexus10':
68+ return [nexus10]
69+ else:
70+ raise RuntimeException('Unrecognized device-option "%s" passed.' % devices)
6671 else:
67− return [
68− (
69− 'Native Device',
70− dict(app_width=0, app_height=0, grid_unit_px=0)
71− )
72− ]
73−
72+ return [native]
7473
7574class UnityTestCase(AutopilotTestCase):
7675
@@ -148,7 +147,7 @@ class UnityTestCase(AutopilotTestCase):
148147 divisor = divisor * 2
149148 return divisor
150149
151− def launch_unity(self):
150+ def launch_unity(self, **kwargs):
152151 """Launch the unity shell, return a proxy object for it."""
153152 binary_path = get_binary_path()
154153 lib_path = get_lib_path()
@@ -167,7 +166,8 @@ class UnityTestCase(AutopilotTestCase):
167166 binary_path,
168167 *self.unity_geometry_args,
169168 app_type='qt',
170− emulator_base=UnityEmulatorBase
169+ emulator_base=UnityEmulatorBase,
170+ **kwargs
171171 )
172172 self._set_proxy(app_proxy)
173173
tests/autopilot/unity8/shell/tests/test_notifications.pynew file+737
@@ -0,0 +1,737 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-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 Notifications"""
21+
22+from __future__ import absolute_import
23+
24+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
25+
26+from testtools.matchers import Equals, NotEquals
27+from autopilot.matchers import Eventually
28+
29+from gi.repository import Notify
30+import time
31+import os
32+import logging
33+import signal
34+import subprocess
35+
36+logger = logging.getLogger(__name__)
37+
38+
39+class NotificationsBase(UnityTestCase):
40+ """Base class for all notification tests that provides helper methods."""
41+
42+ scenarios = _get_device_emulation_scenarios('Nexus4')
43+
44+ def _get_icon_path(self, icon_name):
45+ """Given an icons file name returns the full path (either system or
46+ source tree.
47+
48+ Consider the graphics directory as root so for example (runnign tests
49+ from installed unity8-autopilot package):
50+ >>> self.get_icon_path('clock.png')
51+ /usr/share/unity8/graphics/clock.png
52+
53+ >>> self.get_icon_path('applicationIcons/facebook.png')
54+ /usr/share/unity8/graphics/applicationIcons/facebook.png
55+
56+ """
57+ if os.path.abspath(__file__).startswith('/usr/'):
58+ return '/usr/share/unity8/graphics/' + icon_name
59+ else:
60+ return os.path.abspath(
61+ os.getcwd() + "/../../graphics/" + icon_name
62+ )
63+
64+ def _get_notifications_list(self):
65+ main_view = self.main_window.get_qml_view()
66+ return main_view.select_single(
67+ "QQuickListView",
68+ objectName='notificationList'
69+ )
70+
71+ def _assert_notification(
72+ self,
73+ notification,
74+ summary=None,
75+ body=None,
76+ icon=True,
77+ secondary_icon=False,
78+ opacity=None
79+ ):
80+ """Assert that the expected qualities of a notification are as
81+ expected.
82+
83+ """
84+
85+ if summary is not None:
86+ self.assertThat(notification.summary, Eventually(Equals(summary)))
87+
88+ if body is not None:
89+ self.assertThat(notification.body, Eventually(Equals(body)))
90+
91+ if icon:
92+ self.assertThat(notification.iconSource, Eventually(NotEquals("")))
93+ else:
94+ self.assertThat(notification.iconSource, Eventually(Equals("")))
95+
96+ if secondary_icon:
97+ self.assertThat(
98+ notification.secondaryIconSource,
99+ Eventually(NotEquals(""))
100+ )
101+ else:
102+ self.assertThat(
103+ notification.secondaryIconSource,
104+ Eventually(Equals(""))
105+ )
106+
107+ if opacity is not None:
108+ self.assertThat(notification.opacity, Eventually(Equals(opacity)))
109+
110+
111+class InteractiveNotificationBase(NotificationsBase):
112+ """Collection of test for Interactive tests including snap decisions."""
113+
114+ def setUp(self):
115+ super(InteractiveNotificationBase, self).setUp()
116+ # Need to keep track when we launch the notification script.
117+ self._notify_proc = None
118+
119+ def test_interactive(self):
120+ """Interactive notification must react upon click on itself."""
121+ self.launch_unity()
122+ greeter = self.main_window.get_greeter()
123+ greeter.swipe()
124+
125+ notify_list = self._get_notifications_list()
126+
127+ summary = "Interactive notification"
128+ body = "This notification can be clicked on to trigger an action."
129+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
130+ actions = [("action_id", "dummy")]
131+ hints = [
132+ ("x-canonical-switch-to-application", "true"),
133+ (
134+ "x-canonical-secondary-icon",
135+ self._get_icon_path('applicationIcons/phone-app.png')
136+ )
137+ ]
138+
139+ self._create_interactive_notification(
140+ summary,
141+ body,
142+ icon_path,
143+ "NORMAL",
144+ actions,
145+ hints,
146+ )
147+
148+ get_notification = lambda: notify_list.select_single('Notification')
149+ self.assertThat(get_notification, Eventually(NotEquals(None)))
150+ notification = get_notification()
151+
152+ self.touch.tap_object(
153+ notification.select_single(objectName="interactiveArea")
154+ )
155+
156+ self.assert_notification_action_id_was_called('action_id')
157+
158+ def test_sd_incoming_call(self):
159+ """Rejecting a call should make notification expand and offer more options."""
160+ self.launch_unity()
161+ greeter = self.main_window.get_greeter()
162+ greeter.swipe()
163+
164+ notify_list = self._get_notifications_list()
165+
166+ summary = "Incoming call"
167+ body = "Frank Zappa\n+44 (0)7736 027340"
168+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
169+ hints = [
170+ (
171+ "x-canonical-secondary-icon",
172+ self._get_icon_path('applicationIcons/phone-app.png')
173+ ),
174+ ("x-canonical-snap-decisions", "true"),
175+ ]
176+
177+ actions = [
178+ ('action_accept', 'Accept'),
179+ ('action_decline_1', 'Decline'),
180+ ('action_decline_2', '"Can\'t talk now, what\'s up?"'),
181+ ('action_decline_3', '"I call you back."'),
182+ ('action_decline_4', 'Send custom message...'),
183+ ]
184+
185+ self._create_interactive_notification(
186+ summary,
187+ body,
188+ icon_path,
189+ "NORMAL",
190+ actions,
191+ hints
192+ )
193+
194+ get_notification = lambda: notify_list.select_single('Notification')
195+ self.assertThat(get_notification, Eventually(NotEquals(None)))
196+ notification = get_notification()
197+ self._assert_notification(notification, None, None, True, True, 1.0)
198+ initial_height = notification.height
199+ self.touch.tap_object(notification.select_single(objectName="button1"))
200+ self.assertThat(
201+ notification.height,
202+ Eventually(Equals(initial_height +
203+ 3 * notification.select_single(objectName="buttonColumn").spacing +
204+ 3 * notification.select_single(objectName="button4").height)))
205+ self.touch.tap_object(notification.select_single(objectName="button4"))
206+ self.assert_notification_action_id_was_called("action_decline_4")
207+
208+ def _create_interactive_notification(
209+ self,
210+ summary="",
211+ body="",
212+ icon=None,
213+ urgency="NORMAL",
214+ actions=[],
215+ hints=[]
216+ ):
217+ """Create a interactive notification command.
218+
219+ :param summary: Summary text for the notification
220+ :param body: Body text to display in the notification
221+ :param icon: Path string to the icon to use
222+ :param urgency: Urgency string for the noticiation, either: 'LOW',
223+ 'NORMAL', 'CRITICAL'
224+ :param actions: List of tuples containing the 'id' and 'label' for all
225+ the actions to add
226+ :param hint_strings: List of tuples containing the 'name' and value for
227+ setting the hint strings for the notification
228+
229+ """
230+
231+ logger.info(
232+ "Creating snap-decision notification with summary(%s), body(%s) "
233+ "and urgency(%r)",
234+ summary,
235+ body,
236+ urgency
237+ )
238+
239+ script_args = [
240+ '--summary', summary,
241+ '--body', body,
242+ '--urgency', urgency
243+ ]
244+
245+ if icon is not None:
246+ script_args.extend(['--icon', icon])
247+
248+ for hint in hints:
249+ key, value = hint
250+ script_args.extend(['--hint', "%s,%s" % (key, value)])
251+
252+ for action in actions:
253+ action_id, action_label = action
254+ action_string = "%s,%s" % (action_id, action_label)
255+ script_args.extend(['--action', action_string])
256+
257+ python_bin = subprocess.check_output(['which', 'python']).strip()
258+ command = [python_bin, self._get_notify_script()] + script_args
259+ logger.info("Launching snap-decision notification as: %s", command)
260+ self._notify_proc = subprocess.Popen(
261+ command,
262+ stdin=subprocess.PIPE,
263+ stdout=subprocess.PIPE,
264+ stderr=subprocess.PIPE,
265+ close_fds=True,
266+ )
267+
268+ self.addCleanup(self._tidy_up_script_process)
269+
270+ poll_result = self._notify_proc.poll()
271+ if poll_result is not None and self._notify_proc.returncode != 0:
272+ error_output = self._notify_proc.communicate()[1]
273+ raise RuntimeError("Call to script failed with: %s" % error_output)
274+
275+ def _get_notify_script(self):
276+ """Returns the path to the interactive notification creation script."""
277+ file_path = "../../emulators/create_interactive_notification.py"
278+
279+ the_path = os.path.abspath(
280+ os.path.join(__file__, file_path))
281+
282+ return the_path
283+
284+ def _tidy_up_script_process(self):
285+ if self._notify_proc is not None and self._notify_proc.poll() is None:
286+ logger.error("Notification process wasn't killed, killing now.")
287+ os.killpg(self._notify_proc.pid, signal.SIGTERM)
288+
289+ def assert_notification_action_id_was_called(self, action_id, timeout=10):
290+ """Assert that the interactive notification callback of id *action_id*
291+ was called.
292+
293+ :raises AssertionError: If no interactive notification has actually
294+ been created.
295+ :raises AssertionError: When *action_id* does not match the actual
296+ returned.
297+ :raises AssertionError: If no callback was called at all.
298+ """
299+
300+ if self._notify_proc is None:
301+ raise AssertionError("No interactive notification was created.")
302+
303+ for i in xrange(timeout):
304+ self._notify_proc.poll()
305+ if self._notify_proc.returncode is not None:
306+ output = self._notify_proc.communicate()
307+ actual_action_id = output[0].strip("\n")
308+ if actual_action_id != action_id:
309+ raise AssertionError(
310+ "action id '%s' does not match actual returned '%s'"
311+ % (action_id, actual_action_id)
312+ )
313+ else:
314+ return
315+ time.sleep(1)
316+
317+ os.killpg(self._notify_proc.pid, signal.SIGTERM)
318+ self._notify_proc = None
319+ raise AssertionError(
320+ "No callback was called, killing interactivenotification script"
321+ )
322+
323+
324+class EphemeralNotificationsTests(NotificationsBase):
325+ """Collection of tests for Emphemeral notifications (non-interactive.)"""
326+
327+ def setUp(self):
328+ super(EphemeralNotificationsTests, self).setUp()
329+ # Because we are using the Notify library we need to init and un-init
330+ # otherwise we get crashes.
331+ Notify.init("Autopilot Ephemeral Notification Tests")
332+ self.addCleanup(Notify.uninit)
333+
334+ def test_icon_summary_body(self):
335+ """Notification must display the expected summary and body text."""
336+ self.launch_unity()
337+ greeter = self.main_window.get_greeter()
338+ greeter.swipe()
339+
340+ notify_list = self._get_notifications_list()
341+
342+ summary = "Icon-Summary-Body"
343+ body = "Hey pal, what's up with the party next weekend? Will you " \
344+ "join me and Anna?"
345+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
346+ hints = [
347+ (
348+ "x-canonical-secondary-icon",
349+ self._get_icon_path('applicationIcons/phone-app.png')
350+ )
351+ ]
352+
353+ notification = self._create_ephemeral_notification(
354+ summary,
355+ body,
356+ icon_path,
357+ hints,
358+ "NORMAL",
359+ )
360+
361+ notification.show()
362+
363+ get_notification = lambda: notify_list.select_single('Notification')
364+ self.assertThat(get_notification, Eventually(NotEquals(None)))
365+ notification = get_notification()
366+ self._assert_notification(notification, summary, body, True, True, 1.0)
367+
368+ def test_icon_summary(self):
369+ """Notification must display the expected summary and secondary icon."""
370+ self.launch_unity()
371+ greeter = self.main_window.get_greeter()
372+ greeter.swipe()
373+
374+ notify_list = self._get_notifications_list()
375+
376+ summary = "Upload of image completed"
377+ hints = [
378+ (
379+ "x-canonical-secondary-icon",
380+ self._get_icon_path('applicationIcons/facebook.png')
381+ )
382+ ]
383+
384+ notification = self._create_ephemeral_notification(
385+ summary,
386+ None,
387+ None,
388+ hints,
389+ "NORMAL",
390+ )
391+
392+ notification.show()
393+
394+ get_notification = lambda: notify_list.select_single('Notification')
395+ self.assertThat(get_notification, Eventually(NotEquals(None)))
396+ notification = get_notification()
397+ self._assert_notification(
398+ notification,
399+ summary,
400+ None,
401+ False,
402+ True,
403+ 1.0
404+ )
405+
406+ def test_urgency_order(self):
407+ """Notifications must be displayed in order according to their urgency."""
408+ self.launch_unity()
409+ greeter = self.main_window.get_greeter()
410+ greeter.swipe()
411+
412+ notify_list = self._get_notifications_list()
413+
414+ summary_low = 'Low Urgency'
415+ body_low = "No, I'd rather see paint dry, pal *yawn*"
416+ icon_path_low = self._get_icon_path('avatars/amanda.png')
417+
418+ summary_normal = 'Normal Urgency'
419+ body_normal = "Hey pal, what's up with the party next weekend? Will " \
420+ "you join me and Anna?"
421+ icon_path_normal = self._get_icon_path('avatars/funky.png')
422+
423+ summary_critical = 'Critical Urgency'
424+ body_critical = 'Dude, this is so urgent you have no idea :)'
425+ icon_path_critical = self._get_icon_path('avatars/anna_olsson.png')
426+
427+ notification_normal = self._create_ephemeral_notification(
428+ summary_normal,
429+ body_normal,
430+ icon_path_normal,
431+ urgency="NORMAL"
432+ )
433+ notification_normal.show()
434+
435+ notification_low = self._create_ephemeral_notification(
436+ summary_low,
437+ body_low,
438+ icon_path_low,
439+ urgency="LOW"
440+ )
441+ notification_low.show()
442+
443+ notification_critical = self._create_ephemeral_notification(
444+ summary_critical,
445+ body_critical,
446+ icon_path_critical,
447+ urgency="CRITICAL"
448+ )
449+ notification_critical.show()
450+
451+ get_notification = lambda: notify_list.select_single(
452+ 'Notification',
453+ summary=summary_critical
454+ )
455+ self.assertThat(get_notification, Eventually(NotEquals(None)))
456+
457+ notification = get_notification()
458+ self._assert_notification(
459+ notification,
460+ summary_critical,
461+ body_critical,
462+ True,
463+ False,
464+ 1.0
465+ )
466+
467+ get_normal_notification = lambda: notify_list.select_single(
468+ 'Notification',
469+ summary=summary_normal
470+ )
471+ self.assertThat(get_normal_notification, Eventually(NotEquals(None)))
472+ notification = get_normal_notification()
473+ self._assert_notification(
474+ notification,
475+ summary_normal,
476+ body_normal,
477+ True,
478+ False,
479+ 1.0
480+ )
481+
482+ get_low_notification = lambda: notify_list.select_single(
483+ 'Notification',
484+ summary=summary_low
485+ )
486+ self.assertThat(get_low_notification, Eventually(NotEquals(None)))
487+ notification = get_low_notification()
488+ self._assert_notification(
489+ notification,
490+ summary_low,
491+ body_low,
492+ True,
493+ False,
494+ 1.0
495+ )
496+
497+ def test_summary_and_body(self):
498+ """Notification must display the expected summary- and body-text."""
499+ self.launch_unity()
500+ greeter = self.main_window.get_greeter()
501+ greeter.swipe()
502+
503+ notify_list = self._get_notifications_list()
504+
505+ summary = 'Summary-Body'
506+ body = 'This is a superfluous notification'
507+
508+ notification = self._create_ephemeral_notification(summary, body)
509+ notification.show()
510+
511+ get_notification = lambda: notify_list.select_single('Notification')
512+ self.assertThat(get_notification, Eventually(NotEquals(None)))
513+ notification = get_notification()
514+ self._assert_notification(
515+ notification,
516+ summary,
517+ body,
518+ False,
519+ False,
520+ 1.0
521+ )
522+
523+ def test_summary_only(self):
524+ """Notification must display only the expected summary-text."""
525+ self.launch_unity()
526+ greeter = self.main_window.get_greeter()
527+ greeter.swipe()
528+
529+ notify_list = self._get_notifications_list()
530+
531+ summary = 'Summary-Only'
532+
533+ notification = self._create_ephemeral_notification(summary)
534+ notification.show()
535+
536+ get_notification = lambda: notify_list.select_single('Notification')
537+ self.assertThat(get_notification, Eventually(NotEquals(None)))
538+ notification = get_notification()
539+
540+ self._assert_notification(notification, summary, '', False, False, 1.0)
541+
542+ def test_update_notification_same_layout(self):
543+ """Notification must allow updating its contents while being displayed."""
544+ self.launch_unity()
545+ greeter = self.main_window.get_greeter()
546+ greeter.swipe()
547+
548+ notify_list = self._get_notifications_list()
549+
550+ summary = 'Initial notification'
551+ body = 'This is the original content of this notification-bubble.'
552+ icon_path = self._get_icon_path('avatars/funky.png')
553+
554+ notification = self._create_ephemeral_notification(
555+ summary,
556+ body,
557+ icon_path
558+ )
559+ notification.show()
560+
561+ get_notification = lambda: notify_list.select_single('Notification', summary=summary)
562+ self.assertThat(get_notification, Eventually(NotEquals(None)))
563+ self._assert_notification(
564+ get_notification(),
565+ summary,
566+ body,
567+ True,
568+ False,
569+ 1.0
570+ )
571+
572+ summary = 'Updated notification'
573+ body = 'Here the same bubble with new title- and body-text, even ' \
574+ 'the icon can be changed on the update.'
575+ icon_path = self._get_icon_path('avatars/amanda.png')
576+ notification.update(summary, body, icon_path)
577+ notification.show()
578+ self.assertThat(get_notification, Eventually(NotEquals(None)))
579+ self._assert_notification(get_notification(), summary, body)
580+
581+ def test_update_notification_layout_change(self):
582+ """Notification must allow updating its contents and layout while being displayed."""
583+ self.launch_unity()
584+ greeter = self.main_window.get_greeter()
585+ greeter.swipe()
586+
587+ notify_list = self._get_notifications_list()
588+
589+ summary = 'Initial layout'
590+ body = 'This bubble uses the icon-title-body layout with a ' \
591+ 'secondary icon.'
592+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
593+ hint_icon = self._get_icon_path('applicationIcons/phone-app.png')
594+
595+ notification = self._create_ephemeral_notification(
596+ summary,
597+ body,
598+ icon_path
599+ )
600+ notification.set_hint_string(
601+ 'x-canonical-secondary-icon',
602+ hint_icon
603+ )
604+ notification.show()
605+
606+ get_notification = lambda: notify_list.select_single('Notification')
607+ self.assertThat(get_notification, Eventually(NotEquals(None)))
608+ self._assert_notification(
609+ get_notification(),
610+ summary,
611+ body,
612+ True,
613+ True,
614+ 1.0
615+ )
616+
617+ notification.clear_hints()
618+ summary = 'Updated layout'
619+ body = 'After the update we now have a bubble using the title-body ' \
620+ 'layout.'
621+ notification.update(summary, body, None)
622+ notification.show()
623+
624+ self.assertThat(get_notification, Eventually(NotEquals(None)))
625+ self._assert_notification(get_notification(), summary, body, False)
626+
627+ def test_append_hint(self):
628+ """Notification has to accumulate body-text using append-hint."""
629+ self.launch_unity()
630+ greeter = self.main_window.get_greeter()
631+ greeter.swipe()
632+
633+ notify_list = self._get_notifications_list()
634+
635+ summary = 'Cole Raby'
636+ body = 'Hey Bro Coly!'
637+ icon_path = self._get_icon_path('avatars/amanda.png')
638+ body_sum = body
639+ notification = self._create_ephemeral_notification(
640+ summary,
641+ body,
642+ icon_path,
643+ hints=[('x-canonical-append', 'true')]
644+ )
645+
646+ notification.show()
647+
648+ get_notification = lambda: notify_list.select_single('Notification')
649+ self.assertThat(get_notification, Eventually(NotEquals(None)))
650+ notification = get_notification()
651+ self._assert_notification(
652+ notification,
653+ summary,
654+ body_sum,
655+ True,
656+ False,
657+ 1.0
658+ )
659+
660+ bodies = [
661+ 'What\'s up dude?',
662+ 'Did you watch the air-race in Oshkosh last week?',
663+ 'Phil owned the place like no one before him!',
664+ 'Did really everything in the race work according to regulations?',
665+ 'Somehow I think to remember Burt Williams did cut corners and was not punished for this.',
666+ 'Hopefully the referees will watch the videos of the race.',
667+ 'Burt could get fined with US$ 50000 for that rule-violation :)'
668+ ]
669+
670+ for new_body in bodies:
671+ body = new_body
672+ body_sum += '\n' + body
673+ notification = self._create_ephemeral_notification(
674+ summary,
675+ body,
676+ icon_path,
677+ hints=[('x-canonical-append', 'true')]
678+ )
679+ notification.show()
680+ get_notification = lambda: notify_list.select_single(
681+ 'Notification'
682+ )
683+ self.assertThat(get_notification, Eventually(NotEquals(None)))
684+ notification = get_notification()
685+ self._assert_notification(
686+ notification,
687+ summary,
688+ body_sum,
689+ True,
690+ False,
691+ 1.0
692+ )
693+
694+ def _create_ephemeral_notification(
695+ self,
696+ summary="",
697+ body="",
698+ icon=None,
699+ hints=[],
700+ urgency="NORMAL"
701+ ):
702+ """Create an ephemeral (non-interactive) notification
703+
704+ :param summary: Summary text for the notification
705+ :param body: Body text to display in the notification
706+ :param icon: Path string to the icon to use
707+ :param hint_strings: List of tuples containing the 'name' and value
708+ for setting the hint strings for the notification
709+ :param urgency: Urgency string for the noticiation, either: 'LOW',
710+ 'NORMAL', 'CRITICAL'
711+
712+ """
713+ logger.info(
714+ "Creating ephemeral: summary(%s), body(%s), urgency(%r) "
715+ "and Icon(%s)",
716+ summary,
717+ body,
718+ urgency,
719+ icon
720+ )
721+
722+ n = Notify.Notification.new(summary, body, icon)
723+
724+ for hint in hints:
725+ key, value = hint
726+ n.set_hint_string(key, value)
727+ logger.info("Adding hint to notification: (%s, %s)", key, value)
728+ n.set_urgency(self._get_urgency(urgency))
729+
730+ return n
731+
732+ def _get_urgency(self, urgency):
733+ """Translates urgency string to enum."""
734+ _urgency_enums = {'LOW': Notify.Urgency.LOW,
735+ 'NORMAL': Notify.Urgency.NORMAL,
736+ 'CRITICAL': Notify.Urgency.CRITICAL}
737+ return _urgency_enums.get(urgency.upper())
tests/mocks/AccountsService/AccountsService.h+5−6
@@ -20,9 +20,9 @@
2020#ifndef UNITY_MOCK_ACCOUNTSSERVICE_H
2121#define UNITY_MOCK_ACCOUNTSSERVICE_H
2222
23−#include <QtCore/QObject>
24−#include <QtCore/QString>
25−#include <QtCore/QVariant>
23+#include <QObject>
24+#include <QString>
25+#include <QVariant>
2626
2727class AccountsService: public QObject
2828{
@@ -31,9 +31,8 @@ class AccountsService: public QObject
3131public:
3232 explicit AccountsService(QObject *parent = 0);
3333
34−public Q_SLOTS:
35− QVariant getUserProperty(const QString &user, const QString &property);
36− void setUserProperty(const QString &user, const QString &property, const QVariant &value);
34+ Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &property);
35+ Q_INVOKABLE void setUserProperty(const QString &user, const QString &property, const QVariant &value);
3736};
3837
3938#endif
tests/mocks/AccountsService/plugin.cpp+1−1
@@ -20,7 +20,7 @@
2020#include "plugin.h"
2121#include "AccountsService.h"
2222
23−#include <QtQml/QtQml>
23+#include <QtQml>
2424
2525static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
2626{
tests/mocks/AccountsService/plugin.h+2−2
@@ -20,8 +20,8 @@
2020#ifndef MOCK_ACCOUNTSSERVICE_PLUGIN_H
2121#define MOCK_ACCOUNTSSERVICE_PLUGIN_H
2222
23−#include <QtQml/QQmlEngine>
24−#include <QtQml/QQmlExtensionPlugin>
23+#include <QQmlEngine>
24+#include <QQmlExtensionPlugin>
2525
2626class AccountsServicePlugin : public QQmlExtensionPlugin
2727{
tests/mocks/GSettings.1.0/fake_gsettings.h+1−1
@@ -18,7 +18,7 @@
1818#define FAKE_GSETTINGS_H
1919
2020#include <QList>
21−#include <QtQml>
21+#include <QObject>
2222
2323class GSettingsSchemaQml: public QObject
2424{
tests/mocks/GSettings.1.0/plugin.cpp+1−1
@@ -17,7 +17,7 @@
1717#include "plugin.h"
1818#include "fake_gsettings.h"
1919
20−#include <QtQml>
20+#include <QtQml/qqml.h>
2121
2222static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
2323{
tests/mocks/Powerd/Powerd.h+15−2
@@ -20,17 +20,30 @@
2020#define UNITY_MOCK_POWERD_H
2121
2222#include <QtCore/QObject>
23−#include <QtDBus/QDBusInterface>
2423
2524class Powerd: public QObject
2625{
2726 Q_OBJECT
27+ Q_ENUMS(Status)
28+ Q_FLAGS(DisplayFlag DisplayFlags)
2829
2930public:
31+ enum DisplayFlag {
32+ UseProximity = 1, // Use proximity sensor to override screen state
33+ DisableAutoBrightness = 2, // Force autobrightness to be disabled
34+ Bright = 4, // Request the screen to stay bright
35+ };
36+ Q_DECLARE_FLAGS(DisplayFlags, DisplayFlag)
37+
38+ enum Status {
39+ Off,
40+ On,
41+ };
42+
3043 explicit Powerd(QObject *parent = 0);
3144
3245Q_SIGNALS:
33− void powerStateChange(int state);
46+ void displayPowerStateChange(int status, unsigned int flags);
3447};
3548
3649#endif
tests/mocks/Powerd/plugin.cpp+1−1
@@ -19,7 +19,7 @@
1919#include "plugin.h"
2020#include "Powerd.h"
2121
22−#include <QtQml/QtQml>
22+#include <QtQml/qqml.h>
2323
2424static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
2525{
tests/mocks/Ubuntu/DownloadDaemonListener/plugin.cpp−1
@@ -17,7 +17,6 @@
1717#include "plugin.h"
1818#include "MockDownloadTracker.h"
1919
20−#include <QtQml>
2120#include <QtQml/QQmlContext>
2221#include <qqml.h>
2322
tests/mocks/Unity/Indicators/fakeplugin.cpp+1−1
@@ -17,7 +17,7 @@
1717 */
1818
1919// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
2121
2222// self
2323#include "fakeplugin.h"
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+6
@@ -174,3 +174,9 @@ int MockLauncherModel::findApp(const QString &appId)
174174 }
175175 return -1;
176176}
177+
178+void MockLauncherModel::setUser(const QString &username)
179+{
180+ Q_UNUSED(username)
181+ // TODO: implement this...
182+}
tests/mocks/Unity/Launcher/MockLauncherModel.h+1
@@ -43,6 +43,7 @@ public:
4343 Q_INVOKABLE void pin(const QString &appId, int index = -1);
4444 Q_INVOKABLE void requestRemove(const QString &appId);
4545 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
46+ Q_INVOKABLE void setUser(const QString &username);
4647
4748Q_SIGNALS:
4849 void quickListTriggered(const QString &appId, int index);
tests/mocks/Unity/fake_unity_plugin.cpp+2−1
@@ -26,7 +26,8 @@
2626
2727// External
2828#include <glib-object.h>
29−#include <QtQml>
29+
30+#include <QtQml/qqml.h>
3031
3132void FakeUnityPlugin::registerTypes(const char *uri)
3233{
tests/plugins/AccountsService/AccountsServer.h+4−4
@@ -20,10 +20,10 @@
2020#ifndef UNITY_ACCOUNTSSERVER_H
2121#define UNITY_ACCOUNTSSERVER_H
2222
23−#include <QtCore/QObject>
24−#include <QtCore/QString>
25−#include <QtDBus/QDBusContext>
26−#include <QtDBus/QDBusObjectPath>
23+#include <QDBusContext>
24+#include <QDBusObjectPath>
25+#include <QObject>
26+#include <QString>
2727
2828class AccountsServer: public QObject, protected QDBusContext
2929{
tests/plugins/AccountsService/PropertiesServer.h+4−4
@@ -21,10 +21,10 @@
2121#define UNITY_PROPERTIESSERVER_H
2222
2323#include "PropertiesServer.h"
24−#include <QtCore/QObject>
25−#include <QtCore/QString>
26−#include <QtDBus/QDBusContext>
27−#include <QtDBus/QDBusVariant>
24+#include <QDBusContext>
25+#include <QDBusVariant>
26+#include <QObject>
27+#include <QString>
2828
2929class PropertiesServer: public QObject, protected QDBusContext
3030{
tests/plugins/AccountsService/client.cpp+2−2
@@ -18,8 +18,8 @@
1818 */
1919
2020#include "AccountsService.h"
21−#include <QtTest/QSignalSpy>
22−#include <QtTest/QTest>
21+#include <QSignalSpy>
22+#include <QTest>
2323
2424class AccountsServiceTest : public QObject
2525{
tests/plugins/AccountsService/server.cpp+1−1
@@ -21,7 +21,7 @@
2121#include "AccountsServer.h"
2222#include "PropertiesAdaptor.h"
2323#include "PropertiesServer.h"
24−#include <QtCore/QCoreApplication>
24+#include <QCoreApplication>
2525
2626int main(int argc, char *argv[])
2727{
tests/plugins/Unity/Launcher/launcherbackendtest.cpp+2−8
@@ -31,15 +31,12 @@ private Q_SLOTS:
3131 {
3232 LauncherBackend backend(false);
3333
34− auto apps = QStringList() << "one.desktop" << "two" << "/full";
35− backend.setStoredApplications(apps);
34+ backend.setStoredApplications(QStringList() << "relative.path" << "/full/path");
35+ QCOMPARE(backend.storedApplications(), QStringList() << "/usr/share/applications/relative.path" << "/full/path");
3636
3737 QCOMPARE(backend.desktopFile("one.desktop"), QString("/usr/share/applications/one.desktop"));
3838 QCOMPARE(backend.desktopFile("two"), QString("/usr/share/applications/two"));
3939 QCOMPARE(backend.desktopFile("/full"), QString("/full"));
40− QCOMPARE(backend.storedApplications(), QStringList() << "/usr/share/applications/one.desktop" << "/usr/share/applications/two" << "/full");
41−
42− QCOMPARE(backend.desktopFile("full"), QString(""));
4340
4441 QCOMPARE(backend.desktopFile("/usr/share/applications/one.desktop"), QString("/usr/share/applications/one.desktop"));
4542 }
@@ -48,9 +45,6 @@ private Q_SLOTS:
4845 {
4946 LauncherBackend backend(false);
5047
51− auto apps = QStringList() << SRCDIR "/rel-icon.desktop" << SRCDIR "/abs-icon.desktop" << SRCDIR "/no-icon.desktop" << SRCDIR "/no-name.desktop" << SRCDIR "/no-exist.desktop";
52− backend.setStoredApplications(apps);
53−
5448 QCOMPARE(backend.displayName(SRCDIR "/rel-icon.desktop"), QString("Relative Icon"));
5549 QCOMPARE(backend.icon(SRCDIR "/rel-icon.desktop"), QString("image://gicon/rel-icon"));
5650
tests/plugins/Utils/qlimitproxymodeltest.cpp+133−8
@@ -58,10 +58,17 @@ public:
5858 m_roles = roles;
5959 }
6060
61+ void clear()
62+ {
63+ beginRemoveRows(QModelIndex(), 0, m_list.count() - 1);
64+ m_list.clear();
65+ endRemoveRows();
66+ }
67+
6168 bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
6269 beginInsertRows(parent, row, row+count-1);
6370 for (int i=0; i<count; i++) {
64− m_list.insert(i+row, "test"+i);
71+ m_list.insert(i+row, QString::number(i));
6572 }
6673 endInsertRows();
6774 return true;
@@ -88,6 +95,60 @@ private:
8895 QHash<int, QByteArray> m_roles;
8996};
9097
98+class MirrorModel : public QObject
99+{
100+ Q_OBJECT
101+
102+public:
103+ MirrorModel(QAbstractItemModel *model) : m_mirror(model)
104+ {
105+ connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)));
106+ connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)));
107+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
108+ }
109+
110+ void check()
111+ {
112+ QCOMPARE(m_list.count(), m_mirror->rowCount());
113+ for (int i = 0; i < m_list.count(); ++i) {
114+ QCOMPARE(m_list[i], m_mirror->data(m_mirror->index(i, 0)).toString());
115+ }
116+ }
117+
118+private Q_SLOTS:
119+ void rowsInserted(const QModelIndex &parent, int start, int end)
120+ {
121+ QVERIFY(!parent.isValid());
122+ for (int i = start; i <= end; ++i) {
123+ m_list.insert(i, m_mirror->data(m_mirror->index(i, 0)).toString());
124+ }
125+ }
126+
127+ void rowsRemoved(const QModelIndex &parent, int start, int end)
128+ {
129+ QVERIFY(!parent.isValid());
130+ for (int i = end; i >= start; --i) {
131+ m_list.removeAt(i);
132+ }
133+ }
134+
135+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
136+ {
137+ QVERIFY(topLeft.isValid());
138+ QVERIFY(bottomRight.isValid());
139+ QVERIFY(!topLeft.parent().isValid());
140+ QVERIFY(!bottomRight.parent().isValid());
141+ for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
142+ m_list[i] = m_mirror->data(m_mirror->index(i, 0)).toString();
143+ }
144+ }
145+
146+
147+private:
148+ QStringList m_list;
149+ QAbstractItemModel *m_mirror;
150+};
151+
91152class QLimitProxyModelTest : public QObject
92153{
93154 Q_OBJECT
@@ -401,15 +462,10 @@ private Q_SLOTS:
401462 QCOMPARE(spyOnCountChanged.count(), 0);
402463
403464 model.removeRows(2, 2);
404− QCOMPARE(spyOnRowsRemoved.count(), 1);
465+ QCOMPARE(spyOnRowsRemoved.count(), 0);
405466 QCOMPARE(spyOnRowsInserted.count(), 0);
406− QCOMPARE(spyOnCountChanged.count(), 1);
407− arguments = spyOnRowsRemoved.takeFirst();
408− QCOMPARE(arguments.at(1).toInt(), 2);
409− QCOMPARE(arguments.at(2).toInt(), 3);
467+ QCOMPARE(spyOnCountChanged.count(), 0);
410468 QCOMPARE(proxy.rowCount(), 7);
411− spyOnRowsInserted.clear();
412− spyOnCountChanged.clear();
413469
414470 model.removeRows(0, 7);
415471 QCOMPARE(spyOnRowsRemoved.count(), 1);
@@ -479,6 +535,75 @@ private Q_SLOTS:
479535 QCOMPARE(proxy.rowCount(), 5);
480536 QCOMPARE(spyOnCountChanged.count(), 1);
481537 }
538+
539+ void testMirrorModel() {
540+ QLimitProxyModelQML proxy;
541+ MockListModel model;
542+ MirrorModel mirror(&proxy);
543+
544+ proxy.setModel(&model);
545+ proxy.setLimit(5);
546+
547+ // Checks what happens when the model already has
548+ // more items than the limit and we add stuff to
549+ // the front
550+ model.insertRows(0, 7);
551+ mirror.check();
552+ model.insertRows(1, 3);
553+ mirror.check();
554+ model.clear();
555+ mirror.check();
556+
557+ // Checks what happens when the model does
558+ // not has more items than limit but adding
559+ // stuff to its front makes it go past the limit
560+ model.insertRows(0, 3);
561+ mirror.check();
562+ model.insertRows(1, 3);
563+ mirror.check();
564+ model.clear();
565+ mirror.check();
566+
567+ // Checks what happens when the model does
568+ // not has more items than limit and adding
569+ // stuff to its front makes it not go past the limit
570+ model.insertRows(0, 1);
571+ mirror.check();
572+ model.insertRows(1, 3);
573+ mirror.check();
574+ model.clear();
575+ mirror.check();
576+
577+ // Checks what happens when the model already has
578+ // more items than the limit and we remove stuff from
579+ // the front but it still has more than the limit
580+ model.insertRows(0, 10);
581+ mirror.check();
582+ model.removeRows(1, 3);
583+ mirror.check();
584+ model.clear();
585+ mirror.check();
586+
587+ // Checks what happens when the model already has
588+ // more items than the limit and we remove stuff from
589+ // the front but it has less than the limit
590+ model.insertRows(0, 6);
591+ mirror.check();
592+ model.removeRows(1, 3);
593+ mirror.check();
594+ model.clear();
595+ mirror.check();
596+
597+ // Checks what happens when the model has
598+ // less items than the limit and we remove stuff from
599+ // the front
600+ model.insertRows(0, 4);
601+ mirror.check();
602+ model.removeRows(1, 3);
603+ mirror.check();
604+ model.clear();
605+ mirror.check();
606+ }
482607};
483608
484609QTEST_GUILESS_MAIN(QLimitProxyModelTest)
tests/qmltests/CMakeLists.txt+1−1
@@ -28,9 +28,9 @@ set(qmltest_DEFAULT_PROPERTIES "")
2828
2929add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
3030 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
31−add_qml_test(Components DemoOverlay)
3231add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
3332add_qml_test(Components DraggingArea)
33+add_qml_test(Components EdgeDemoOverlay)
3434add_qml_test(Components FilterGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
3535add_qml_test(Components ResponsiveFlowView)
3636add_qml_test(Components ResponsiveGridView)
tests/qmltests/Components/tst_DemoOverlay.qmldeleted−99
@@ -1,99 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU 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 QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import "../../../Components"
21−
22−Item {
23− id: root
24− width: 700
25− height: 500
26−
27− DemoOverlay {
28− id: top
29− edge: "top"
30− title: "Top"
31− text: "Displayed on top left"
32− x: 0
33− y: 0
34− width: parent.width/2
35− height: parent.height/2
36− }
37−
38− DemoOverlay {
39− id: right
40− edge: "right"
41− title: "Right"
42− text: "Displayed on top right"
43− x: parent.width/2
44− y: 0
45− width: parent.width/2
46− height: parent.height/2
47− }
48−
49− DemoOverlay {
50− id: left
51− edge: "left"
52− title: "Left"
53− text: "Displayed on bottom right"
54− x: parent.width/2
55− y: parent.height/2
56− width: parent.width/2
57− height: parent.height/2
58− enabled: false
59− }
60−
61− DemoOverlay {
62− id: bottom
63− edge: "bottom"
64− title: "Bottom"
65− text: "Displayed on bottom left"
66− x: 0
67− y: parent.height/2
68− width: parent.width/2
69− height: parent.height/2
70− }
71−
72− SignalSpy {
73− id: signalSpy
74− target: bottom
75− }
76−
77− UT.UnityTestCase {
78− name: "DemoOverlay"
79− when: windowShown
80−
81− function test_animations() {
82− compare(right.__anim_running, 1)
83−
84− compare(left.__anim_running, 0)
85− left.enabled = true
86− compare(left.__anim_running, 1)
87− }
88−
89− function test_skip() {
90− signalSpy.signalName = "skip"
91− signalSpy.clear()
92− var bottomSkip = findChild(bottom, "skipLabel")
93− mousePress(bottomSkip, 1, 1)
94− mouseRelease(bottomSkip, 1, 1)
95− signalSpy.wait()
96− compare(bottom.enabled, false)
97− }
98− }
99−}
tests/qmltests/Components/tst_EdgeDemoOverlay.qmlnew file+123
@@ -0,0 +1,123 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU 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 QtTest 1.0
19+import Unity.Test 0.1 as UT
20+import "../../../Components"
21+
22+Item {
23+ id: root
24+ width: boxWidth * 3
25+ height: boxHeight * 2
26+
27+ property int boxWidth: 250
28+ property int boxHeight: 250
29+
30+ EdgeDemoOverlay {
31+ id: top
32+ edge: "top"
33+ title: "Top"
34+ text: "Displayed on top left"
35+ anchors.left: parent.left
36+ anchors.top: parent.top
37+ width: boxWidth
38+ height: boxHeight
39+ }
40+
41+ EdgeDemoOverlay {
42+ id: right
43+ edge: "right"
44+ title: "Right"
45+ text: "Displayed on top right"
46+ anchors.right: parent.right
47+ anchors.top: parent.top
48+ width: boxWidth
49+ height: boxHeight
50+ }
51+
52+ EdgeDemoOverlay {
53+ id: left
54+ edge: "left"
55+ title: "Left"
56+ text: "Displayed on bottom right"
57+ anchors.right: parent.right
58+ anchors.bottom: parent.bottom
59+ width: boxWidth
60+ height: boxHeight
61+ available: false
62+ }
63+
64+ EdgeDemoOverlay {
65+ id: bottom
66+ edge: "bottom"
67+ title: "Bottom"
68+ text: "Displayed on bottom left"
69+ anchors.left: parent.left
70+ anchors.bottom: parent.bottom
71+ width: boxWidth
72+ height: boxHeight
73+ }
74+
75+ EdgeDemoOverlay {
76+ id: none
77+ edge: "none"
78+ title: "None"
79+ text: "Displayed on top middle"
80+ anchors.horizontalCenter: parent.horizontalCenter
81+ anchors.top: parent.top
82+ width: boxWidth
83+ height: boxHeight
84+ }
85+
86+ SignalSpy {
87+ id: signalSpy
88+ }
89+
90+ UT.UnityTestCase {
91+ name: "EdgeDemoOverlay"
92+ when: windowShown
93+
94+ function test_animations() {
95+ compare(right.running, true)
96+
97+ compare(left.running, false)
98+ left.available = true
99+ compare(left.running, true)
100+ }
101+
102+ function test_skip() {
103+ signalSpy.target = bottom
104+ signalSpy.signalName = "skip"
105+ signalSpy.clear()
106+ var bottomSkip = findChild(bottom, "skipLabel")
107+ mousePress(bottomSkip, 1, 1)
108+ mouseRelease(bottomSkip, 1, 1)
109+ signalSpy.wait()
110+ compare(bottom.available, false)
111+
112+ // Test that the 'none' edge skips anywhere
113+ signalSpy.target = none
114+ signalSpy.clear()
115+ var backgroundShade = findChild(none, "backgroundShade")
116+ tryCompare(none, "opacity", 1)
117+ mousePress(backgroundShade, 1, 1)
118+ mouseRelease(backgroundShade, 1, 1)
119+ signalSpy.wait()
120+ compare(none.available, false)
121+ }
122+ }
123+}
tests/qmltests/Greeter/tst_Clock.qml+1−1
@@ -70,7 +70,7 @@ Rectangle {
7070 compare(clock.__timerRunning, true, "Timer should be running")
7171 wait(0) // spin event loop to trigger the timer
7272 verify(dateLabel.text !== dateString)
73− if (timeLabel.text == "11:13") wait(60000) // next test will fail at 11:13, wait 1 minute
73+ if (timeLabel.text.indexOf("11:13") != -1) wait(60000) // next test will fail at 11:13, wait 1 minute
7474 verify(timeLabel.text !== timeString)
7575 }
7676
tests/qmltests/Launcher/tst_Launcher.qml+38−14
@@ -277,19 +277,24 @@ Item {
277277
278278 function test_dragndrop_data() {
279279 return [
280− {tag: "startDrag", fullDrag: false},
281− {tag: "fullDrag", fullDrag: true},
280+ {tag: "startDrag", fullDrag: false },
281+ {tag: "fullDrag horizontal", fullDrag: true, orientation: ListView.Horizontal },
282+ {tag: "fullDrag vertical", fullDrag: true, orientation: ListView.Vertical },
282283 ];
283284 }
284285
285286 function test_dragndrop(data) {
286287 revealer.dragLauncherIntoView();
287− var draggedItem = findChild(launcher, "launcherDelegate5")
288+ var draggedItem = findChild(launcher, "launcherDelegate4")
288289 var item0 = findChild(launcher, "launcherDelegate0")
289290 var fakeDragItem = findChild(launcher, "fakeDragItem")
290291 var initialItemHeight = draggedItem.height
291− var item5Name = LauncherModel.get(5).iconName
292− var item4Name = LauncherModel.get(4).iconName
292+ var item4 = LauncherModel.get(4).appId
293+ var item3 = LauncherModel.get(3).appId
294+
295+ var listView = findChild(launcher, "launcherListView");
296+ listView.flick(0, units.gu(200));
297+ tryCompare(listView, "flicking", false);
293298
294299 // Initial state
295300 compare(draggedItem.itemOpacity, 1, "Item's opacity is not 1 at beginning")
@@ -297,28 +302,43 @@ Item {
297302 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
298303
299304 // Doing longpress
300− mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
305+ var currentMouseX = draggedItem.width / 2
306+ var currentMouseY = draggedItem.height / 2
307+ mousePress(draggedItem, currentMouseX, currentMouseY)
301308 // DraggedItem needs to hide and fakeDragItem become visible
302309 tryCompare(draggedItem, "itemOpacity", 0)
303310 tryCompare(fakeDragItem, "visible", true)
304311
305312 // Dragging a bit (> 1.5 gu)
306− mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
313+ currentMouseX -= units.gu(2)
314+ mouseMove(draggedItem, currentMouseX, currentMouseY)
307315 // Other items need to expand and become 0.6 opaque
308316 tryCompare(item0, "angle", 0)
309317 tryCompare(item0, "itemOpacity", 0.6)
310318
311319 if (data.fullDrag) {
312320 // Dragging a bit more
313− mouseMove(draggedItem, -units.gu(15), draggedItem.height / 2, 100)
314− tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
315− tryCompare(draggedItem, "height", units.gu(1))
321+ if (data.orientation == ListView.Horizontal) {
322+ currentMouseX -= units.gu(15)
323+ mouseMove(draggedItem, currentMouseX, currentMouseY, 100)
324+
325+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
326+ tryCompare(draggedItem, "height", units.gu(1))
327+
328+ // Dragging downwards. Item needs to move in the model
329+ currentMouseY -= initialItemHeight * 1.5
330+ mouseMove(draggedItem, currentMouseX, currentMouseY)
331+ } else if (data.orientation == ListView.Vertical) {
332+ currentMouseY -= initialItemHeight * 1.5
333+ mouseMove(draggedItem, currentMouseX, currentMouseY, 100)
334+
335+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
336+ tryCompare(draggedItem, "height", units.gu(1))
337+ }
316338
317− // Dragging downwards. Item needs to move in the model
318− mouseMove(draggedItem, -units.gu(15), -initialItemHeight)
319339 waitForRendering(draggedItem)
320− compare(LauncherModel.get(5).iconName, item4Name)
321− compare(LauncherModel.get(5).iconName, item5Name)
340+ compare(LauncherModel.get(4).appId, item3)
341+ compare(LauncherModel.get(3).appId, item4)
322342 }
323343
324344 // Releasing and checking if initial values are restored
@@ -326,6 +346,10 @@ Item {
326346 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
327347 tryCompare(draggedItem, "itemOpacity", 1)
328348 tryCompare(fakeDragItem, "visible", false)
349+
350+ // Click somewhere in the empty space to make it hide in case it isn't
351+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
352+ revealer.waitUntilLauncherDisappears();
329353 }
330354
331355 function test_quicklist_dismiss() {
tests/qmltests/tst_Shell.qml+7−3
@@ -125,13 +125,17 @@ Item {
125125 var mainApp = ApplicationManager.mainStageFocusedApplication;
126126 tryCompareFunction(function() { return mainApp != null; }, true);
127127
128− // Now suspend
129− Powerd.powerStateChange(0);
128+ // Try to suspend while proximity is engaged...
129+ Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
130+ tryCompare(greeter, "showProgress", 0);
131+
132+ // Now really suspend
133+ Powerd.displayPowerStateChange(Powerd.Off, 0);
130134 tryCompare(greeter, "showProgress", 1);
131135 tryCompare(ApplicationManager, "mainStageFocusedApplication", null);
132136
133137 // And wake up
134− Powerd.powerStateChange(1);
138+ Powerd.displayPowerStateChange(Powerd.On, 0);
135139 tryCompare(ApplicationManager, "mainStageFocusedApplication", mainApp);
136140 tryCompare(greeter, "showProgress", 1);
137141 }
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+1−1
@@ -19,7 +19,7 @@
1919
2020#include <QtCore/QObject>
2121#include <QtTest/QTest>
22−#include <QtQml/QtQml>
22+#include <QtQml/qqml.h>
2323
2424class TouchEventSequenceWrapper : public QObject
2525{
tests/utils/modules/Unity/Test/UnityTestCase.qml+21−2
@@ -69,9 +69,28 @@ TestCase {
6969 }
7070
7171
72− // Find an object with the given name recursively, starting
73− // at the given object "obj"
72+ // Find an object with the given name in the children tree of "obj"
7473 function findChild(obj,objectName) {
74+ var childs = new Array(0);
75+ childs.push(obj)
76+ while (childs.length > 0) {
77+ if (childs[0].objectName == objectName) {
78+ return childs[0]
79+ }
80+ for (var i in childs[0].children) {
81+ childs.push(childs[0].children[i])
82+ }
83+ childs.splice(0, 1);
84+ }
85+ return undefined;
86+ }
87+
88+ // Find an object with the given name in the children tree of "obj"
89+ // Including invisible children like animations, timers etc.
90+ // Note: you should use findChild if you're not sure you need this
91+ // as this tree is much bigger and might contain stuff that goes
92+ // away randomly.
93+ function findInvisibleChild(obj,objectName) {
7594 var childs = new Array(0);
7695 childs.push(obj)
7796 while (childs.length > 0) {