on xenial help sign in
merge launcher-items
c47c68bMichael Zanetti
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 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17+import Powerd 0.1
17import QtQuick 2.0
18import QtQuick 2.0
18import QtGraphicalEffects 1.0
19import QtGraphicalEffects 1.0
19import Ubuntu.Application 0.1
20import Ubuntu.Application 0.1
20import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
21
22
22−Item {
23+Showable {
23 id: overlay
24 id: overlay
24
25
25 /*
26 /*
@@ -30,38 +31,69 @@ Item {
30 /*
31 /*
31 * This is the header displayed, like "Right edge".
32 * This is the header displayed, like "Right edge".
32 */
33 */
33− property string title
34+ property alias title: titleLabel.text
34
35
35 /*
36 /*
36 * This is the block of text displayed below the header.
37 * This is the block of text displayed below the header.
37 */
38 */
38− property string text
39+ property alias text: textLabel.text
39
40
40 /*
41 /*
41 * This is the text for the skip button.
42 * This is the text for the skip button.
42 */
43 */
43− property string skipText: i18n.tr("Skip intro")
44+ property alias skipText: skipLabel.text
44
45
45 /*
46 /*
46− * Whether this demo is allowed to run.
47+ * Whether this demo is running currently.
47 */
48 */
48− property bool enabled: true
49+ readonly property bool active: available && visible
49
50
50 /*
51 /*
51− * Whether this demo is running currently.
52+ * Whether animations are paused.
52 */
53 */
53− readonly property bool active: enabled && visible
54+ property alias paused: wholeAnimation.paused
54
55
55− property color __orange: Qt.hsla(16.0/360.0, 0.83, 0.47, 1.0)
56+ /*
56− property color __orange_transparent: Qt.hsla(16.0/360.0, 0.83, 0.47, 0.4)
57+ * Whether animations are running.
57− property int __edge_margin: units.gu(4)
58+ */
58− property int __text_margin: units.gu(3)
59− property int __anim_running: wholeAnimation.running // for testing
59+ readonly property alias running: wholeAnimation.running
60
60
61 signal skip()
61 signal skip()
62
62
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+
63 Rectangle {
94 Rectangle {
64− id: backgroundShade
95+ objectName: "backgroundShade"
96+
65 anchors.fill: parent
97 anchors.fill: parent
66 color: "black"
98 color: "black"
67 opacity: 0.8
99 opacity: 0.8
@@ -70,7 +102,7 @@ Item {
70 MouseArea {
102 MouseArea {
71 anchors.fill: parent
103 anchors.fill: parent
72 enabled: overlay.edge == "none" && overlay.opacity == 1.0
104 enabled: overlay.edge == "none" && overlay.opacity == 1.0
73− onClicked: fadeOutAnimation.running = true
105+ onClicked: overlay.doSkip()
74 }
106 }
75 }
107 }
76
108
@@ -85,10 +117,10 @@ Item {
85 Column {
117 Column {
86 id: labelGroup
118 id: labelGroup
87 layer.enabled: true // otherwise the underlining on "Skip intro" jumps
119 layer.enabled: true // otherwise the underlining on "Skip intro" jumps
88− spacing: overlay.__text_margin
120+ spacing: units.gu(3)
89
121
90 anchors {
122 anchors {
91− margins: overlay.__edge_margin
123+ margins: d.edgeMargin
92 left: parent.left
124 left: parent.left
93 top: overlay.edge == "bottom" ? undefined : parent.top
125 top: overlay.edge == "bottom" ? undefined : parent.top
94 bottom: overlay.edge == "bottom" ? parent.bottom : undefined
126 bottom: overlay.edge == "bottom" ? parent.bottom : undefined
@@ -96,7 +128,6 @@ Item {
96
128
97 Label {
129 Label {
98 id: titleLabel
130 id: titleLabel
99− text: overlay.title
100 fontSize: "x-large"
131 fontSize: "x-large"
101 width: units.gu(25)
132 width: units.gu(25)
102 wrapMode: Text.WordWrap
133 wrapMode: Text.WordWrap
@@ -104,7 +135,6 @@ Item {
104
135
105 Label {
136 Label {
106 id: textLabel
137 id: textLabel
107− text: overlay.text
108 width: units.gu(25)
138 width: units.gu(25)
109 wrapMode: Text.WordWrap
139 wrapMode: Text.WordWrap
110 }
140 }
@@ -112,14 +142,14 @@ Item {
112 Label {
142 Label {
113 id: skipLabel
143 id: skipLabel
114 objectName: "skipLabel"
144 objectName: "skipLabel"
115− text: overlay.skipText
145+ text: i18n.tr("Skip intro")
116− color: overlay.__orange
146+ color: UbuntuColors.orange
117 fontSize: "small"
147 fontSize: "small"
118 font.underline: true
148 font.underline: true
119
149
120 MouseArea {
150 MouseArea {
121 anchors.fill: parent
151 anchors.fill: parent
122− onClicked: fadeOutAnimation.running = true
152+ onClicked: overlay.doSkip()
123 }
153 }
124 }
154 }
125 }
155 }
@@ -132,11 +162,11 @@ Item {
132 gradient: Gradient {
162 gradient: Gradient {
133 GradientStop {
163 GradientStop {
134 position: 0.0
164 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
136 }
166 }
137 GradientStop {
167 GradientStop {
138 position: 1.0
168 position: 1.0
139− color: "#00000000"
169+ color: "transparent"
140 }
170 }
141 }
171 }
142 anchors.fill: parent
172 anchors.fill: parent
@@ -165,22 +195,9 @@ Item {
165 }
195 }
166 }
196 }
167
197
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−
182 SequentialAnimation {
198 SequentialAnimation {
183 id: wholeAnimation
199 id: wholeAnimation
200+ objectName: "wholeAnimation"
184 running: overlay.active
201 running: overlay.active
185
202
186 ParallelAnimation {
203 ParallelAnimation {
@@ -199,20 +216,20 @@ Item {
199 }
216 }
200 from: {
217 from: {
201 if (overlay.edge == "right") {
218 if (overlay.edge == "right") {
202− return overlay.__edge_margin + units.gu(3)
219+ return d.edgeMargin + units.gu(3)
203 } else {
220 } else {
204− return overlay.__edge_margin - units.gu(3)
221+ return d.edgeMargin - units.gu(3)
205 }
222 }
206 }
223 }
207− to: overlay.__edge_margin
224+ to: d.edgeMargin
208− duration: overlay.edge == "none" ? 0 : 1000
225+ duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
209 }
226 }
210 StandardAnimation {
227 StandardAnimation {
211 target: overlay
228 target: overlay
212 property: "opacity"
229 property: "opacity"
213 from: 0.0
230 from: 0.0
214 to: 1.0
231 to: 1.0
215− duration: 1000
232+ duration: UbuntuAnimation.SleepyDuration
216 }
233 }
217 }
234 }
218
235
@@ -222,7 +239,7 @@ Item {
222 property string prop: (overlay.edge == "left" || overlay.edge == "right") ? "x" : "y"
239 property string prop: (overlay.edge == "left" || overlay.edge == "right") ? "x" : "y"
223 property double endVal: units.dp(5) * ((overlay.edge == "left" || overlay.edge == "top") ? 1 : -1)
240 property double endVal: units.dp(5) * ((overlay.edge == "left" || overlay.edge == "top") ? 1 : -1)
224 property double maxGlow: units.dp(20)
241 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
226
243
227 ParallelAnimation {
244 ParallelAnimation {
228 StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: 0; to: hintAnimation.endVal; duration: hintAnimation.duration }
245 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 {
38 metadata: ""
38 metadata: ""
39 }
39 }
40 ListElement {
40 ListElement {
41− uri: "application:///usr/share/applications/galery-app.desktop"
41+ uri: "application:///usr/share/applications/gallery-app.desktop"
42 icon: "../../graphics/applicationIcons/gallery.png"
42 icon: "../../graphics/applicationIcons/gallery.png"
43 category: 0
43 category: 0
44 mimetype: "application/x-desktop"
44 mimetype: "application/x-desktop"
Dash/GenericScopeView.qml+6
@@ -80,6 +80,12 @@ ScopeView {
80 item.model = results
80 item.model = results
81 }
81 }
82 item.objectName = categoryId
82 item.objectName = categoryId
83+ if (item.expandable) {
84+ var shouldFilter = index != categoryView.expandedIndex;
85+ if (shouldFilter != item.filter) {
86+ item.filter = shouldFilter;
87+ }
88+ }
83 }
89 }
84
90
85 Connections {
91 Connections {
Launcher/FoldingLauncherDelegate.qml+35−24
@@ -26,21 +26,21 @@ LauncherDelegate {
26 if (index == 0 || index == priv.listView.count-1) {
26 if (index == 0 || index == priv.listView.count-1) {
27 if (priv.distanceFromEdge < 0) {
27 if (priv.distanceFromEdge < 0) {
28 // proportion equation: distanceFromTopEdge : angle = totalUnfoldedHeight/2 : maxAngle
28 // 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
30 }
30 }
31 return 0; // Don't fold first/last item as long as inside the view
31 return 0; // Don't fold first/last item as long as inside the view
32 }
32 }
33
33
34− // Are we in the already completely outside the flickable? Fold for the last 5 degrees
34+ // We reached the folded area... fold the last 5 degrees
35− if (priv.distanceFromEdge < 0) {
35+ if (priv.overlapWithFoldedArea > 0) {
36− // proportion equation: -distanceFromTopEdge : angle = totalUnfoldedHeight : 5
36+ // proportion equation: overlap : x = height : 5
37− return Math.max(-maxAngle, (priv.distanceFromEdge * 5 / priv.listView.foldingAreaHeight) - (maxAngle-5)) * priv.orientationFlag
37+ return ((maxAngle - 5) + (priv.overlapWithFoldedArea * 5 / priv.foldingAreaHeight)) * -priv.orientationFlag
38 }
38 }
39
39
40 // We are overlapping with the folding area, fold the icon to maxAngle - 5 degrees
40 // We are overlapping with the folding area, fold the icon to maxAngle - 5 degrees
41 if (priv.overlapWithFoldingArea > 0) {
41 if (priv.overlapWithFoldingArea > 0) {
42 // proportion equation: overlap: totalHeight = angle : (maxAngle - 5)
42 // 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;
44 }
44 }
45 return 0;
45 return 0;
46 }
46 }
@@ -61,9 +61,9 @@ LauncherDelegate {
61 return (-priv.distanceFromEdge - (root.height - effectiveHeight)) * priv.orientationFlag;
61 return (-priv.distanceFromEdge - (root.height - effectiveHeight)) * priv.orientationFlag;
62 }
62 }
63
63
64− // We're touching the edge, move slower than the actual flicking speed.
64+ // We stopped folding, move slower than the actual flicking speed.
65− if (priv.distanceFromEdge < 0) {
65+ if (priv.overlapWithFoldedArea > 0) {
66− return (Math.abs(priv.distanceFromEdge) * priv.totalEffectiveHeight / priv.totalUnfoldedHeight) * priv.orientationFlag
66+ return (priv.overlapWithFoldedArea * priv.totalEffectiveHeight / (priv.totalUnfoldedHeight + priv.listView.foldingStopHeight)) * priv.orientationFlag;
67 }
67 }
68 return 0;
68 return 0;
69 }
69 }
@@ -72,22 +72,23 @@ LauncherDelegate {
72 // First/last items are special
72 // First/last items are special
73 if (index == 0 || index == priv.listView.count-1) {
73 if (index == 0 || index == priv.listView.count-1) {
74 if (priv.distanceFromEdge < 0) {
74 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)
75+ // Fade from 1 to 0 while traversing a distance of 2*foldingAreaHeight
76+ // proportion equation: 0.5 : x = -2*foldingAreaHeight : distance
76− return 1.0 - (-priv.distanceFromEdge / (priv.listView.foldingAreaHeight * 3))
77+ return 1 + (priv.distanceFromEdge / (priv.foldingAreaHeight * 2))
77 }
78 }
78 return 1; // Don't make first/last item transparent as long as inside the view
79 return 1; // Don't make first/last item transparent as long as inside the view
79 }
80 }
80
81
81− // Are we already completely outside the flickable? Fade from 0.75 to 0 in 2 items height
82+ // Did we stop folding? Fade out to 0 in 2*foldingAreaHeight
82− if (priv.distanceFromEdge < 0) {
83+ if (priv.overlapWithFoldedArea > 0) {
83− // proportion equation: -distanceFromEdge : 1-opacity = totalUnfoldedHeight : 0.75
84+ // overlap : foldingAreaHeight = opacity : 0.75
84− return 0.75 - (-priv.distanceFromEdge * 0.75 / (priv.totalUnfoldedHeight*2))
85+ return 0.75 - (priv.overlapWithFoldedArea * 0.75 / (priv.foldingAreaHeight * 2));
85 }
86 }
86
87
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
88 if (priv.overlapWithFoldingArea > 0) {
89 if (priv.overlapWithFoldingArea > 0) {
89 // proportion equation: overlap : totalHeight = 1-opacity : 0.25
90 // 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)
91 }
92 }
92 return 1;
93 return 1;
93 }
94 }
@@ -95,19 +96,27 @@ LauncherDelegate {
95 brightness: {
96 brightness: {
96 // First/last items are special
97 // First/last items are special
97 if (index == 0 || index == priv.listView.count-1) {
98 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
98 if (priv.distanceFromEdge < 0) {
106 if (priv.distanceFromEdge < 0) {
99− return -(-priv.distanceFromEdge / (priv.listView.foldingAreaHeight * 3))
107+ return -0.3 * (-priv.distanceFromEdge / (priv.foldingAreaHeight))
100 }
108 }
101 return 0;
109 return 0;
102 }
110 }
103− // Are we already completely outside the flickable? Fade from 0.7 to 0 in 2 items height
111+
104− if (priv.distanceFromEdge < 0) {
112+ // We stopped folding? Stop brightness change at 0.3
105− return -0.3 - (-priv.distanceFromEdge * 0.1 / (priv.totalUnfoldedHeight*2))
113+ if (priv.overlapWithFoldedArea > 0) {
114+ return -0.3;
106 }
115 }
107
116
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
109 if (priv.overlapWithFoldingArea > 0) {
118 if (priv.overlapWithFoldingArea > 0) {
110− return - (priv.overlapWithFoldingArea * 0.3 / priv.listView.foldingAreaHeight)
119+ return - (priv.overlapWithFoldingArea * 0.3 / priv.listView.foldingStartHeight);
111 }
120 }
112 return 0;
121 return 0;
113 }
122 }
@@ -127,7 +136,9 @@ LauncherDelegate {
127 property real distanceFromEdge: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? distanceFromBottomEdge : distanceFromTopEdge
136 property real distanceFromEdge: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? distanceFromBottomEdge : distanceFromTopEdge
128 property real orientationFlag: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? -1 : 1
137 property real orientationFlag: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? -1 : 1
129
138
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
131 }
142 }
132
143
133}
144}
Launcher/Launcher.qml+5−3
@@ -26,9 +26,11 @@ Item {
26
26
27 property bool available: true // can be used to disable all interactions
27 property bool available: true // can be used to disable all interactions
28
28
29− property int panelWidth: units.gu(8.5)
29+ property int panelWidth: units.gu(8)
30 property int dragAreaWidth: units.gu(1)
30 property int dragAreaWidth: units.gu(1)
31+ property int minimizeDistance: panelWidth * 2.5
31− property real progress: dragArea.dragging && dragArea.touchX > panel.width ? dragArea.touchX : 0
32+ property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
33+ (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
32
34
33 readonly property bool shown: panel.x > -panel.width
35 readonly property bool shown: panel.x > -panel.width
34
36
@@ -207,7 +209,7 @@ Item {
207 if (!dragging) {
209 if (!dragging) {
208 if (distance > panel.width / 2) {
210 if (distance > panel.width / 2) {
209 root.switchToNextState("visible")
211 root.switchToNextState("visible")
210− if (distance > panel.width * 2) {
212+ if (distance > minimizeDistance) {
211 root.dash()
213 root.dash()
212 }
214 }
213 } else {
215 } else {
Launcher/LauncherDelegate.qml+2−2
@@ -80,7 +80,7 @@ Item {
80 top: parent.top
80 top: parent.top
81 margins: -units.dp(1)
81 margins: -units.dp(1)
82 }
82 }
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)))
84 height: units.gu(3)
84 height: units.gu(3)
85 source: "graphics/notification.sci"
85 source: "graphics/notification.sci"
86 visible: root.count > 0
86 visible: root.count > 0
@@ -89,7 +89,7 @@ Item {
89 id: countLabel
89 id: countLabel
90 text: root.count
90 text: root.count
91 anchors.centerIn: parent
91 anchors.centerIn: parent
92− width: units.gu(6)
92+ width: root.itemWidth - units.gu(1)
93 horizontalAlignment: Text.AlignHCenter
93 horizontalAlignment: Text.AlignHCenter
94 elide: Text.ElideRight
94 elide: Text.ElideRight
95 color: "white"
95 color: "white"
Launcher/LauncherPanel.qml+59−17
@@ -62,7 +62,7 @@ Item {
62 z: 1
62 z: 1
63 Image {
63 Image {
64 objectName: "dashItem"
64 objectName: "dashItem"
65− width: units.gu(5.5)
65+ width: units.gu(5)
66 height: width
66 height: width
67 anchors.centerIn: parent
67 anchors.centerIn: parent
68 source: "graphics/home.png"
68 source: "graphics/home.png"
@@ -113,14 +113,22 @@ Item {
113 property int extensionSize: itemHeight * 3
113 property int extensionSize: itemHeight * 3
114
114
115 // The height of the area where icons start getting folded
115 // The height of the area where icons start getting folded
116− property int foldingAreaHeight: itemHeight * 0.75
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
117− property int itemWidth: width
119+ property int itemWidth: units.gu(7)
118− property int itemHeight: width * 7.5 / 8
120+ property int itemHeight: units.gu(6.5)
119 property int clickFlickSpeed: units.gu(60)
121 property int clickFlickSpeed: units.gu(60)
120 property int draggedIndex: dndArea.draggedIndex
122 property int draggedIndex: dndArea.draggedIndex
121 property real realContentY: contentY - originY + topMargin
123 property real realContentY: contentY - originY + topMargin
122 property int realItemHeight: itemHeight + spacing
124 property int realItemHeight: itemHeight + spacing
123
125
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+
124 displaced: Transition {
132 displaced: Transition {
125 NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
133 NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
126 }
134 }
@@ -136,9 +144,8 @@ Item {
136 count: model.count
144 count: model.count
137 progress: model.progress
145 progress: model.progress
138 inverted: root.inverted
146 inverted: root.inverted
139− highlighted: dragging && index === root.highlightIndex
140 z: -Math.abs(offset)
147 z: -Math.abs(offset)
141− maxAngle: 60
148+ maxAngle: 55
142 property bool dragging: false
149 property bool dragging: false
143
150
144 ThinDivider {
151 ThinDivider {
@@ -202,10 +209,26 @@ Item {
202 UbuntuNumberAnimation { properties: "angle,offset" }
209 UbuntuNumberAnimation { properties: "angle,offset" }
203 },
210 },
204 Transition {
211 Transition {
212+ id: draggingTransition
205 from: "selected"
213 from: "selected"
206 to: "dragging"
214 to: "dragging"
215+ SequentialAnimation {
216+ PropertyAction { target: launcherListView; property: "draggingTransitionRunning"; value: true }
217+ ParallelAnimation {
207− UbuntuNumberAnimation { properties: "height" }
218+ UbuntuNumberAnimation { properties: "height" }
208− NumberAnimation { target: dropIndicator; properties: "opacity"; duration: UbuntuAnimation.FastDuration }
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+ }
209 },
232 },
210 Transition {
233 Transition {
211 from: "dragging"
234 from: "dragging"
@@ -213,7 +236,9 @@ Item {
213 NumberAnimation { target: dropIndicator; properties: "opacity"; duration: UbuntuAnimation.FastDuration }
236 NumberAnimation { target: dropIndicator; properties: "opacity"; duration: UbuntuAnimation.FastDuration }
214 NumberAnimation { properties: "itemOpacity"; duration: UbuntuAnimation.BriskDuration }
237 NumberAnimation { properties: "itemOpacity"; duration: UbuntuAnimation.BriskDuration }
215 SequentialAnimation {
238 SequentialAnimation {
239+ ScriptAction { script: if (index == launcherListView.count-1) launcherListView.flick(0, -launcherListView.clickFlickSpeed); }
216 UbuntuNumberAnimation { properties: "height" }
240 UbuntuNumberAnimation { properties: "height" }
241+ ScriptAction { script: if (index == launcherListView.count-1) launcherListView.flick(0, -launcherListView.clickFlickSpeed); }
217 PropertyAction { target: dndArea; property: "postDragging"; value: false }
242 PropertyAction { target: dndArea; property: "postDragging"; value: false }
218 PropertyAction { target: dndArea; property: "draggedIndex"; value: -1 }
243 PropertyAction { target: dndArea; property: "draggedIndex"; value: -1 }
219 }
244 }
@@ -235,7 +260,7 @@ Item {
235 property int draggedIndex: -1
260 property int draggedIndex: -1
236 property var selectedItem
261 property var selectedItem
237 property bool preDragging: false
262 property bool preDragging: false
238− property bool dragging: selectedItem !== undefined && selectedItem.dragging
263+ property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging
239 property bool postDragging: false
264 property bool postDragging: false
240 property int startX
265 property int startX
241 property int startY
266 property int startY
@@ -243,13 +268,17 @@ Item {
243
268
244 onPressed: {
269 onPressed: {
245 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
270 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
246− selectedItem.highlighted = true
247 }
271 }
248
272
249 onClicked: {
273 onClicked: {
250 var index = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
274 var index = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
251 var clickedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
275 var clickedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
252
276
277+ // Check if we actually clicked an item or only at the spacing in between
278+ if (clickedItem === null) {
279+ return;
280+ }
281+
253 // First/last item do the scrolling at more than 12 degrees
282 // First/last item do the scrolling at more than 12 degrees
254 if (index == 0 || index == launcherListView.count - 1) {
283 if (index == 0 || index == launcherListView.count - 1) {
255 if (clickedItem.angle > 12) {
284 if (clickedItem.angle > 12) {
@@ -273,7 +302,6 @@ Item {
273 }
302 }
274
303
275 onCanceled: {
304 onCanceled: {
276− selectedItem.highlighted = false
277 selectedItem = undefined;
305 selectedItem = undefined;
278 preDragging = false;
306 preDragging = false;
279 postDragging = false;
307 postDragging = false;
@@ -287,7 +315,10 @@ Item {
287 draggedIndex = -1;
315 draggedIndex = -1;
288 }
316 }
289
317
290− selectedItem.highlighted = false
318+ if (!selectedItem) {
319+ return;
320+ }
321+
291 selectedItem.dragging = false;
322 selectedItem.dragging = false;
292 selectedItem = undefined;
323 selectedItem = undefined;
293 preDragging = false;
324 preDragging = false;
@@ -299,6 +330,9 @@ Item {
299 if (droppedIndex >= launcherListView.count - 2 && postDragging) {
330 if (droppedIndex >= launcherListView.count - 2 && postDragging) {
300 launcherListView.flick(0, -launcherListView.clickFlickSpeed);
331 launcherListView.flick(0, -launcherListView.clickFlickSpeed);
301 }
332 }
333+ if (droppedIndex == 0 && postDragging) {
334+ launcherListView.flick(0, launcherListView.clickFlickSpeed);
335+ }
302 }
336 }
303
337
304 onPressAndHold: {
338 onPressAndHold: {
@@ -376,8 +410,12 @@ Item {
376 }
410 }
377
411
378 if (newIndex >= 0 && newIndex < launcherListView.count) {
412 if (newIndex >= 0 && newIndex < launcherListView.count) {
413+ if (launcherListView.draggingTransitionRunning) {
414+ launcherListView.scheduledMoveTo = newIndex
415+ } else {
379− launcherListView.model.move(draggedIndex, newIndex)
416+ launcherListView.model.move(draggedIndex, newIndex)
380− draggedIndex = newIndex
417+ draggedIndex = newIndex
418+ }
381 }
419 }
382 }
420 }
383 }
421 }
@@ -414,8 +452,7 @@ Item {
414 height: itemHeight
452 height: itemHeight
415 width: itemWidth
453 width: itemWidth
416 rotation: root.rotation
454 rotation: root.rotation
417− highlighted: true
418− itemOpacity: 0.8
455+ itemOpacity: 0.9
419
456
420 function flatten() {
457 function flatten() {
421 fakeDragItemAnimation.start();
458 fakeDragItemAnimation.start();
@@ -439,6 +476,7 @@ Item {
439 property var model
476 property var model
440 property string appId
477 property string appId
441 contentWidth: quickListColumn.width
478 contentWidth: quickListColumn.width
479+ foregroundStyle: QuicklistForegroundStyle {}
442
480
443 // FIXME: There's a bug in the Popover positioning that it covers the item in case it is rotated.
481 // FIXME: There's a bug in the Popover positioning that it covers the item in case it is rotated.
444 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1204470
482 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1204470
@@ -454,6 +492,7 @@ Item {
454 height: childrenRect.height
492 height: childrenRect.height
455
493
456 Repeater {
494 Repeater {
495+ id: popoverRepeater
457 model: popover.model
496 model: popover.model
458
497
459 ListItems.Standard {
498 ListItems.Standard {
@@ -464,8 +503,11 @@ Item {
464 if (!model.clickable) {
503 if (!model.clickable) {
465 return;
504 return;
466 }
505 }
467− LauncherModel.quickListActionInvoked(appId, index);
468 PopupUtils.close(popover);
506 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;
469 }
511 }
470 }
512 }
471 }
513 }
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 {
27 Component.onCompleted: load()
27 Component.onCompleted: load()
28
28
29 indicatorData : {
29 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+ },
30 'indicator-bluetooth' : {
36 'indicator-bluetooth' : {
31 'title': 'Bluetooth',
37 'title': 'Bluetooth',
32 'description' : '',
38 'description' : '',
@@ -49,7 +55,7 @@ Indicators.IndicatorsModel {
49 'title': 'Networks',
55 'title': 'Networks',
50 'description' : '',
56 'description' : '',
51 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
57 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
52− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
58+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage2.qml'
53 },
59 },
54 'indicator-power' : {
60 'indicator-power' : {
55 'title': 'Battery',
61 'title': 'Battery',
@@ -68,14 +74,6 @@ Indicators.IndicatorsModel {
68 'description' : '',
74 'description' : '',
69 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
75 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
70 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
76 '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'
79 }
77 }
80 }
78 }
81}
79}
Shell.qml+32−138
@@ -21,7 +21,6 @@ import Ubuntu.Components 0.1
21import Ubuntu.Gestures 0.1
21import Ubuntu.Gestures 0.1
22import Unity.Launcher 0.1
22import Unity.Launcher 0.1
23import LightDM 0.1 as LightDM
23import LightDM 0.1 as LightDM
24−import AccountsService 0.1
25import Powerd 0.1
24import Powerd 0.1
26import "Dash"
25import "Dash"
27import "Greeter"
26import "Greeter"
@@ -61,19 +60,6 @@ FocusScope {
61 }
60 }
62 }
61 }
63
62
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−
77 property ListModel searchHistory: SearchHistoryModel {}
63 property ListModel searchHistory: SearchHistoryModel {}
78
64
79 property var applicationManager: ApplicationManagerWrapper {}
65 property var applicationManager: ApplicationManagerWrapper {}
@@ -185,10 +171,14 @@ FocusScope {
185 // Whether the underlay is fully covered by opaque UI elements.
171 // Whether the underlay is fully covered by opaque UI elements.
186 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
172 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
187
173
174+ readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
175+ || (sideStage.applications && sideStage.applications.count > 0))
176+
188 // Whether the user should see the topmost application surface (if there's one at all).
177 // Whether the user should see the topmost application surface (if there's one at all).
189− property bool applicationSurfaceShouldBeSeen:
178+ readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
190− (mainStage.applications && mainStage.applications.count > 0)
191− && (!stages.fullyHidden && !mainStage.usingScreenshots)
179+ && !mainStage.usingScreenshots // but want sideStage animating over app surface
180+
181+
192
182
193 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
183 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
194 // through the translucent parts of the shell surface.
184 // through the translucent parts of the shell surface.
@@ -215,7 +205,7 @@ FocusScope {
215 available: !greeter.shown && !lockscreen.shown
205 available: !greeter.shown && !lockscreen.shown
216 hides: [stages, launcher, panel.indicators]
206 hides: [stages, launcher, panel.indicators]
217 shown: disappearingAnimationProgress !== 1.0
207 shown: disappearingAnimationProgress !== 1.0
218− enabled: disappearingAnimationProgress === 0.0 && !leftEdgeDemo.active && !topEdgeDemo.active && !finalEdgeDemo.active
208+ enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
219 // FIXME: unfocus all applications when going back to the dash
209 // FIXME: unfocus all applications when going back to the dash
220 onEnabledChanged: {
210 onEnabledChanged: {
221 if (enabled) {
211 if (enabled) {
@@ -242,50 +232,6 @@ FocusScope {
242 // greeterRevealer.animatedProgress are not animated
232 // greeterRevealer.animatedProgress are not animated
243 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
233 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
244 }
234 }
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− }
289 }
235 }
290
236
291 Item {
237 Item {
@@ -529,9 +475,6 @@ FocusScope {
529
475
530 onUnlocked: greeter.hide()
476 onUnlocked: greeter.hide()
531 onSelected: {
477 onSelected: {
532− // Update edge demo hint
533− var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole)
534− shell.showEdgeDemo = AccountsService.getUserProperty(user, "demo-edges")
535 // Update launcher items for new user
478 // Update launcher items for new user
536 LauncherModel.setUser(user);
479 LauncherModel.setUser(user);
537 }
480 }
@@ -541,34 +484,6 @@ FocusScope {
541 launcher.tease();
484 launcher.tease();
542 }
485 }
543 }
486 }
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− }
572 }
487 }
573
488
574 InputFilterArea {
489 InputFilterArea {
@@ -602,13 +517,22 @@ FocusScope {
602 }
517 }
603 }
518 }
604
519
605− onPowerStateChange: {
520+ onDisplayPowerStateChange: {
606− if (state == 0) { // suspend
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) {
607 powerConnection.setFocused(false);
524 powerConnection.setFocused(false);
608 greeter.show();
525 greeter.show();
609− } else if (state == 1) { // active
526+ } else if (status == Powerd.On) {
610 powerConnection.setFocused(true);
527 powerConnection.setFocused(true);
611 }
528 }
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+ }
612 }
536 }
613 }
537 }
614
538
@@ -623,7 +547,7 @@ FocusScope {
623 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
547 indicatorsMenuWidth: parent.width > units.gu(60) ? units.gu(40) : parent.width
624 indicators {
548 indicators {
625 hides: [launcher]
549 hides: [launcher]
626− available: !rightEdgeDemo.active && !leftEdgeDemo.active && !finalEdgeDemo.active
550+ available: edgeDemo.panelEnabled
627 }
551 }
628 fullscreenMode: shell.fullscreenMode
552 fullscreenMode: shell.fullscreenMode
629 searchVisible: !greeter.shown && !lockscreen.shown
553 searchVisible: !greeter.shown && !lockscreen.shown
@@ -632,24 +556,6 @@ FocusScope {
632 anchors.fill: parent
556 anchors.fill: parent
633 blockInput: panel.indicators.shown
557 blockInput: panel.indicators.shown
634 }
558 }
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− }
653 }
559 }
654
560
655 Hud {
561 Hud {
@@ -658,7 +564,7 @@ FocusScope {
658 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
564 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
659 height: parent.height
565 height: parent.height
660
566
661− available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown && !topEdgeDemo.active
567+ available: !greeter.shown && !panel.indicators.shown && !lockscreen.shown && edgeDemo.dashEnabled
662 shown: false
568 shown: false
663 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
569 showAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: 0; easing.type: Easing.Linear }
664 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
570 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
@@ -716,7 +622,7 @@ FocusScope {
716 anchors.bottom: parent.bottom
622 anchors.bottom: parent.bottom
717 width: parent.width
623 width: parent.width
718 dragAreaWidth: shell.edgeSize
624 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
720 onDashItemSelected: {
626 onDashItemSelected: {
721 greeter.hide()
627 greeter.hide()
722 // Animate if moving between application and dash
628 // Animate if moving between application and dash
@@ -774,27 +680,6 @@ FocusScope {
774 }
680 }
775 }
681 }
776
682
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−
798 focus: true
683 focus: true
799
684
800 InputFilterArea {
685 InputFilterArea {
@@ -846,4 +731,13 @@ FocusScope {
846 rotation: -45
731 rotation: -45
847 scale: Math.min(parent.width, parent.height) / width
732 scale: Math.min(parent.width, parent.height) / width
848 }
733 }
734+
735+ EdgeDemo {
736+ id: edgeDemo
737+ greeter: greeter
738+ launcher: launcher
739+ dash: dash
740+ indicators: panel.indicators
741+ overlay: overlay
742+ }
849}
743}
build+7−3
@@ -40,11 +40,14 @@ install_dependencies() {
40 echo "Installing Unity 8 dependencies.."
40 echo "Installing Unity 8 dependencies.."
41 sudo apt-get install devscripts \
41 sudo apt-get install devscripts \
42 equivs \
42 equivs \
43+ gsettings-desktop-schemas \
43 qmenumodel-qml \
44 qmenumodel-qml \
44− qtdeclarative5-ubuntu-ui-toolkit-plugin \
45 qtdeclarative5-dee-plugin \
45 qtdeclarative5-dee-plugin \
46+ qtdeclarative5-gsettings1.0 \
47+ qtdeclarative5-ubuntu-ui-toolkit-plugin \
48+ unity-notifications-impl \
49+ qtdeclarative5-xmllistmodel-plugin \
46 ubuntu-mobile-icons \
50 ubuntu-mobile-icons \
47− unity-notifications-impl-1 \
48 unity-scope-home \
51 unity-scope-home \
49 unity-lens-applications \
52 unity-lens-applications \
50 || exit 5
53 || exit 5
@@ -56,7 +59,8 @@ mk_build_deps() {
56
59
57if [ -f "/usr/bin/ninja" ] ; then
60if [ -f "/usr/bin/ninja" ] ; then
58 GENERATOR="-G Ninja"
61 GENERATOR="-G Ninja"
62+ # Ninja does not need -j, it parallelizes automatically.
59− BUILD_COMMAND="ninja -j$NUM_JOBS"
63+ BUILD_COMMAND="ninja"
60else
64else
61 GENERATOR=
65 GENERATOR=
62 BUILD_COMMAND="make -j$NUM_JOBS"
66 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+
1unity8 (7.81.3+13.10.20130820.2-0ubuntu1) saucy; urgency=low
172unity8 (7.81.3+13.10.20130820.2-0ubuntu1) saucy; urgency=low
2
173
3 [ Michael Zanetti ]
174 [ Michael Zanetti ]
debian/control+2−1
@@ -21,7 +21,7 @@ Build-Depends: cmake,
21 libsigc++-2.0-dev,
21 libsigc++-2.0-dev,
22 libunity-api-dev (>= 7.80.3),
22 libunity-api-dev (>= 7.80.3),
23 libunity-core-6.0-dev (<< 7.80),
23 libunity-core-6.0-dev (<< 7.80),
24− libunity-dev (>= 7.0.3),
24+ libunity-dev (>= 7.0.12),
25 libunity-scopes-json-def-phone,
25 libunity-scopes-json-def-phone,
26 libupstart-dev,
26 libupstart-dev,
27 libusermetricsoutput1-dev,
27 libusermetricsoutput1-dev,
@@ -62,6 +62,7 @@ Description: Indicators client test application
62
62
63Package: unity8
63Package: unity8
64Architecture: any
64Architecture: any
65+Provides: indicator-renderer,
65Depends: gsettings-desktop-schemas,
66Depends: gsettings-desktop-schemas,
66 qmenumodel-qml (>= 0.2.7),
67 qmenumodel-qml (>= 0.2.7),
67 qtdeclarative5-dee-plugin,
68 qtdeclarative5-dee-plugin,
debian/rules+5
@@ -16,6 +16,11 @@ override_dh_auto_clean:
16 dh_auto_clean
16 dh_auto_clean
17 rm -rf builddir
17 rm -rf builddir
18
18
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+
19override_dh_install:
24override_dh_install:
20 # install autopilot tests
25 # install autopilot tests
21 cd tests/autopilot; \
26 cd tests/autopilot; \
main.cpp−1
@@ -18,7 +18,6 @@
18 */
18 */
19
19
20// Qt
20// Qt
21−#include <QtQml>
22#include <QtQuick/QQuickView>
21#include <QtQuick/QQuickView>
23#include <QtGui/QIcon>
22#include <QtGui/QIcon>
24#include <QtGui/QGuiApplication>
23#include <QtGui/QGuiApplication>
plugins/AccountsService/AccountsService.cpp+8−4
@@ -17,19 +17,23 @@
17 */
17 */
18
18
19#include "AccountsService.h"
19#include "AccountsService.h"
20−#include <QtDBus/QDBusConnection>
20+#include <QDBusConnection>
21+#include <QDBusConnectionInterface>
21−#include <QtDBus/QDBusMessage>
22+#include <QDBusMessage>
22−#include <QtDBus/QDBusVariant>
23+#include <QDBusVariant>
23
24
24AccountsService::AccountsService(QObject* parent)
25AccountsService::AccountsService(QObject* parent)
25 : QObject(parent),
26 : QObject(parent),
26 accounts_manager(NULL),
27 accounts_manager(NULL),
27 users()
28 users()
28{
29{
30+ auto connection = QDBusConnection::SM_BUSNAME();
31+ auto interface = connection.interface();
32+ interface->startService("org.freedesktop.Accounts");
29 accounts_manager = new QDBusInterface("org.freedesktop.Accounts",
33 accounts_manager = new QDBusInterface("org.freedesktop.Accounts",
30 "/org/freedesktop/Accounts",
34 "/org/freedesktop/Accounts",
31 "org.freedesktop.Accounts",
35 "org.freedesktop.Accounts",
32− QDBusConnection::SM_BUSNAME(), this);
36+ connection, this);
33}
37}
34
38
35QVariant AccountsService::getUserProperty(const QString &user, const QString &property)
39QVariant AccountsService::getUserProperty(const QString &user, const QString &property)
plugins/AccountsService/AccountsService.h+7−9
@@ -19,10 +19,10 @@
19#ifndef UNITY_ACCOUNTSSERVICE_H
19#ifndef UNITY_ACCOUNTSSERVICE_H
20#define UNITY_ACCOUNTSSERVICE_H
20#define UNITY_ACCOUNTSSERVICE_H
21
21
22−#include <QtDBus/QDBusInterface>
22+#include <QDBusInterface>
23−#include <QtCore/QMap>
23+#include <QMap>
24−#include <QtCore/QObject>
24+#include <QObject>
25−#include <QtCore/QString>
25+#include <QString>
26
26
27class AccountsService: public QObject
27class AccountsService: public QObject
28{
28{
@@ -31,14 +31,12 @@ class AccountsService: public QObject
31public:
31public:
32 explicit AccountsService(QObject *parent = 0);
32 explicit AccountsService(QObject *parent = 0);
33
33
34−public Q_SLOTS:
35− QVariant getUserProperty(const QString &user, const QString &property);
34+ Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &property);
36− void setUserProperty(const QString &user, const QString &property, const QVariant &value);
35+ Q_INVOKABLE void setUserProperty(const QString &user, const QString &property, const QVariant &value);
37
36
38−private Q_SLOTS:
37+private:
39 QDBusInterface *getUserInterface(const QString &user);
38 QDBusInterface *getUserInterface(const QString &user);
40
39
41−private:
42 QDBusInterface *accounts_manager;
40 QDBusInterface *accounts_manager;
43 QMap<QString, QDBusInterface *> users;
41 QMap<QString, QDBusInterface *> users;
44};
42};
plugins/AccountsService/CMakeLists.txt+1−3
@@ -12,9 +12,7 @@ qt5_use_modules(AccountsService-qml DBus Qml)
12export_qmlfiles(AccountsService AccountsService)
12export_qmlfiles(AccountsService AccountsService)
13export_qmlplugin(AccountsService 0.1 AccountsService TARGETS AccountsService-qml)
13export_qmlplugin(AccountsService 0.1 AccountsService TARGETS AccountsService-qml)
14
14
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")
18set(POLKIT_DATA_DIR "${CMAKE_INSTALL_PREFIX}/share/polkit-1")
16set(POLKIT_DATA_DIR "${CMAKE_INSTALL_PREFIX}/share/polkit-1")
19set(DBUS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/dbus-1/interfaces")
17set(DBUS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/dbus-1/interfaces")
20set(ACCOUNTS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/accountsservice/interfaces")
18set(ACCOUNTS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/accountsservice/interfaces")
plugins/AccountsService/com.canonical.unity.AccountsService.xml+2−2
@@ -16,8 +16,8 @@
16 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
16 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
17 </property>
17 </property>
18
18
19− <property name="launcher-items" type="as" access="readwrite">
19+ <property name="launcher-items" type="aa{sv}" access="readwrite">
20− <annotation name="org.freedesktop.Accounts.DefaultValue" value="[]"/>
20+ <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
21 </property>
21 </property>
22
22
23 </interface>
23 </interface>
plugins/AccountsService/plugin.cpp+3−1
@@ -19,7 +19,8 @@
19#include "plugin.h"
19#include "plugin.h"
20#include "AccountsService.h"
20#include "AccountsService.h"
21
21
22−#include <QtQml/QtQml>
22+#include <QDBusMetaType>
23+#include <QtQml>
23
24
24static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25{
26{
@@ -31,5 +32,6 @@ static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
31void AccountsServicePlugin::registerTypes(const char *uri)
32void AccountsServicePlugin::registerTypes(const char *uri)
32{
33{
33 Q_ASSERT(uri == QLatin1String("AccountsService"));
34 Q_ASSERT(uri == QLatin1String("AccountsService"));
35+ qDBusRegisterMetaType<QList<QVariantMap>>();
34 qmlRegisterSingletonType<AccountsService>(uri, 0, 1, "AccountsService", service_provider);
36 qmlRegisterSingletonType<AccountsService>(uri, 0, 1, "AccountsService", service_provider);
35}
37}
plugins/AccountsService/plugin.h+2−2
@@ -19,8 +19,8 @@
19#ifndef ACCOUNTSSERVICE_PLUGIN_H
19#ifndef ACCOUNTSSERVICE_PLUGIN_H
20#define ACCOUNTSSERVICE_PLUGIN_H
20#define ACCOUNTSSERVICE_PLUGIN_H
21
21
22−#include <QtQml/QQmlEngine>
22+#include <QQmlEngine>
23−#include <QtQml/QQmlExtensionPlugin>
23+#include <QQmlExtensionPlugin>
24
24
25class AccountsServicePlugin : public QQmlExtensionPlugin
25class AccountsServicePlugin : public QQmlExtensionPlugin
26{
26{
plugins/HudClient/qmldir+1
@@ -1,2 +1,3 @@
1+module HudClient
1plugin HudClientQml
2plugin HudClientQml
2typeinfo plugin.qmltypes
3typeinfo plugin.qmltypes
plugins/LightDM/plugin.cpp+1−1
@@ -25,7 +25,7 @@
25#include <QLightDM/UsersModel>
25#include <QLightDM/UsersModel>
26
26
27#include <QtCore/QAbstractItemModel>
27#include <QtCore/QAbstractItemModel>
28−#include <QtQml>
28+#include <QtQml/qqml.h>
29
29
30static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
30static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
31{
31{
plugins/Powerd/Powerd.cpp+2−2
@@ -30,7 +30,7 @@ Powerd::Powerd(QObject* parent)
30 powerd->connection().connect("com.canonical.powerd",
30 powerd->connection().connect("com.canonical.powerd",
31 "/com/canonical/powerd",
31 "/com/canonical/powerd",
32 "com.canonical.powerd",
32 "com.canonical.powerd",
33− "SysPowerStateChange",
33+ "DisplayPowerStateChange",
34 this,
34 this,
35− SIGNAL(powerStateChange(int)));
35+ SIGNAL(displayPowerStateChange(int, unsigned int)));
36}
36}
plugins/Powerd/Powerd.h+15−1
@@ -26,12 +26,26 @@
26class Powerd: public QObject
26class Powerd: public QObject
27{
27{
28 Q_OBJECT
28 Q_OBJECT
29+ Q_ENUMS(Status)
30+ Q_FLAGS(DisplayFlag DisplayFlags)
29
31
30public:
32public:
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+
31 explicit Powerd(QObject *parent = 0);
45 explicit Powerd(QObject *parent = 0);
32
46
33Q_SIGNALS:
47Q_SIGNALS:
34− void powerStateChange(int state);
48+ void displayPowerStateChange(int status, unsigned int flags);
35
49
36private:
50private:
37 QDBusInterface *powerd;
51 QDBusInterface *powerd;
plugins/Powerd/plugin.cpp+1−1
@@ -20,7 +20,7 @@
20#include "plugin.h"
20#include "plugin.h"
21#include "Powerd.h"
21#include "Powerd.h"
22
22
23−#include <QtQml/QtQml>
23+#include <QtQml/qqml.h>
24
24
25static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
26{
26{
plugins/Ubuntu/DownloadDaemonListener/plugin.cpp+1−1
@@ -22,7 +22,7 @@
22#include "plugin.h"
22#include "plugin.h"
23#include "DownloadTracker.h"
23#include "DownloadTracker.h"
24
24
25−#include <QtQml>
25+#include <QtQml/qqml.h>
26#include <QtQml/QQmlContext>
26#include <QtQml/QQmlContext>
27
27
28
28
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -12,6 +12,7 @@ find_package(Qt5Core REQUIRED)
12find_package(Qt5Quick REQUIRED)
12find_package(Qt5Quick REQUIRED)
13
13
14add_subdirectory(qml)
14add_subdirectory(qml)
15+add_subdirectory(Network)
15
16
16include_directories(
17include_directories(
17 ${CMAKE_CURRENT_SOURCE_DIR}
18 ${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)
229 case IndicatorsModelRole::Description:
229 case IndicatorsModelRole::Description:
230 return "";
230 return "";
231 case IndicatorsModelRole::WidgetSource:
231 case IndicatorsModelRole::WidgetSource:
232− return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorWidget.qml";
232+ return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorWidget2.qml";
233 case IndicatorsModelRole::PageSource:
233 case IndicatorsModelRole::PageSource:
234− return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorPage.qml";
234+ return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorPage2.qml";
235 }
235 }
236 return QVariant();
236 return QVariant();
237}
237}
plugins/Unity/Indicators/plugin.cpp+1−1
@@ -18,7 +18,7 @@
18 */
18 */
19
19
20// Qt
20// Qt
21−#include <QtQml>
21+#include <QtQml/qqml.h>
22
22
23// self
23// self
24#include "plugin.h"
24#include "plugin.h"
plugins/Unity/Indicators/qml/BaseMenuItem.qml−4
@@ -25,14 +25,10 @@ ListItem.Standard {
25 id: baseMenu
25 id: baseMenu
26
26
27 property bool menuActivated: false
27 property bool menuActivated: false
28− property QtObject menu
29
28
30 signal selectMenu()
29 signal selectMenu()
31 signal deselectMenu()
30 signal deselectMenu()
32
31
33− signal activate()
34− signal changeState(var value)
35−
36 showDivider: false
32 showDivider: false
37 __foregroundColor: "#e8e1d0" // FIXME not in palette yet
33 __foregroundColor: "#e8e1d0" // FIXME not in palette yet
38
34
plugins/Unity/Indicators/qml/ButtonMenuItem.qml+4−9
@@ -24,14 +24,14 @@ import Unity.Indicators 0.1 as Indicators
24
24
25BaseMenuItem {
25BaseMenuItem {
26 id: menuItem
26 id: menuItem
27− objectName: menuAction.name
28 implicitHeight: units.gu(7)
27 implicitHeight: units.gu(7)
29− enabled: menuAction.active
28+
29+ property alias text: button.text
30+
31+ signal activate()
30
32
31 Button {
33 Button {
32 id: button
34 id: button
33− enabled: menuAction.enabled
34− text: menu ? menu.label : ""
35 anchors.centerIn: parent
35 anchors.centerIn: parent
36 height: units.gu(4)
36 height: units.gu(4)
37 width: units.gu(16)
37 width: units.gu(16)
@@ -41,9 +41,4 @@ BaseMenuItem {
41 menuItem.activate();
41 menuItem.activate();
42 }
42 }
43 }
43 }
44−
45− Indicators.MenuAction {
46− id: menuAction
47− menu: menuItem.menu
48− }
49}
44}
plugins/Unity/Indicators/qml/FramedMenuItem.qml−2
@@ -23,10 +23,8 @@ import Ubuntu.Components 0.1
23import Ubuntu.Components.ListItems 0.1 as ListItem
23import Ubuntu.Components.ListItems 0.1 as ListItem
24
24
25BaseMenuItem {
25BaseMenuItem {
26− text: menu && menu.label ? menu.label : ""
27 implicitHeight: units.gu(7)
26 implicitHeight: units.gu(7)
28
27
29− icon: menu ? menu.icon : ""
30 iconFrame: false
28 iconFrame: false
31
29
32 HLine {
30 HLine {
plugins/Unity/Indicators/qml/IndicatorPage.qml+4−9
@@ -96,18 +96,13 @@ IndicatorBase {
96 }
96 }
97 if (item.hasOwnProperty("menu")) {
97 if (item.hasOwnProperty("menu")) {
98 item.menu = Qt.binding(function() { return model; });
98 item.menu = Qt.binding(function() { return model; });
99−
100− item.activate.connect(onActivateMenu);
101− item.changeState.connect(onChangeState);
102 }
99 }
103 }
100 }
104
101
105− function onActivateMenu(value) {
102+ Binding {
106− mainMenu.model.activate(index, value);
103+ target: item ? item : null
107− }
104+ property: "objectName"
108−
105+ value: model.action
109− function onChangeState(value) {
110− mainMenu.model.changeState(index, value);
111 }
106 }
112 }
107 }
113 }
108 }
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 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Unity.Indicators 0.1 as Indicators
21import Unity.Indicators 0.1 as Indicators
22+import Unity.Indicators.Network 0.1 as ICNetwork
23+import QMenuModel 0.1 as QMenuModel
22
24
23Item {
25Item {
24 id: menuFactory
26 id: menuFactory
@@ -38,25 +40,161 @@ Item {
38
40
39 "com.canonical.unity.slider" : sliderMenu,
41 "com.canonical.unity.slider" : sliderMenu,
40 "com.canonical.unity.switch" : switchMenu,
42 "com.canonical.unity.switch" : switchMenu,
43+
44+ "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
45+ "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
41 }
46 }
42
47
43 Component {
48 Component {
44 id: sliderMenu;
49 id: sliderMenu;
45 Indicators.SliderMenuItem {
50 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+
46 Component.onCompleted: {
69 Component.onCompleted: {
47 model.loadExtendedAttributes(modelIndex, {'min-value': 'double',
70 model.loadExtendedAttributes(modelIndex, {'min-value': 'double',
48 'max-value': 'double',
71 'max-value': 'double',
49 'min-icon': 'icon',
72 'min-icon': 'icon',
50 'max-icon': 'icon'});
73 'max-icon': 'icon'});
51 }
74 }
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+ }
52 }
94 }
53 }
95 }
54− Component { id: buttonMenu; Indicators.ButtonMenuItem {} }
96+
55 Component { id: divMenu; Indicators.DivMenuItem {} }
97 Component { id: divMenu; Indicators.DivMenuItem {} }
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;
56− Component { id: sectionMenu; Indicators.SectionMenuItem {} }
112+ Indicators.SectionMenuItem {
113+ property QtObject menu: null
114+
115+ text: menu && menu.label ? menu.label : ""
116+ }
117+ }
118+
119+ Component {
120+ id: progressMenu;
57− Component { id: progressMenu; Indicators.ProgressMenuItem {} }
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;
58− Component { id: standardMenu; Indicators.StandardMenuItem {} }
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;
59− Component { id: switchMenu; Indicators.SwitchMenuItem {} }
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+ }
60
198
61 function load(modelData) {
199 function load(modelData) {
62 if (modelData.type !== undefined) {
200 if (modelData.type !== undefined) {
plugins/Unity/Indicators/qml/ProgressMenuItem.qml+1−8
@@ -23,10 +23,8 @@ import Unity.Indicators 0.1 as Indicators
23
23
24FramedMenuItem {
24FramedMenuItem {
25 id: menuItem
25 id: menuItem
26− objectName: menuAction.name
27− enabled: menuAction.active
28
26
29− property int value : menuAction.state
27+ property int value : 0.0
30
28
31 control: Label {
29 control: Label {
32 id: progress
30 id: progress
@@ -37,9 +35,4 @@ FramedMenuItem {
37 fontSize: "medium"
35 fontSize: "medium"
38 color: "#e8e1d0"
36 color: "#e8e1d0"
39 }
37 }
40−
41− Indicators.MenuAction {
42− id: menuAction
43− menu: menuItem.menu
44− }
45}
38}
plugins/Unity/Indicators/qml/SectionMenuItem.qml+3−3
@@ -22,15 +22,15 @@ import Ubuntu.Components 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem
22import Ubuntu.Components.ListItems 0.1 as ListItem
23
23
24BaseMenuItem {
24BaseMenuItem {
25− property string label: menu && menu.label ? menu.label : ""
25+ id: menuItem
26+ property alias text: header.text
26 property bool busy: false
27 property bool busy: false
27
28
28− implicitHeight: label !== "" ? header.height : 0
29+ implicitHeight: text !== "" ? header.height : 0
29
30
30 ListItem.Header {
31 ListItem.Header {
31 id: header
32 id: header
32
33
33− text: label
34 height: units.gu(4)
34 height: units.gu(4)
35 anchors {
35 anchors {
36 left: parent.left
36 left: parent.left
plugins/Unity/Indicators/qml/SliderMenuItem.qml+8−23
@@ -24,17 +24,20 @@ import Unity.Indicators 0.1 as Indicators
24
24
25FramedMenuItem {
25FramedMenuItem {
26 id: menuItem
26 id: menuItem
27− objectName: menuAction.name
28− enabled: menuAction.active
29
27
30 property alias minimumValue: slider.minimumValue
28 property alias minimumValue: slider.minimumValue
31 property alias maximumValue: slider.maximumValue
29 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
33
34
34 property QtObject d: QtObject {
35 property QtObject d: QtObject {
35 property bool enableValueConnection: true
36 property bool enableValueConnection: true
36 }
37 }
37
38
39+ signal changeState(real value)
40+
38 onValueChanged: {
41 onValueChanged: {
39 // TODO: look into adding a component to manage bi-directional bindings.
42 // TODO: look into adding a component to manage bi-directional bindings.
40 var oldEnable = d.enableValueConnection
43 var oldEnable = d.enableValueConnection
@@ -58,7 +61,6 @@ FramedMenuItem {
58 anchors.verticalCenter: row.verticalCenter
61 anchors.verticalCenter: row.verticalCenter
59 height: units.gu(4)
62 height: units.gu(4)
60 width: height
63 width: height
61− source: menu.ext.minIcon
62 }
64 }
63
65
64 Components.Slider {
66 Components.Slider {
@@ -75,19 +77,8 @@ FramedMenuItem {
75 value = menuItem.value
77 value = menuItem.value
76 }
78 }
77
79
78− // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
80+ minimumValue: 0.0
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: {
81+ maximumValue: 0.1
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− }
91
82
92 // FIXME - to be deprecated in Ubuntu.Components.
83 // FIXME - to be deprecated in Ubuntu.Components.
93 // Use this to disable the label, since there is not way to do it on the component.
84 // Use this to disable the label, since there is not way to do it on the component.
@@ -109,12 +100,6 @@ FramedMenuItem {
109 anchors.verticalCenter: row.verticalCenter
100 anchors.verticalCenter: row.verticalCenter
110 height: units.gu(4)
101 height: units.gu(4)
111 width: height
102 width: height
112− source: menu.ext.maxIcon
113 }
103 }
114 }
104 }
115−
116− Indicators.MenuAction {
117− id: menuAction
118− menu: menuItem.menu
119− }
120}
105}
plugins/Unity/Indicators/qml/StandardMenuItem.qml+5−10
@@ -23,16 +23,16 @@ import Unity.Indicators 0.1 as Indicators
23
23
24FramedMenuItem {
24FramedMenuItem {
25 id: menuItem
25 id: menuItem
26− objectName: menuAction.name
27− enabled: menuAction.active
28
26
29− readonly property bool checkable: menu ? (menu.isCheck || menu.isRadio) : false
27+ property bool checkable: false
30− readonly property bool checked: checkable ? menu.isToggled : false
28+ property bool checked: false
29+
30+ signal activate()
31
31
32 // FIXME : need a radio button from sdk
32 // FIXME : need a radio button from sdk
33 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211866
33 // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211866
34 onCheckableChanged: {
34 onCheckableChanged: {
35− if (menu && checkable) {
35+ if (checkable) {
36 icon = checkComponent.createObject(menuItem);
36 icon = checkComponent.createObject(menuItem);
37 }
37 }
38 }
38 }
@@ -68,9 +68,4 @@ FramedMenuItem {
68 }
68 }
69 }
69 }
70 }
70 }
71−
72− Indicators.MenuAction {
73− id: menuAction
74− menu: menuItem.menu
75− }
76}
71}
plugins/Unity/Indicators/qml/SwitchMenuItem.qml+3−8
@@ -24,10 +24,10 @@ import Unity.Indicators 0.1 as Indicators
24
24
25FramedMenuItem {
25FramedMenuItem {
26 id: menuItem
26 id: menuItem
27− objectName: menuAction.name
28− enabled: menuAction.enabled
29
27
30− readonly property bool checked: menu ? menu.isToggled : false
28+ property bool checked: false
29+
30+ signal activate()
31
31
32 onCheckedChanged: {
32 onCheckedChanged: {
33 // Can't rely on binding. Checked is assigned on click.
33 // Can't rely on binding. Checked is assigned on click.
@@ -50,9 +50,4 @@ FramedMenuItem {
50 menuItem.activate();
50 menuItem.activate();
51 }
51 }
52 }
52 }
53−
54− Indicators.MenuAction {
55− id: menuAction
56− menu: menuItem.menu
57− }
58}
53}
plugins/Unity/Indicators/qml/qmldir−1
@@ -11,7 +11,6 @@ HLine 0.1 HLine.qml
11IndicatorBase 0.1 IndicatorBase.qml
11IndicatorBase 0.1 IndicatorBase.qml
12IndicatorPage 0.1 IndicatorPage.qml
12IndicatorPage 0.1 IndicatorPage.qml
13IndicatorWidget 0.1 IndicatorWidget.qml
13IndicatorWidget 0.1 IndicatorWidget.qml
14−MenuAction 0.1 MenuAction.qml
15MenuActionBinding 0.1 MenuActionBinding.qml
14MenuActionBinding 0.1 MenuActionBinding.qml
16MenuItemFactory 0.1 MenuItemFactory.qml
15MenuItemFactory 0.1 MenuItemFactory.qml
17ProgressMenuItem 0.1 ProgressMenuItem.qml
16ProgressMenuItem 0.1 ProgressMenuItem.qml
plugins/Unity/IndicatorsLegacy/Network/CMakeLists.txt+8−8
@@ -1,26 +1,26 @@
1# export_qmlplugin macro
1# export_qmlplugin macro
2include(Plugins)
2include(Plugins)
3
3
4−project(IndicatorsNetworkQml)
4+project(IndicatorsLegacyNetworkQml)
5
5
6pkg_check_modules(GLIB glib-2.0>=2.32)
6pkg_check_modules(GLIB glib-2.0>=2.32)
7pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
7pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
8pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
8pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
9
9
10if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
10if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
11− set(INDICATORS_NETWORK_SRC
11+ set(INDICATORS_LEGACY_NETWORK_SRC
12 networkagent.cpp
12 networkagent.cpp
13 plugin.cpp
13 plugin.cpp
14 secret-agent.c
14 secret-agent.c
15 )
15 )
16
16
17− add_library(IndicatorsNetworkQml MODULE
17+ add_library(IndicatorsLegacyNetworkQml MODULE
18− ${INDICATORS_NETWORK_SRC}
18+ ${INDICATORS_LEGACY_NETWORK_SRC}
19 )
19 )
20
20
21− qt5_use_modules(IndicatorsNetworkQml Core Qml Widgets)
21+ qt5_use_modules(IndicatorsLegacyNetworkQml Core Qml Widgets)
22
22
23− set_target_properties(IndicatorsNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
23+ set_target_properties(IndicatorsLegacyNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
24
24
25 include_directories(
25 include_directories(
26 ${CMAKE_CURRENT_SOURCE_DIR}
26 ${CMAKE_CURRENT_SOURCE_DIR}
@@ -29,14 +29,14 @@ if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
29 ${LIBNM_UTIL_INCLUDE_DIRS}
29 ${LIBNM_UTIL_INCLUDE_DIRS}
30 )
30 )
31
31
32− target_link_libraries(IndicatorsNetworkQml
32+ target_link_libraries(IndicatorsLegacyNetworkQml
33 ${GLIB_LIBRARIES}
33 ${GLIB_LIBRARIES}
34 ${LIBNM_GLIB_LIBRARIES}
34 ${LIBNM_GLIB_LIBRARIES}
35 ${LIBNM_UTIL_LIBRARIES}
35 ${LIBNM_UTIL_LIBRARIES}
36 )
36 )
37
37
38 # export the qmltypes and plugin files
38 # 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)
40
40
41else()
41else()
42 message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
42 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 @@
17 */
17 */
18
18
19// Qt
19// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
21
21
22// self
22// self
23#include "plugin.h"
23#include "plugin.h"
plugins/Unity/IndicatorsLegacy/Network/qml/qmldir+1−1
@@ -1,5 +1,5 @@
1module Unity.IndicatorsLegacy.Network
1module Unity.IndicatorsLegacy.Network
2−plugin IndicatorsNetworkQml
2+plugin IndicatorsLegacyNetworkQml
3typeinfo plugin.qmltypes
3typeinfo plugin.qmltypes
4
4
5NetworkPage 0.1 NetworkPage.qml
5NetworkPage 0.1 NetworkPage.qml
plugins/Unity/IndicatorsLegacy/plugin.cpp+1−1
@@ -17,7 +17,7 @@
17 */
17 */
18
18
19// Qt
19// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
21
21
22// self
22// self
23#include "plugin.h"
23#include "plugin.h"
plugins/Unity/Launcher/backend/launcherbackend.cpp+91−87
@@ -22,7 +22,9 @@
22#include "launcherbackend.h"
22#include "launcherbackend.h"
23
23
24#include <QDir>
24#include <QDir>
25+#include <QDBusArgument>
25#include <QFileInfo>
26#include <QFileInfo>
27+#include <QDebug>
26
28
27LauncherBackend::LauncherBackend(bool useStorage, QObject *parent):
29LauncherBackend::LauncherBackend(bool useStorage, QObject *parent):
28 QObject(parent),
30 QObject(parent),
@@ -41,8 +43,8 @@ LauncherBackend::~LauncherBackend()
41
43
42void LauncherBackend::clearItems()
44void LauncherBackend::clearItems()
43{
45{
44− for (int i = 0; i < m_storedApps.size(); i++) {
46+ for (LauncherBackendItem &app: m_storedApps) {
45− delete m_storedApps[i].settings;
47+ delete app.settings;
46 }
48 }
47 m_storedApps.clear();
49 m_storedApps.clear();
48}
50}
@@ -50,80 +52,59 @@ void LauncherBackend::clearItems()
50QStringList LauncherBackend::storedApplications() const
52QStringList LauncherBackend::storedApplications() const
51{
53{
52 auto files = QStringList();
54 auto files = QStringList();
53− for (int i = 0; i < m_storedApps.size(); i++) {
55+ for (const LauncherBackendItem &app: m_storedApps) {
54− files << m_storedApps[i].settings->fileName();
56+ files << app.settings->fileName();
55 }
57 }
56 return files;
58 return files;
57}
59}
58
60
59void LauncherBackend::setStoredApplications(const QStringList &appIds)
61void LauncherBackend::setStoredApplications(const QStringList &appIds)
60{
62{
61− // Are we dropping any pinned apps?
63+ // Record all existing pinned apps, so we can notice them in new list
62− auto needToSync = false;
63 auto pinnedItems = QStringList();
64 auto pinnedItems = QStringList();
64− for (int i = 0; i < m_storedApps.size(); i++) {
65+ for (LauncherBackendItem &app: m_storedApps) {
65− if (m_storedApps[i].pinned) {
66+ if (app.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());
67+ pinnedItems.append(app.settings->fileName());
78 }
68 }
79 }
69 }
80
70
81 clearItems();
71 clearItems();
82
72
83− for (auto appId: appIds) {
73+ for (const QString &appId: appIds) {
84− loadDesktopFile(appId, pinnedItems.contains(resolveAppId(appId)));
74+ loadApp(makeAppDetails(appId, pinnedItems.contains(desktopFile(appId))));
85 }
75 }
86
76
87− if (needToSync) {
88− syncToAccounts();
77+ syncToAccounts();
89− }
90}
78}
91
79
92QString LauncherBackend::desktopFile(const QString &appId) const
80QString LauncherBackend::desktopFile(const QString &appId) const
93{
81{
94− auto index = findItem(appId);
82+ QFileInfo fileInfo(QDir("/usr/share/applications"), appId);
95− if (index < 0) {
83+ return fileInfo.absoluteFilePath();
96− return "";
97− } else {
98− return resolveAppId(appId);
99− }
100}
84}
101
85
102QString LauncherBackend::displayName(const QString &appId) const
86QString LauncherBackend::displayName(const QString &appId) const
103{
87{
104− auto index = findItem(appId);
88+ auto desktopFile = parseDesktopFile(appId);
105− if (index < 0) {
106− return "";
107− } else {
108− return m_storedApps[index].settings->value("Desktop Entry/Name").toString();
89+ auto displayName = desktopFile->value("Desktop Entry/Name").toString();
109− }
90+ delete desktopFile;
91+ return displayName;
110}
92}
111
93
112QString LauncherBackend::icon(const QString &appId) const
94QString LauncherBackend::icon(const QString &appId) const
113{
95{
114− auto index = findItem(appId);
96+ auto desktopFile = parseDesktopFile(appId);
115− if (index < 0) {
116− return "";
117− } else {
118− auto iconName = m_storedApps[index].settings->value("Desktop Entry/Icon").toString();
97+ auto iconName = desktopFile->value("Desktop Entry/Icon").toString();
98+ delete desktopFile;
99+
119− if (!iconName.isEmpty()) {
100+ if (!iconName.isEmpty()) {
120− QFileInfo iconFileInfo(iconName);
101+ QFileInfo iconFileInfo(iconName);
121− if (iconFileInfo.isRelative()) {
102+ if (iconFileInfo.isRelative()) {
122− iconName = "image://gicon/" + iconName;
103+ iconName = "image://gicon/" + iconName;
123− }
124 }
104 }
125− return iconName;
126 }
105 }
106+
107+ return iconName;
127}
108}
128
109
129bool LauncherBackend::isPinned(const QString &appId) const
110bool LauncherBackend::isPinned(const QString &appId) const
@@ -184,71 +165,94 @@ void LauncherBackend::triggerQuickListAction(const QString &appId, const QString
184 Q_UNUSED(quickListId)
165 Q_UNUSED(quickListId)
185}
166}
186
167
187−QString LauncherBackend::resolveAppId(const QString &appId) const
188−{
189− QFileInfo fileInfo(QDir("/usr/share/applications"), appId);
190− return fileInfo.absoluteFilePath();
191−}
192−
193void LauncherBackend::syncFromAccounts()
168void LauncherBackend::syncFromAccounts()
194{
169{
195− auto appIds = QStringList();
170+ QList<QVariantMap> apps;
171+ bool defaults = true;
172+
173+ clearItems();
196
174
197 if (m_user != "" && m_accounts != nullptr) {
175 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);
199 }
179 }
200
180
201 // TODO: load default pinned ones from default config, instead of hardcoding here...
181 // TODO: load default pinned ones from default config, instead of hardcoding here...
202− if (appIds.isEmpty()) {
182+ if (defaults) {
203− appIds <<
183+ qDebug() << "loading default applications for launcher";
184+ apps <<
204− "phone-app.desktop" <<
185+ makeAppDetails("phone-app.desktop", true) <<
205− "camera-app.desktop" <<
186+ makeAppDetails("camera-app.desktop", true) <<
206− "gallery-app.desktop" <<
187+ makeAppDetails("gallery-app.desktop", true) <<
207− "facebook-webapp.desktop" <<
188+ makeAppDetails("facebook-webapp.desktop", true) <<
208− "webbrowser-app.desktop" <<
189+ makeAppDetails("webbrowser-app.desktop", true) <<
209− "twitter-webapp.desktop" <<
190+ makeAppDetails("twitter-webapp.desktop", true) <<
210− "gmail-webapp.desktop" <<
191+ makeAppDetails("gmail-webapp.desktop", true) <<
211− "ubuntu-weather-app.desktop" <<
192+ makeAppDetails("ubuntu-weather-app.desktop", true) <<
212− "notes-app.desktop" <<
193+ makeAppDetails("notes-app.desktop", true) <<
213− "calendar-app.desktop";
194+ makeAppDetails("calendar-app.desktop", true);
214 }
195 }
215
196
216− setStoredApplications(appIds);
197+ for (const QVariant &app: apps) {
217−
198+ loadApp(app.toMap());
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;
221 }
199 }
222}
200}
223
201
224void LauncherBackend::syncToAccounts()
202void LauncherBackend::syncToAccounts()
225{
203{
226− auto pinnedItems = QStringList();
204+ if (m_user != "" && m_accounts != nullptr) {
227− for (int i = 0; i < m_storedApps.size(); i++) {
205+ QList<QVariantMap> items;
206+
228− if (m_storedApps[i].pinned) {
207+ for (LauncherBackendItem &app: m_storedApps) {
229− pinnedItems.append(m_storedApps[i].settings->fileName());
208+ items << makeAppDetails(app.settings->fileName(), app.pinned);
230 }
209 }
231− }
232
210
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));
235 }
212 }
236}
213}
237
214
238−bool LauncherBackend::loadDesktopFile(const QString &appId, bool isPinned)
215+QSettings *LauncherBackend::parseDesktopFile(const QString &appId) const
239{
216{
240− auto item = LauncherBackendItem();
241− auto fullAppId = resolveAppId(appId);
217+ auto fullAppId = desktopFile(appId);
242− item.settings = new QSettings(fullAppId, QSettings::IniFormat);
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);
243 item.pinned = isPinned;
232 item.pinned = isPinned;
244 m_storedApps.append(item);
233 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());
246}
250}
247
251
248int LauncherBackend::findItem(const QString &appId) const
252int LauncherBackend::findItem(const QString &appId) const
249{
253{
250− auto fullAppId = resolveAppId(appId);
254+ auto fullAppId = desktopFile(appId);
251− for (int i = 0; i < m_storedApps.size(); i++) {
255+ for (int i = 0; i < m_storedApps.size(); ++i) {
252 if (m_storedApps[i].settings->fileName() == fullAppId) {
256 if (m_storedApps[i].settings->fileName() == fullAppId) {
253 return i;
257 return i;
254 }
258 }
plugins/Unity/Launcher/backend/launcherbackend.h+13−2
@@ -59,12 +59,18 @@ public:
59
59
60 /**
60 /**
61 * @brief Get the full path to the .desktop file.
61 * @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+ *
62 * @returns The full path to the .dekstop file.
65 * @returns The full path to the .dekstop file.
63 */
66 */
64 QString desktopFile(const QString &appId) const;
67 QString desktopFile(const QString &appId) const;
65
68
66 /**
69 /**
67 * @brief Get the user friendly name of an application.
70 * @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+ *
68 * @param appId The ID of the application.
74 * @param appId The ID of the application.
69 * @returns The user friendly name of the application.
75 * @returns The user friendly name of the application.
70 */
76 */
@@ -72,6 +78,9 @@ public:
72
78
73 /**
79 /**
74 * @brief Get the icon of an application.
80 * @brief Get the icon of an application.
81+ *
82+ * The application does not need to be in the list of stored applications.
83+ *
75 * @param appId The ID of the application.
84 * @param appId The ID of the application.
76 * @returns The full path to the icon for the application.
85 * @returns The full path to the icon for the application.
77 */
86 */
@@ -132,9 +141,11 @@ Q_SIGNALS:
132 void countChanged(const QString &appId, int count);
141 void countChanged(const QString &appId, int count);
133
142
134private:
143private:
135− QString resolveAppId(const QString &appId) const;
144+ QSettings *parseDesktopFile(const QString &appId) const;
136− bool loadDesktopFile(const QString &appId, bool isPinned);
145+ QVariantMap makeAppDetails(const QString &appId, bool pinned) const;
146+ void loadApp(const QVariantMap &details);
137 int findItem(const QString &appId) const;
147 int findItem(const QString &appId) const;
148+ bool isDefaultsItem(const QList<QVariantMap> &apps) const;
138 void syncFromAccounts();
149 void syncFromAccounts();
139 void syncToAccounts();
150 void syncToAccounts();
140 void clearItems();
151 void clearItems();
plugins/Unity/Launcher/launchermodel.cpp+28
@@ -25,6 +25,9 @@ LauncherModel::LauncherModel(QObject *parent):
25 LauncherModelInterface(parent),
25 LauncherModelInterface(parent),
26 m_backend(new LauncherBackend(true, this))
26 m_backend(new LauncherBackend(true, this))
27{
27{
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+
28 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
31 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
29 LauncherItem *item = new LauncherItem(entry,
32 LauncherItem *item = new LauncherItem(entry,
30 m_backend->desktopFile(entry),
33 m_backend->desktopFile(entry),
@@ -67,6 +70,10 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
67 return item->icon();
70 return item->icon();
68 case RolePinned:
71 case RolePinned:
69 return item->pinned();
72 return item->pinned();
73+ case RoleCount:
74+ return item->count();
75+ case RoleProgress:
76+ return item->progress();
70 }
77 }
71
78
72 return QVariant();
79 return QVariant();
@@ -228,3 +235,24 @@ int LauncherModel::findApplication(const QString &appId)
228 }
235 }
229 return -1;
236 return -1;
230}
237}
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:
55 void storeAppList();
55 void storeAppList();
56 int findApplication(const QString &appId);
56 int findApplication(const QString &appId);
57
57
58+private Q_SLOTS:
59+ void progressChanged(const QString &appId, int progress);
60+ void countChanged(const QString &appId, int count);
61+
58private:
62private:
59 QList<LauncherItem*> m_list;
63 QList<LauncherItem*> m_list;
60 LauncherBackend *m_backend;
64 LauncherBackend *m_backend;
plugins/Unity/Launcher/plugin.cpp+1−1
@@ -18,7 +18,7 @@
18 */
18 */
19
19
20// Qt
20// Qt
21−#include <QtQml>
21+#include <QtQml/qqml.h>
22
22
23// self
23// self
24#include "plugin.h"
24#include "plugin.h"
plugins/Unity/categories.cpp+6
@@ -39,6 +39,7 @@ Categories::Categories(QObject* parent)
39 m_roles[Categories::RoleIcon] = "icon";
39 m_roles[Categories::RoleIcon] = "icon";
40 m_roles[Categories::RoleRenderer] = "renderer";
40 m_roles[Categories::RoleRenderer] = "renderer";
41 m_roles[Categories::RoleContentType] = "contentType";
41 m_roles[Categories::RoleContentType] = "contentType";
42+ m_roles[Categories::RoleProgressSource] = "progressSource";
42 m_roles[Categories::RoleHints] = "hints";
43 m_roles[Categories::RoleHints] = "hints";
43 m_roles[Categories::RoleResults] = "results";
44 m_roles[Categories::RoleResults] = "results";
44 m_roles[Categories::RoleCount] = "count";
45 m_roles[Categories::RoleCount] = "count";
@@ -198,6 +199,11 @@ Categories::data(const QModelIndex& index, int role) const
198 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
199 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
199 return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
200 return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
200 }
201 }
202+ case RoleProgressSource:
203+ {
204+ auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
205+ return hints.contains("progress-source") ? hints["progress-source"] : QVariant();
206+ }
201 case RoleHints:
207 case RoleHints:
202 return DeeListModel::data(index, CategoryColumn::HINTS);
208 return DeeListModel::data(index, CategoryColumn::HINTS);
203 case RoleResults:
209 case RoleResults:
plugins/Unity/categories.h+1
@@ -47,6 +47,7 @@ public:
47 RoleIcon,
47 RoleIcon,
48 RoleRenderer,
48 RoleRenderer,
49 RoleContentType,
49 RoleContentType,
50+ RoleProgressSource,
50 RoleHints,
51 RoleHints,
51 RoleResults,
52 RoleResults,
52 RoleCount,
53 RoleCount,
plugins/Unity/genericoptionsmodel.cpp+2−2
@@ -76,9 +76,9 @@ int GenericOptionsModel::rowCount(const QModelIndex& /* parent */) const
76 return m_options.size ();
76 return m_options.size ();
77}
77}
78
78
79−void GenericOptionsModel::setActive(QVector<AbstractFilterOption *>::size_type idx, bool value)
79+void GenericOptionsModel::setActive(int idx, bool value)
80{
80{
81− if (idx < m_options.size())
81+ if (idx >= 0 && idx < m_options.size())
82 {
82 {
83 m_options[idx]->setActive(value);
83 m_options[idx]->setActive(value);
84 }
84 }
plugins/Unity/genericoptionsmodel.h+1−1
@@ -49,7 +49,7 @@ public:
49 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
49 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
50 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
50 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
51
51
52− Q_INVOKABLE void setActive(QVector<AbstractFilterOption *>::size_type idx, bool value);
52+ Q_INVOKABLE void setActive(int idx, bool value);
53 Q_INVOKABLE void setActive(const QString &id, bool value);
53 Q_INVOKABLE void setActive(const QString &id, bool value);
54
54
55 AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
55 AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
plugins/Unity/plugin.cpp+4−1
@@ -17,8 +17,9 @@
17 */
17 */
18
18
19// Qt
19// Qt
20−#include <QtQml>
21#include <QDBusConnection>
20#include <QDBusConnection>
21+#include <QQmlContext>
22+#include <qqml.h>
22
23
23// self
24// self
24#include "plugin.h"
25#include "plugin.h"
@@ -33,6 +34,7 @@
33#include "categories.h"
34#include "categories.h"
34#include "categoryresults.h"
35#include "categoryresults.h"
35#include "bottombarvisibilitycommunicatorshell.h"
36#include "bottombarvisibilitycommunicatorshell.h"
37+#include "genericoptionsmodel.h"
36
38
37// libqtdee
39// libqtdee
38#include "deelistmodel.h"
40#include "deelistmodel.h"
@@ -47,6 +49,7 @@ void UnityPlugin::registerTypes(const char *uri)
47 qmlRegisterUncreatableType<PreviewAction>(uri, 0, 1, "PreviewAction", "Can't create PreviewAction object in QML.");
49 qmlRegisterUncreatableType<PreviewAction>(uri, 0, 1, "PreviewAction", "Can't create PreviewAction object in QML.");
48 qmlRegisterUncreatableType<PreviewInfoHint>(uri, 0, 1, "PreviewInfoHint", "Can't create PreviewInfoHint object in QML.");
50 qmlRegisterUncreatableType<PreviewInfoHint>(uri, 0, 1, "PreviewInfoHint", "Can't create PreviewInfoHint object in QML.");
49 qmlRegisterUncreatableType<SocialPreviewComment>(uri, 0, 1, "SocialPreviewComment", "Can't create SocialPreviewComment object in QML.");
51 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.");
50 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
53 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
51 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
54 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
52 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
55 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
plugins/Utils/plugin.cpp+1−1
@@ -17,7 +17,7 @@
17 */
17 */
18
18
19// Qt
19// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
21#include <QtQuick/QQuickWindow>
21#include <QtQuick/QQuickWindow>
22
22
23// self
23// self
plugins/Utils/qlimitproxymodelqml.cpp+90−4
@@ -25,6 +25,8 @@ QLimitProxyModelQML::QLimitProxyModelQML(QObject *parent)
25 , m_limit(-1)
25 , m_limit(-1)
26 , m_sourceInserting(false)
26 , m_sourceInserting(false)
27 , m_sourceRemoving(false)
27 , m_sourceRemoving(false)
28+ , m_dataChangedBegin(-1)
29+ , m_dataChangedEnd(-1)
28{
30{
29 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
31 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
30 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
32 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
@@ -130,8 +132,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeInserted(const QModelIndex &parent, int
130 beginInsertRows(mapFromSource(parent), start, end);
132 beginInsertRows(mapFromSource(parent), start, end);
131 m_sourceInserting = true;
133 m_sourceInserting = true;
132 } else if (start < m_limit) {
134 } else if (start < m_limit) {
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
133− beginInsertRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
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);
134− m_sourceInserting = true;
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+ }
135 }
174 }
136}
175}
137
176
@@ -142,8 +181,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int s
142 beginRemoveRows(mapFromSource(parent), start, end);
181 beginRemoveRows(mapFromSource(parent), start, end);
143 m_sourceRemoving = true;
182 m_sourceRemoving = true;
144 } else if (start < m_limit) {
183 } else if (start < m_limit) {
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
145− beginRemoveRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
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);
146− m_sourceRemoving = true;
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+ }
147 }
223 }
148}
224}
149
225
@@ -154,6 +230,11 @@ QLimitProxyModelQML::sourceRowsInserted(const QModelIndex & /*parent*/, int /*st
154 endInsertRows();
230 endInsertRows();
155 m_sourceInserting = false;
231 m_sourceInserting = false;
156 }
232 }
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+ }
157}
238}
158
239
159void
240void
@@ -163,4 +244,9 @@ QLimitProxyModelQML::sourceRowsRemoved(const QModelIndex & /*parent*/, int /*sta
163 endRemoveRows();
244 endRemoveRows();
164 m_sourceRemoving = false;
245 m_sourceRemoving = false;
165 }
246 }
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+ }
166}
252}
plugins/Utils/qlimitproxymodelqml.h+2
@@ -55,6 +55,8 @@ private:
55 int m_limit;
55 int m_limit;
56 bool m_sourceInserting;
56 bool m_sourceInserting;
57 bool m_sourceRemoving;
57 bool m_sourceRemoving;
58+ int m_dataChangedBegin;
59+ int m_dataChangedEnd;
58};
60};
59
61
60#endif // QLIMITPROXYMODELQML_H
62#endif // QLIMITPROXYMODELQML_H
po/ar.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
17"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
18"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
18"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"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
20+"X-Generator: Launchpad (build 16734)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
po/az.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/bg.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Transfer-Encoding: 8bit\n"
17"Content-Transfer-Encoding: 8bit\n"
18"Plural-Forms: nplurals=2; plural=n != 1;\n"
18"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
20+"X-Generator: Launchpad (build 16734)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
po/ca.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/de.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/el.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/en_AU.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/en_GB.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/eo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/es.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/fi.po+13−13
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-08-09 12:18+0200\n"
10"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11−"PO-Revision-Date: 2013-08-12 17:45+0000\n"
11+"PO-Revision-Date: 2013-08-23 16:15+0000\n"
12−"Last-Translator: Aleksi Kinnunen <Unknown>\n"
12+"Last-Translator: Timo Jyrinki <timo.jyrinki@canonical.com>\n"
13"Language-Team: Finnish <fi@li.org>\n"
13"Language-Team: Finnish <fi@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-24 05:59+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16738)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
@@ -94,35 +94,35 @@ msgstr "Viimeisimmät haut"
94#: Dash/Apps/AppInfo.qml:72
94#: Dash/Apps/AppInfo.qml:72
95#, qt-format
95#, qt-format
96msgid "(%1)"
96msgid "(%1)"
97−msgstr ""
97+msgstr "(%1)"
98
98
99#. TRANSLATORS: Number of persons who wrote reviews for this app
99#. TRANSLATORS: Number of persons who wrote reviews for this app
100#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
100#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101#, qt-format
101#, qt-format
102msgid "%1 review"
102msgid "%1 review"
103msgid_plural "%1 reviews"
103msgid_plural "%1 reviews"
104−msgstr[0] ""
104+msgstr[0] "%1 arvostelu"
105−msgstr[1] ""
105+msgstr[1] "%1 arvostelua"
106
106
107#: Dash/Apps/AppPreview.qml:181
107#: Dash/Apps/AppPreview.qml:181
108msgid "Rate this"
108msgid "Rate this"
109−msgstr ""
109+msgstr "Arvioi tämä"
110
110
111#: Dash/Apps/AppReviews.qml:72
111#: Dash/Apps/AppReviews.qml:72
112msgid "Add a review"
112msgid "Add a review"
113−msgstr ""
113+msgstr "Lisää arvostelu"
114
114
115#: Dash/Apps/AppReviews.qml:84
115#: Dash/Apps/AppReviews.qml:84
116msgid "Review"
116msgid "Review"
117−msgstr ""
117+msgstr "Arvostelu"
118
118
119#: Dash/Apps/AppReviews.qml:118
119#: Dash/Apps/AppReviews.qml:118
120msgid "Send"
120msgid "Send"
121−msgstr ""
121+msgstr "Lähetä"
122
122
123#: Dash/Apps/AppReviews.qml:136
123#: Dash/Apps/AppReviews.qml:136
124msgid "Comments:"
124msgid "Comments:"
125−msgstr ""
125+msgstr "Kommentit:"
126
126
127#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
128msgid "Running apps"
128msgid "Running apps"
@@ -244,4 +244,4 @@ msgstr "Hakuun täsmääviä tuloksia ei löydy."
244#: Shell.qml:399
244#: Shell.qml:399
245#, qt-format
245#, qt-format
246msgid "Please enter %1"
246msgid "Please enter %1"
247−msgstr ""
247+msgstr "Kirjoita %1"
po/fo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/fr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/gl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/he.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/hr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
20+"X-Generator: Launchpad (build 16734)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
po/hu.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/hy.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/id.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/it.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/ja.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/kn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/ko.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/ky.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/lv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
17"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"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/my.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/nb.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/nl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/pl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
18"|| n%100>=20) ? 1 : 2;\n"
18"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
20+"X-Generator: Launchpad (build 16734)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
po/pt_BR.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/ro.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
17"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
18"== 0) && (n != 0))) ? 2: 1));\n"
18"== 0) && (n != 0))) ? 2: 1));\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
20+"X-Generator: Launchpad (build 16734)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
po/ru.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
20+"X-Generator: Launchpad (build 16734)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
po/si.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/sl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
17"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
18"n%100==4 ? 3 : 0);\n"
18"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
20+"X-Generator: Launchpad (build 16734)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
po/sr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
20+"X-Generator: Launchpad (build 16734)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
po/sv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/te.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/tr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/ug.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/uk.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
20+"X-Generator: Launchpad (build 16734)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
po/unity8.pot+51−43
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"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"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -86,25 +86,69 @@ msgstr ""
86msgid "YouTube"
86msgid "YouTube"
87msgstr ""
87msgstr ""
88
88
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+
89#: Components/PageHeader.qml:298
133#: Components/PageHeader.qml:298
90msgid "Recent searches"
134msgid "Recent searches"
91msgstr ""
135msgstr ""
92
136
93#. TRANSLATORS: Number of persons who rated this app
137#. TRANSLATORS: Number of persons who rated this app
94−#: Dash/Apps/AppInfo.qml:72
138+#: Dash/Apps/AppInfo.qml:73
95#, qt-format
139#, qt-format
96msgid "(%1)"
140msgid "(%1)"
97msgstr ""
141msgstr ""
98
142
99#. TRANSLATORS: Number of persons who wrote reviews for this app
143#. 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
101#, qt-format
145#, qt-format
102msgid "%1 review"
146msgid "%1 review"
103msgid_plural "%1 reviews"
147msgid_plural "%1 reviews"
104msgstr[0] ""
148msgstr[0] ""
105msgstr[1] ""
149msgstr[1] ""
106
150
107−#: Dash/Apps/AppPreview.qml:181
151+#: Dash/Apps/AppPreview.qml:175
108msgid "Rate this"
152msgid "Rate this"
109msgstr ""
153msgstr ""
110
154
@@ -124,42 +168,6 @@ msgstr ""
124msgid "Comments:"
168msgid "Comments:"
125msgstr ""
169msgstr ""
126
170
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−
163#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
171#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
164msgid "Featured"
172msgid "Featured"
165msgstr ""
173msgstr ""
@@ -200,7 +208,7 @@ msgstr ""
200msgid "Author:"
208msgid "Author:"
201msgstr ""
209msgstr ""
202
210
203−#: Greeter/Lockscreen.qml:133
211+#: Greeter/Lockscreen.qml:134
204msgid "Emergency Call"
212msgid "Emergency Call"
205msgstr ""
213msgstr ""
206
214
@@ -237,11 +245,11 @@ msgstr ""
237msgid "Search"
245msgid "Search"
238msgstr ""
246msgstr ""
239
247
240−#: plugins/Unity/scope.cpp:298
248+#: plugins/Unity/scope.cpp:317
241msgid "Sorry, there is nothing that matches your search."
249msgid "Sorry, there is nothing that matches your search."
242msgstr ""
250msgstr ""
243
251
244−#: Shell.qml:399
252+#: Shell.qml:429
245#, qt-format
253#, qt-format
246msgid "Please enter %1"
254msgid "Please enter %1"
247msgstr ""
255msgstr ""
po/zh_CN.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
po/zh_TW.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
19+"X-Generator: Launchpad (build 16734)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
src/Panel/Indicators/servicefiles/CMakeLists.txt−1
@@ -1,6 +1,5 @@
1file(GLOB INDICATOR_SERVICE_FILES
1file(GLOB INDICATOR_SERVICE_FILES
2 ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.messages
2 ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.messages
3− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.settings.network
4)
3)
5
4
6# copy indicators files into build directory for shadow builds
5# 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/"
51UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
51UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
52UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
52UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
53
53
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+
55 if model() == 'Desktop':
62 if model() == 'Desktop':
63+ if devices == 'All':
56− return [
64+ return [nexus4, nexus10]
57− (
65+ elif devices == 'Nexus4':
58− 'Desktop Nexus 4',
66+ return [nexus4]
59− dict(app_width=768, app_height=1280, grid_unit_px=18)
67+ elif devices == 'Nexus10':
60− ),
68+ return [nexus10]
61− (
69+ else:
62− 'Desktop Nexus 10',
70+ raise RuntimeException('Unrecognized device-option "%s" passed.' % devices)
63− dict(app_width=2560, app_height=1600, grid_unit_px=20)
64− ),
65− ]
66 else:
71 else:
67− return [
72+ return [native]
68− (
69− 'Native Device',
70− dict(app_width=0, app_height=0, grid_unit_px=0)
71− )
72− ]
73−
74
73
75class UnityTestCase(AutopilotTestCase):
74class UnityTestCase(AutopilotTestCase):
76
75
@@ -148,7 +147,7 @@ class UnityTestCase(AutopilotTestCase):
148 divisor = divisor * 2
147 divisor = divisor * 2
149 return divisor
148 return divisor
150
149
151− def launch_unity(self):
150+ def launch_unity(self, **kwargs):
152 """Launch the unity shell, return a proxy object for it."""
151 """Launch the unity shell, return a proxy object for it."""
153 binary_path = get_binary_path()
152 binary_path = get_binary_path()
154 lib_path = get_lib_path()
153 lib_path = get_lib_path()
@@ -167,7 +166,8 @@ class UnityTestCase(AutopilotTestCase):
167 binary_path,
166 binary_path,
168 *self.unity_geometry_args,
167 *self.unity_geometry_args,
169 app_type='qt',
168 app_type='qt',
170− emulator_base=UnityEmulatorBase
169+ emulator_base=UnityEmulatorBase,
170+ **kwargs
171 )
171 )
172 self._set_proxy(app_proxy)
172 self._set_proxy(app_proxy)
173
173
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 @@
20#ifndef UNITY_MOCK_ACCOUNTSSERVICE_H
20#ifndef UNITY_MOCK_ACCOUNTSSERVICE_H
21#define UNITY_MOCK_ACCOUNTSSERVICE_H
21#define UNITY_MOCK_ACCOUNTSSERVICE_H
22
22
23−#include <QtCore/QObject>
23+#include <QObject>
24−#include <QtCore/QString>
24+#include <QString>
25−#include <QtCore/QVariant>
25+#include <QVariant>
26
26
27class AccountsService: public QObject
27class AccountsService: public QObject
28{
28{
@@ -31,9 +31,8 @@ class AccountsService: public QObject
31public:
31public:
32 explicit AccountsService(QObject *parent = 0);
32 explicit AccountsService(QObject *parent = 0);
33
33
34−public Q_SLOTS:
35− QVariant getUserProperty(const QString &user, const QString &property);
34+ Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &property);
36− void setUserProperty(const QString &user, const QString &property, const QVariant &value);
35+ Q_INVOKABLE void setUserProperty(const QString &user, const QString &property, const QVariant &value);
37};
36};
38
37
39#endif
38#endif
tests/mocks/AccountsService/plugin.cpp+1−1
@@ -20,7 +20,7 @@
20#include "plugin.h"
20#include "plugin.h"
21#include "AccountsService.h"
21#include "AccountsService.h"
22
22
23−#include <QtQml/QtQml>
23+#include <QtQml>
24
24
25static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
26{
26{
tests/mocks/AccountsService/plugin.h+2−2
@@ -20,8 +20,8 @@
20#ifndef MOCK_ACCOUNTSSERVICE_PLUGIN_H
20#ifndef MOCK_ACCOUNTSSERVICE_PLUGIN_H
21#define MOCK_ACCOUNTSSERVICE_PLUGIN_H
21#define MOCK_ACCOUNTSSERVICE_PLUGIN_H
22
22
23−#include <QtQml/QQmlEngine>
23+#include <QQmlEngine>
24−#include <QtQml/QQmlExtensionPlugin>
24+#include <QQmlExtensionPlugin>
25
25
26class AccountsServicePlugin : public QQmlExtensionPlugin
26class AccountsServicePlugin : public QQmlExtensionPlugin
27{
27{
tests/mocks/GSettings.1.0/fake_gsettings.h+1−1
@@ -18,7 +18,7 @@
18#define FAKE_GSETTINGS_H
18#define FAKE_GSETTINGS_H
19
19
20#include <QList>
20#include <QList>
21−#include <QtQml>
21+#include <QObject>
22
22
23class GSettingsSchemaQml: public QObject
23class GSettingsSchemaQml: public QObject
24{
24{
tests/mocks/GSettings.1.0/plugin.cpp+1−1
@@ -17,7 +17,7 @@
17#include "plugin.h"
17#include "plugin.h"
18#include "fake_gsettings.h"
18#include "fake_gsettings.h"
19
19
20−#include <QtQml>
20+#include <QtQml/qqml.h>
21
21
22static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
22static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
23{
23{
tests/mocks/Powerd/Powerd.h+15−2
@@ -20,17 +20,30 @@
20#define UNITY_MOCK_POWERD_H
20#define UNITY_MOCK_POWERD_H
21
21
22#include <QtCore/QObject>
22#include <QtCore/QObject>
23−#include <QtDBus/QDBusInterface>
24
23
25class Powerd: public QObject
24class Powerd: public QObject
26{
25{
27 Q_OBJECT
26 Q_OBJECT
27+ Q_ENUMS(Status)
28+ Q_FLAGS(DisplayFlag DisplayFlags)
28
29
29public:
30public:
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+
30 explicit Powerd(QObject *parent = 0);
43 explicit Powerd(QObject *parent = 0);
31
44
32Q_SIGNALS:
45Q_SIGNALS:
33− void powerStateChange(int state);
46+ void displayPowerStateChange(int status, unsigned int flags);
34};
47};
35
48
36#endif
49#endif
tests/mocks/Powerd/plugin.cpp+1−1
@@ -19,7 +19,7 @@
19#include "plugin.h"
19#include "plugin.h"
20#include "Powerd.h"
20#include "Powerd.h"
21
21
22−#include <QtQml/QtQml>
22+#include <QtQml/qqml.h>
23
23
24static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
24static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25{
25{
tests/mocks/Ubuntu/DownloadDaemonListener/plugin.cpp−1
@@ -17,7 +17,6 @@
17#include "plugin.h"
17#include "plugin.h"
18#include "MockDownloadTracker.h"
18#include "MockDownloadTracker.h"
19
19
20−#include <QtQml>
21#include <QtQml/QQmlContext>
20#include <QtQml/QQmlContext>
22#include <qqml.h>
21#include <qqml.h>
23
22
tests/mocks/Unity/Indicators/fakeplugin.cpp+1−1
@@ -17,7 +17,7 @@
17 */
17 */
18
18
19// Qt
19// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
21
21
22// self
22// self
23#include "fakeplugin.h"
23#include "fakeplugin.h"
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+6
@@ -174,3 +174,9 @@ int MockLauncherModel::findApp(const QString &appId)
174 }
174 }
175 return -1;
175 return -1;
176}
176}
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:
43 Q_INVOKABLE void pin(const QString &appId, int index = -1);
43 Q_INVOKABLE void pin(const QString &appId, int index = -1);
44 Q_INVOKABLE void requestRemove(const QString &appId);
44 Q_INVOKABLE void requestRemove(const QString &appId);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
46+ Q_INVOKABLE void setUser(const QString &username);
46
47
47Q_SIGNALS:
48Q_SIGNALS:
48 void quickListTriggered(const QString &appId, int index);
49 void quickListTriggered(const QString &appId, int index);
tests/mocks/Unity/fake_unity_plugin.cpp+2−1
@@ -26,7 +26,8 @@
26
26
27// External
27// External
28#include <glib-object.h>
28#include <glib-object.h>
29+
29−#include <QtQml>
30+#include <QtQml/qqml.h>
30
31
31void FakeUnityPlugin::registerTypes(const char *uri)
32void FakeUnityPlugin::registerTypes(const char *uri)
32{
33{
tests/plugins/AccountsService/AccountsServer.h+4−4
@@ -20,10 +20,10 @@
20#ifndef UNITY_ACCOUNTSSERVER_H
20#ifndef UNITY_ACCOUNTSSERVER_H
21#define UNITY_ACCOUNTSSERVER_H
21#define UNITY_ACCOUNTSSERVER_H
22
22
23+#include <QDBusContext>
24+#include <QDBusObjectPath>
23−#include <QtCore/QObject>
25+#include <QObject>
24−#include <QtCore/QString>
26+#include <QString>
25−#include <QtDBus/QDBusContext>
26−#include <QtDBus/QDBusObjectPath>
27
27
28class AccountsServer: public QObject, protected QDBusContext
28class AccountsServer: public QObject, protected QDBusContext
29{
29{
tests/plugins/AccountsService/PropertiesServer.h+4−4
@@ -21,10 +21,10 @@
21#define UNITY_PROPERTIESSERVER_H
21#define UNITY_PROPERTIESSERVER_H
22
22
23#include "PropertiesServer.h"
23#include "PropertiesServer.h"
24+#include <QDBusContext>
25+#include <QDBusVariant>
24−#include <QtCore/QObject>
26+#include <QObject>
25−#include <QtCore/QString>
27+#include <QString>
26−#include <QtDBus/QDBusContext>
27−#include <QtDBus/QDBusVariant>
28
28
29class PropertiesServer: public QObject, protected QDBusContext
29class PropertiesServer: public QObject, protected QDBusContext
30{
30{
tests/plugins/AccountsService/client.cpp+2−2
@@ -18,8 +18,8 @@
18 */
18 */
19
19
20#include "AccountsService.h"
20#include "AccountsService.h"
21−#include <QtTest/QSignalSpy>
21+#include <QSignalSpy>
22−#include <QtTest/QTest>
22+#include <QTest>
23
23
24class AccountsServiceTest : public QObject
24class AccountsServiceTest : public QObject
25{
25{
tests/plugins/AccountsService/server.cpp+1−1
@@ -21,7 +21,7 @@
21#include "AccountsServer.h"
21#include "AccountsServer.h"
22#include "PropertiesAdaptor.h"
22#include "PropertiesAdaptor.h"
23#include "PropertiesServer.h"
23#include "PropertiesServer.h"
24−#include <QtCore/QCoreApplication>
24+#include <QCoreApplication>
25
25
26int main(int argc, char *argv[])
26int main(int argc, char *argv[])
27{
27{
tests/plugins/Unity/Launcher/launcherbackendtest.cpp+2−8
@@ -31,15 +31,12 @@ private Q_SLOTS:
31 {
31 {
32 LauncherBackend backend(false);
32 LauncherBackend backend(false);
33
33
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");
36
36
37 QCOMPARE(backend.desktopFile("one.desktop"), QString("/usr/share/applications/one.desktop"));
37 QCOMPARE(backend.desktopFile("one.desktop"), QString("/usr/share/applications/one.desktop"));
38 QCOMPARE(backend.desktopFile("two"), QString("/usr/share/applications/two"));
38 QCOMPARE(backend.desktopFile("two"), QString("/usr/share/applications/two"));
39 QCOMPARE(backend.desktopFile("/full"), QString("/full"));
39 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(""));
43
40
44 QCOMPARE(backend.desktopFile("/usr/share/applications/one.desktop"), QString("/usr/share/applications/one.desktop"));
41 QCOMPARE(backend.desktopFile("/usr/share/applications/one.desktop"), QString("/usr/share/applications/one.desktop"));
45 }
42 }
@@ -48,9 +45,6 @@ private Q_SLOTS:
48 {
45 {
49 LauncherBackend backend(false);
46 LauncherBackend backend(false);
50
47
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−
54 QCOMPARE(backend.displayName(SRCDIR "/rel-icon.desktop"), QString("Relative Icon"));
48 QCOMPARE(backend.displayName(SRCDIR "/rel-icon.desktop"), QString("Relative Icon"));
55 QCOMPARE(backend.icon(SRCDIR "/rel-icon.desktop"), QString("image://gicon/rel-icon"));
49 QCOMPARE(backend.icon(SRCDIR "/rel-icon.desktop"), QString("image://gicon/rel-icon"));
56
50
tests/plugins/Utils/qlimitproxymodeltest.cpp+133−8
@@ -58,10 +58,17 @@ public:
58 m_roles = roles;
58 m_roles = roles;
59 }
59 }
60
60
61+ void clear()
62+ {
63+ beginRemoveRows(QModelIndex(), 0, m_list.count() - 1);
64+ m_list.clear();
65+ endRemoveRows();
66+ }
67+
61 bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
68 bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
62 beginInsertRows(parent, row, row+count-1);
69 beginInsertRows(parent, row, row+count-1);
63 for (int i=0; i<count; i++) {
70 for (int i=0; i<count; i++) {
64− m_list.insert(i+row, "test"+i);
71+ m_list.insert(i+row, QString::number(i));
65 }
72 }
66 endInsertRows();
73 endInsertRows();
67 return true;
74 return true;
@@ -88,6 +95,60 @@ private:
88 QHash<int, QByteArray> m_roles;
95 QHash<int, QByteArray> m_roles;
89};
96};
90
97
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+
91class QLimitProxyModelTest : public QObject
152class QLimitProxyModelTest : public QObject
92{
153{
93 Q_OBJECT
154 Q_OBJECT
@@ -401,15 +462,10 @@ private Q_SLOTS:
401 QCOMPARE(spyOnCountChanged.count(), 0);
462 QCOMPARE(spyOnCountChanged.count(), 0);
402
463
403 model.removeRows(2, 2);
464 model.removeRows(2, 2);
404− QCOMPARE(spyOnRowsRemoved.count(), 1);
465+ QCOMPARE(spyOnRowsRemoved.count(), 0);
405 QCOMPARE(spyOnRowsInserted.count(), 0);
466 QCOMPARE(spyOnRowsInserted.count(), 0);
406− QCOMPARE(spyOnCountChanged.count(), 1);
467+ QCOMPARE(spyOnCountChanged.count(), 0);
407− arguments = spyOnRowsRemoved.takeFirst();
408− QCOMPARE(arguments.at(1).toInt(), 2);
409− QCOMPARE(arguments.at(2).toInt(), 3);
410 QCOMPARE(proxy.rowCount(), 7);
468 QCOMPARE(proxy.rowCount(), 7);
411− spyOnRowsInserted.clear();
412− spyOnCountChanged.clear();
413
469
414 model.removeRows(0, 7);
470 model.removeRows(0, 7);
415 QCOMPARE(spyOnRowsRemoved.count(), 1);
471 QCOMPARE(spyOnRowsRemoved.count(), 1);
@@ -479,6 +535,75 @@ private Q_SLOTS:
479 QCOMPARE(proxy.rowCount(), 5);
535 QCOMPARE(proxy.rowCount(), 5);
480 QCOMPARE(spyOnCountChanged.count(), 1);
536 QCOMPARE(spyOnCountChanged.count(), 1);
481 }
537 }
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+ }
482};
607};
483
608
484QTEST_GUILESS_MAIN(QLimitProxyModelTest)
609QTEST_GUILESS_MAIN(QLimitProxyModelTest)
tests/qmltests/CMakeLists.txt+1−1
@@ -28,9 +28,9 @@ set(qmltest_DEFAULT_PROPERTIES "")
28
28
29add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
29add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
30 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
30 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
31−add_qml_test(Components DemoOverlay)
32add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
31add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
33add_qml_test(Components DraggingArea)
32add_qml_test(Components DraggingArea)
33+add_qml_test(Components EdgeDemoOverlay)
34add_qml_test(Components FilterGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
34add_qml_test(Components FilterGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
35add_qml_test(Components ResponsiveFlowView)
35add_qml_test(Components ResponsiveFlowView)
36add_qml_test(Components ResponsiveGridView)
36add_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 {
70 compare(clock.__timerRunning, true, "Timer should be running")
70 compare(clock.__timerRunning, true, "Timer should be running")
71 wait(0) // spin event loop to trigger the timer
71 wait(0) // spin event loop to trigger the timer
72 verify(dateLabel.text !== dateString)
72 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
74 verify(timeLabel.text !== timeString)
74 verify(timeLabel.text !== timeString)
75 }
75 }
76
76
tests/qmltests/Launcher/tst_Launcher.qml+38−14
@@ -277,19 +277,24 @@ Item {
277
277
278 function test_dragndrop_data() {
278 function test_dragndrop_data() {
279 return [
279 return [
280− {tag: "startDrag", fullDrag: false},
280+ {tag: "startDrag", fullDrag: false },
281+ {tag: "fullDrag horizontal", fullDrag: true, orientation: ListView.Horizontal },
281− {tag: "fullDrag", fullDrag: true},
282+ {tag: "fullDrag vertical", fullDrag: true, orientation: ListView.Vertical },
282 ];
283 ];
283 }
284 }
284
285
285 function test_dragndrop(data) {
286 function test_dragndrop(data) {
286 revealer.dragLauncherIntoView();
287 revealer.dragLauncherIntoView();
287− var draggedItem = findChild(launcher, "launcherDelegate5")
288+ var draggedItem = findChild(launcher, "launcherDelegate4")
288 var item0 = findChild(launcher, "launcherDelegate0")
289 var item0 = findChild(launcher, "launcherDelegate0")
289 var fakeDragItem = findChild(launcher, "fakeDragItem")
290 var fakeDragItem = findChild(launcher, "fakeDragItem")
290 var initialItemHeight = draggedItem.height
291 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);
293
298
294 // Initial state
299 // Initial state
295 compare(draggedItem.itemOpacity, 1, "Item's opacity is not 1 at beginning")
300 compare(draggedItem.itemOpacity, 1, "Item's opacity is not 1 at beginning")
@@ -297,28 +302,43 @@ Item {
297 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
302 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
298
303
299 // Doing longpress
304 // Doing longpress
305+ var currentMouseX = draggedItem.width / 2
300− mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
306+ var currentMouseY = draggedItem.height / 2
307+ mousePress(draggedItem, currentMouseX, currentMouseY)
301 // DraggedItem needs to hide and fakeDragItem become visible
308 // DraggedItem needs to hide and fakeDragItem become visible
302 tryCompare(draggedItem, "itemOpacity", 0)
309 tryCompare(draggedItem, "itemOpacity", 0)
303 tryCompare(fakeDragItem, "visible", true)
310 tryCompare(fakeDragItem, "visible", true)
304
311
305 // Dragging a bit (> 1.5 gu)
312 // 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)
307 // Other items need to expand and become 0.6 opaque
315 // Other items need to expand and become 0.6 opaque
308 tryCompare(item0, "angle", 0)
316 tryCompare(item0, "angle", 0)
309 tryCompare(item0, "itemOpacity", 0.6)
317 tryCompare(item0, "itemOpacity", 0.6)
310
318
311 if (data.fullDrag) {
319 if (data.fullDrag) {
312 // Dragging a bit more
320 // Dragging a bit more
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)
313− mouseMove(draggedItem, -units.gu(15), draggedItem.height / 2, 100)
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+
314− tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
335+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
315− tryCompare(draggedItem, "height", units.gu(1))
336+ tryCompare(draggedItem, "height", units.gu(1))
337+ }
316
338
317− // Dragging downwards. Item needs to move in the model
318− mouseMove(draggedItem, -units.gu(15), -initialItemHeight)
319 waitForRendering(draggedItem)
339 waitForRendering(draggedItem)
320− compare(LauncherModel.get(5).iconName, item4Name)
340+ compare(LauncherModel.get(4).appId, item3)
321− compare(LauncherModel.get(5).iconName, item5Name)
341+ compare(LauncherModel.get(3).appId, item4)
322 }
342 }
323
343
324 // Releasing and checking if initial values are restored
344 // Releasing and checking if initial values are restored
@@ -326,6 +346,10 @@ Item {
326 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
346 tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
327 tryCompare(draggedItem, "itemOpacity", 1)
347 tryCompare(draggedItem, "itemOpacity", 1)
328 tryCompare(fakeDragItem, "visible", false)
348 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();
329 }
353 }
330
354
331 function test_quicklist_dismiss() {
355 function test_quicklist_dismiss() {
tests/qmltests/tst_Shell.qml+7−3
@@ -125,13 +125,17 @@ Item {
125 var mainApp = ApplicationManager.mainStageFocusedApplication;
125 var mainApp = ApplicationManager.mainStageFocusedApplication;
126 tryCompareFunction(function() { return mainApp != null; }, true);
126 tryCompareFunction(function() { return mainApp != null; }, true);
127
127
128+ // Try to suspend while proximity is engaged...
129+ Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
130+ tryCompare(greeter, "showProgress", 0);
131+
128− // Now suspend
132+ // Now really suspend
129− Powerd.powerStateChange(0);
133+ Powerd.displayPowerStateChange(Powerd.Off, 0);
130 tryCompare(greeter, "showProgress", 1);
134 tryCompare(greeter, "showProgress", 1);
131 tryCompare(ApplicationManager, "mainStageFocusedApplication", null);
135 tryCompare(ApplicationManager, "mainStageFocusedApplication", null);
132
136
133 // And wake up
137 // And wake up
134− Powerd.powerStateChange(1);
138+ Powerd.displayPowerStateChange(Powerd.On, 0);
135 tryCompare(ApplicationManager, "mainStageFocusedApplication", mainApp);
139 tryCompare(ApplicationManager, "mainStageFocusedApplication", mainApp);
136 tryCompare(greeter, "showProgress", 1);
140 tryCompare(greeter, "showProgress", 1);
137 }
141 }
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+1−1
@@ -19,7 +19,7 @@
19
19
20#include <QtCore/QObject>
20#include <QtCore/QObject>
21#include <QtTest/QTest>
21#include <QtTest/QTest>
22−#include <QtQml/QtQml>
22+#include <QtQml/qqml.h>
23
23
24class TouchEventSequenceWrapper : public QObject
24class TouchEventSequenceWrapper : public QObject
25{
25{
tests/utils/modules/Unity/Test/UnityTestCase.qml+21−2
@@ -69,9 +69,28 @@ TestCase {
69 }
69 }
70
70
71
71
72− // Find an object with the given name recursively, starting
72+ // Find an object with the given name in the children tree of "obj"
73− // at the given object "obj"
74 function findChild(obj,objectName) {
73 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) {
75 var childs = new Array(0);
94 var childs = new Array(0);
76 childs.push(obj)
95 childs.push(obj)
77 while (childs.length > 0) {
96 while (childs.length > 0) {