on xenial help sign in
merged with trunk
616dc46Nick Dedekind
Dash/Apps/AppInfo.qml+1
@@ -53,6 +53,7 @@ Row {
53 }
53 }
54
54
55 Row {
55 Row {
56+ visible: root.rating >= 0
56 spacing: units.gu(1)
57 spacing: units.gu(1)
57
58
58 RatingStars {
59 RatingStars {
Dash/Apps/AppPreview.qml+37−32
@@ -38,7 +38,7 @@ DashPreview {
38 right: parent.right
38 right: parent.right
39 margins: units.gu(1)
39 margins: units.gu(1)
40 }
40 }
41− model: previewData.infoMap["more-screenshots"].value
41+ model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
42
42
43 delegate: UbuntuShape {
43 delegate: UbuntuShape {
44 id: shape
44 id: shape
@@ -138,9 +138,9 @@ DashPreview {
138
138
139 appName: root.previewData.title
139 appName: root.previewData.title
140 icon: IconUtil.from_gicon(root.previewData.appIcon)
140 icon: IconUtil.from_gicon(root.previewData.appIcon)
141− rating: root.previewData.infoMap["rating"] ? root.previewData.infoMap["rating"].value : 0
141+ rating: Math.round(root.previewData.rating * 10)
142+ reviews: root.previewData.numRatings
142 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
143 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
143− reviews: root.previewData.infoMap["reviews"] ? root.previewData.infoMap["reviews"].value : 0
144 }
144 }
145
145
146 Label {
146 Label {
@@ -154,42 +154,47 @@ DashPreview {
154 styleColor: "black"
154 styleColor: "black"
155 }
155 }
156
156
157− ListItem.ThinDivider {}
157+ Column {
158−
158+ visible: root.previewData.rating >= 0
159− Item {
160 anchors { left: parent.left; right: parent.right }
159 anchors { left: parent.left; right: parent.right }
160+ spacing: parent.spacing
161+
162+ ListItem.ThinDivider { }
163+
164+ Item {
165+ anchors { left: parent.left; right: parent.right }
161− height: rateLabel.height
166+ height: rateLabel.height
162−
167+
163− Label {
168+ Label {
164− id: rateLabel
169+ id: rateLabel
165− fontSize: "medium"
170+ fontSize: "medium"
166− color: "white"
171+ color: "white"
167− style: Text.Raised
172+ style: Text.Raised
168− styleColor: "black"
173+ styleColor: "black"
169− opacity: .9
174+ opacity: .9
170− text: i18n.tr("Rate this")
175+ text: i18n.tr("Rate this")
171−
176+
172− anchors.left: parent.left
177+ anchors.left: parent.left
173− anchors.verticalCenter: parent.verticalCenter
178+ anchors.verticalCenter: parent.verticalCenter
174− }
179+ }
175
180
176− // FIXME these need to be made interactive and connected to the scope
181+ // FIXME these need to be made interactive and connected to the scope
177− RatingStars {
182+ RatingStars {
178− anchors.right: parent.right
183+ anchors.right: parent.right
179− anchors.verticalCenter: parent.verticalCenter
184+ anchors.verticalCenter: parent.verticalCenter
185+ }
180 }
186 }
181− }
182
187
183− ListItem.ThinDivider {}
188+ ListItem.ThinDivider { }
184
189
185− AppReviews {
190+ AppReviews {
186− objectName: "appReviews"
191+ objectName: "appReviews"
187− anchors { left: parent.left; right: parent.right }
192+ anchors { left: parent.left; right: parent.right }
188
193
189− model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
194+ model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
190
195
191− onSendReview: root.sendUserReview(review);
196+ onSendReview: root.sendUserReview(review);
197+ }
192 }
198 }
193−
194 }
199 }
195}
200}
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"
Launcher/FoldingLauncherDelegate.qmlnew file+133
@@ -0,0 +1,133 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+
20+LauncherDelegate {
21+ id: root
22+
23+ // The angle used for rotating
24+ angle: {
25+ // First/last items are special
26+ if (index == 0 || index == priv.listView.count-1) {
27+ if (priv.distanceFromEdge < 0) {
28+ // proportion equation: distanceFromTopEdge : angle = totalUnfoldedHeight/2 : maxAngle
29+ return Math.max(-maxAngle, priv.distanceFromEdge * maxAngle / (priv.listView.foldingAreaHeight)) * priv.orientationFlag
30+ }
31+ return 0; // Don't fold first/last item as long as inside the view
32+ }
33+
34+ // Are we in the already completely outside the flickable? Fold for the last 5 degrees
35+ if (priv.distanceFromEdge < 0) {
36+ // proportion equation: -distanceFromTopEdge : angle = totalUnfoldedHeight : 5
37+ return Math.max(-maxAngle, (priv.distanceFromEdge * 5 / priv.listView.foldingAreaHeight) - (maxAngle-5)) * priv.orientationFlag
38+ }
39+
40+ // We are overlapping with the folding area, fold the icon to maxAngle - 5 degrees
41+ if (priv.overlapWithFoldingArea > 0) {
42+ // proportion equation: overlap: totalHeight = angle : (maxAngle - 5)
43+ return -priv.overlapWithFoldingArea * (maxAngle -5) / priv.listView.foldingAreaHeight * priv.orientationFlag;
44+ }
45+ return 0;
46+ }
47+
48+ // This is the offset that keeps the items inside the panel
49+ offset: {
50+ // First/last items are special
51+ if (index == 0 || index == priv.listView.count-1) {
52+ // Just keep them bound to the edges in case they're outside of the visible area
53+ if (priv.distanceFromEdge < 0) {
54+ return (-priv.distanceFromEdge - (height - effectiveHeight)) * priv.orientationFlag;
55+ }
56+ return 0;
57+ }
58+
59+ // Are we already completely outside the flickable? Stop the icon here.
60+ if (priv.distanceFromEdge < -priv.totalUnfoldedHeight) {
61+ return (-priv.distanceFromEdge - (root.height - effectiveHeight)) * priv.orientationFlag;
62+ }
63+
64+ // We're touching the edge, move slower than the actual flicking speed.
65+ if (priv.distanceFromEdge < 0) {
66+ return (Math.abs(priv.distanceFromEdge) * priv.totalEffectiveHeight / priv.totalUnfoldedHeight) * priv.orientationFlag
67+ }
68+ return 0;
69+ }
70+
71+ itemOpacity: {
72+ // First/last items are special
73+ if (index == 0 || index == priv.listView.count-1) {
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)
76+ return 1.0 - (-priv.distanceFromEdge / (priv.listView.foldingAreaHeight * 3))
77+ }
78+ return 1; // Don't make first/last item transparent as long as inside the view
79+ }
80+
81+ // Are we already completely outside the flickable? Fade from 0.75 to 0 in 2 items height
82+ if (priv.distanceFromEdge < 0) {
83+ // proportion equation: -distanceFromEdge : 1-opacity = totalUnfoldedHeight : 0.75
84+ return 0.75 - (-priv.distanceFromEdge * 0.75 / (priv.totalUnfoldedHeight*2))
85+ }
86+
87+ // We are overlapping with the folding area, fade out to 0.75
88+ if (priv.overlapWithFoldingArea > 0) {
89+ // proportion equation: overlap : totalHeight = 1-opacity : 0.25
90+ return 1 - (priv.overlapWithFoldingArea * 0.25 / priv.listView.foldingAreaHeight)
91+ }
92+ return 1;
93+ }
94+
95+ brightness: {
96+ // First/last items are special
97+ if (index == 0 || index == priv.listView.count-1) {
98+ if (priv.distanceFromEdge < 0) {
99+ return -(-priv.distanceFromEdge / (priv.listView.foldingAreaHeight * 3))
100+ }
101+ return 0;
102+ }
103+ // Are we already completely outside the flickable? Fade from 0.7 to 0 in 2 items height
104+ if (priv.distanceFromEdge < 0) {
105+ return -0.3 - (-priv.distanceFromEdge * 0.1 / (priv.totalUnfoldedHeight*2))
106+ }
107+
108+ // We are overlapping with the folding area, fade out to 0.7
109+ if (priv.overlapWithFoldingArea > 0) {
110+ return - (priv.overlapWithFoldingArea * 0.3 / priv.listView.foldingAreaHeight)
111+ }
112+ return 0;
113+ }
114+
115+ QtObject {
116+ id: priv
117+
118+ property ListView listView: root.ListView.view
119+ property real totalUnfoldedHeight: listView.itemHeight + listView.spacing
120+ property real totalEffectiveHeight: effectiveHeight + listView.spacing
121+ property real effectiveContentY: listView.contentY - listView.originY
122+ property real effectiveY: y - listView.originY
123+ property real bottomDraggingDistanceOffset: listView.draggedIndex > index ? totalUnfoldedHeight : 0
124+ property real distanceFromTopEdge: -(effectiveContentY + listView.topMargin - index*totalUnfoldedHeight)
125+ property real distanceFromBottomEdge: listView.height - listView.bottomMargin - (effectiveY+height) + effectiveContentY + bottomDraggingDistanceOffset
126+
127+ property real distanceFromEdge: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? distanceFromBottomEdge : distanceFromTopEdge
128+ property real orientationFlag: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? -1 : 1
129+
130+ property real overlapWithFoldingArea: listView.foldingAreaHeight - distanceFromEdge
131+ }
132+
133+}
Launcher/Launcher.qml+4−4
@@ -73,7 +73,7 @@ Item {
73 id: dismissTimer
73 id: dismissTimer
74 interval: 5000
74 interval: 5000
75 onTriggered: {
75 onTriggered: {
76− if (!panel.moving) {
76+ if (!panel.preventHiding) {
77 root.state = ""
77 root.state = ""
78 } else {
78 } else {
79 dismissTimer.restart()
79 dismissTimer.restart()
@@ -135,9 +135,9 @@ Item {
135 id: backgroundShade
135 id: backgroundShade
136 anchors.fill: parent
136 anchors.fill: parent
137 color: "black"
137 color: "black"
138− opacity: root.state == "visible" ? 0.4 : 0
138+ opacity: root.state == "visible" ? 0.6 : 0
139
139
140− Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuad} }
140+ Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
141 }
141 }
142
142
143 LauncherPanel {
143 LauncherPanel {
@@ -162,7 +162,7 @@ Item {
162 root.dashItemSelected(index)
162 root.dashItemSelected(index)
163 }
163 }
164
164
165− onMovingChanged: {
165+ onPreventHidingChanged: {
166 if (dismissTimer.running) {
166 if (dismissTimer.running) {
167 dismissTimer.restart();
167 dismissTimer.restart();
168 }
168 }
Launcher/LauncherDelegate.qml+89−175
@@ -21,29 +21,33 @@ Item {
21 id: root
21 id: root
22
22
23 property string iconName
23 property string iconName
24−
24+ property int count: -1
25− property real angle: 0
25+ property int progress: -1
26 property bool highlighted: false
26 property bool highlighted: false
27− property real offset: 0
28− property alias brightness: transformEffect.brightness
29− property real itemOpacity: 1
30−
31 property real maxAngle: 0
27 property real maxAngle: 0
32 property bool inverted: false
28 property bool inverted: false
33
29
34− readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * height
30+ readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
35− readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * height
31+ readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
36
32
37− property bool dragging:false
33+ property int itemWidth
34+ property int itemHeight
35+ // The angle used for rotating
36+ property real angle: 0
37+ // This is the offset that keeps the items inside the panel
38+ property real offset: 0
39+ property real itemOpacity: 1
40+ property real brightness: 0
38
41
39− signal clicked()
42+ onIconNameChanged: shaderEffectSource.scheduleUpdate();
40− signal longtap()
43+ onCountChanged: shaderEffectSource.scheduleUpdate();
41− signal released()
44+ onProgressChanged: shaderEffectSource.scheduleUpdate();
45+ onHighlightedChanged: shaderEffectSource.scheduleUpdate();
42
46
43 Item {
47 Item {
44 id: iconItem
48 id: iconItem
45− width: parent.width
49+ width: parent.itemWidth
46− height: parent.height
50+ height: parent.itemHeight
47 anchors.centerIn: parent
51 anchors.centerIn: parent
48
52
49 UbuntuShape {
53 UbuntuShape {
@@ -58,31 +62,79 @@ Item {
58 sourceSize.height: iconShape.height
62 sourceSize.height: iconShape.height
59 source: "../graphics/applicationIcons/" + root.iconName + ".png"
63 source: "../graphics/applicationIcons/" + root.iconName + ".png"
60 }
64 }
61−
62− MouseArea {
63− id: mouseArea
64− anchors.fill: parent
65− onClicked: root.clicked()
66− onCanceled: root.released()
67− preventStealing: false
68−
69− onPressAndHold: {
70− root.state = "moving"
71− }
72− onReleased: {
73− root.state = "docked"
74− }
75− }
76 }
65 }
66+
77 BorderImage {
67 BorderImage {
78 id: overlayHighlight
68 id: overlayHighlight
79 anchors.centerIn: iconItem
69 anchors.centerIn: iconItem
80 rotation: inverted ? 180 : 0
70 rotation: inverted ? 180 : 0
81− source: isSelected ? "graphics/selected.sci" : "graphics/non-selected.sci"
71+ source: root.highlighted ? "graphics/selected.sci" : "graphics/non-selected.sci"
82− width: root.width + units.gu(0.5)
72+ width: root.itemWidth + units.gu(0.5)
73+ height: root.itemHeight + units.gu(0.5)
74+ }
75+
76+ BorderImage {
77+ objectName: "countEmblem"
78+ anchors {
79+ right: parent.right
80+ top: parent.top
81+ margins: -units.dp(1)
82+ }
83+ width: Math.min(units.gu(7.5), Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
83− height: width
84+ height: units.gu(3)
84− property bool isSelected: root.highlighted || mouseArea.pressed
85+ source: "graphics/notification.sci"
85− onIsSelectedChanged: shaderEffectSource.scheduleUpdate();
86+ visible: root.count > 0
87+
88+ Label {
89+ id: countLabel
90+ text: root.count
91+ anchors.centerIn: parent
92+ width: units.gu(6)
93+ horizontalAlignment: Text.AlignHCenter
94+ elide: Text.ElideRight
95+ color: "white"
96+ fontSize: "small"
97+ font.bold: true
98+ }
99+ }
100+
101+ BorderImage {
102+ id: progressOverlay
103+ objectName: "progressOverlay"
104+ anchors {
105+ left: iconItem.left
106+ right: iconItem.right
107+ bottom: iconItem.bottom
108+ leftMargin: units.gu(0.5)
109+ rightMargin: units.gu(0.5)
110+ bottomMargin: units.gu(0.5)
111+ }
112+ height: units.gu(1.5)
113+ visible: root.progress > -1
114+ source: "graphics/progressbar-trough.sci"
115+
116+ // For fill calculation we need to remove the 2 units of border defined in .sci file
117+ property int adjustedWidth: width - units.gu(2)
118+
119+ Item {
120+ anchors {
121+ left: parent.left
122+ top: parent.top
123+ bottom: parent.bottom
124+ }
125+ width: Math.min(100, root.progress) / 100 * parent.adjustedWidth + units.gu(1)
126+ clip: true
127+
128+ BorderImage {
129+ anchors {
130+ left: parent.left
131+ top: parent.top
132+ bottom: parent.bottom
133+ }
134+ width: progressOverlay.width
135+ source: "graphics/progressbar-fill.sci"
136+ }
137+ }
86 }
138 }
87 }
139 }
88
140
@@ -90,14 +142,13 @@ Item {
90 id: transformEffect
142 id: transformEffect
91 anchors.centerIn: parent
143 anchors.centerIn: parent
92 anchors.verticalCenterOffset: root.offset
144 anchors.verticalCenterOffset: root.offset
93− width: parent.width
145+ width: parent.itemWidth
94− height: parent.height
146+ height: parent.itemHeight
95 property real itemOpacity: root.itemOpacity
147 property real itemOpacity: root.itemOpacity
96− property real brightness: 0
148+ property real brightness: Math.max(-1, root.brightness)
97 property real angle: root.angle
149 property real angle: root.angle
98 rotation: root.inverted ? 180 : 0
150 rotation: root.inverted ? 180 : 0
99
151
100−
101 property variant source: ShaderEffectSource {
152 property variant source: ShaderEffectSource {
102 id: shaderEffectSource
153 id: shaderEffectSource
103 sourceItem: iconItem
154 sourceItem: iconItem
@@ -149,141 +200,4 @@ Item {
149 gl_FragColor = sourceColor;
200 gl_FragColor = sourceColor;
150 }"
201 }"
151 }
202 }
152−
153− QtObject {
154− id: priv
155−
156− property ListView listView: root.ListView.view
157− property real totalUnfoldedHeight: listView.itemSize + listView.spacing
158− property real totalEffectiveHeight: effectiveHeight + listView.spacing
159− property real effectiveContentY: listView.contentY - listView.originY
160− property real effectiveY: y - listView.originY
161− property real distanceFromTopEdge: -(effectiveContentY + listView.topMargin - index*totalUnfoldedHeight)
162− property real distanceFromBottomEdge: listView.height - listView.bottomMargin - (effectiveY+height) + effectiveContentY
163−
164− property real distanceFromEdge: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? distanceFromBottomEdge : distanceFromTopEdge
165− property real orientationFlag: Math.abs(distanceFromBottomEdge) < Math.abs(distanceFromTopEdge) ? -1 : 1
166−
167− property real overlapWithFoldingArea: listView.foldingAreaHeight - distanceFromEdge
168− }
169−
170− states: [
171− State {
172− name: "docked"
173− PropertyChanges {
174− target: root
175−
176− // This is the offset that keeps the items inside the panel
177− offset: {
178− // First/last items are special
179− if (index == 0 || index == priv.listView.count-1) {
180− // Just keep them bound to the edges in case they're outside of the visible area
181− if (priv.distanceFromEdge < 0) {
182− return (-priv.distanceFromEdge - (height - effectiveHeight)) * priv.orientationFlag;
183− }
184− return 0;
185− }
186−
187− // Are we already completely outside the flickable? Stop the icon here.
188− if (priv.distanceFromEdge < -priv.totalUnfoldedHeight) {
189− return (-priv.distanceFromEdge - (root.height - effectiveHeight)) * priv.orientationFlag;
190− }
191−
192− // We're touching the edge, move slower than the actual flicking speed.
193− if (priv.distanceFromEdge < 0) {
194− return (Math.abs(priv.distanceFromEdge) * priv.totalEffectiveHeight / priv.totalUnfoldedHeight) * priv.orientationFlag
195− }
196− return 0;
197− }
198−
199− // The angle used for rotating
200− angle: {
201− // First/last items are special
202− if (index == 0 || index == priv.listView.count-1) {
203− if (priv.distanceFromEdge < 0) {
204− // proportion equation: distanceFromTopEdge : angle = totalUnfoldedHeight/2 : maxAngle
205− return Math.max(-maxAngle, priv.distanceFromEdge * maxAngle / (priv.listView.foldingAreaHeight)) * priv.orientationFlag
206− }
207− return 0; // Don't fold first/last item as long as inside the view
208− }
209−
210− // Are we in the already completely outside the flickable? Fold for the last 5 degrees
211− if (priv.distanceFromEdge < 0) {
212− // proportion equation: -distanceFromTopEdge : angle = totalUnfoldedHeight : 5
213− return Math.max(-maxAngle, (priv.distanceFromEdge * 5 / priv.listView.foldingAreaHeight) - (maxAngle-5)) * priv.orientationFlag
214− }
215−
216− // We are overlapping with the folding area, fold the icon to maxAngle - 5 degrees
217− if (priv.overlapWithFoldingArea > 0) {
218− // proportion equation: overlap: totalHeight = angle : (maxAngle - 5)
219− return -priv.overlapWithFoldingArea * (maxAngle -5) / priv.listView.foldingAreaHeight * priv.orientationFlag;
220− }
221− return 0;
222− }
223−
224− itemOpacity: {
225− // First/last items are special
226− if (index == 0 || index == priv.listView.count-1) {
227− if (priv.distanceFromEdge < 0) {
228− // Fade from 1 to 0 in the distance of 3 * foldingAreaHeight (which is when the next item reaches the edge)
229− return 1.0 - (-priv.distanceFromEdge / (priv.listView.foldingAreaHeight * 3))
230− }
231− return 1; // Don't make first/last item transparent as long as inside the view
232− }
233−
234− // Are we already completely outside the flickable? Fade from 0.75 to 0 in 2 items height
235− if (priv.distanceFromEdge < 0) {
236− // proportion equation: -distanceFromEdge : 1-opacity = totalUnfoldedHeight : 0.75
237− return 0.75 - (-priv.distanceFromEdge * 0.75 / (priv.totalUnfoldedHeight*2))
238− }
239−
240− // We are overlapping with the folding area, fade out to 0.75
241− if (priv.overlapWithFoldingArea > 0) {
242− // proportion equation: overlap : totalHeight = 1-opacity : 0.25
243− return 1 - (priv.overlapWithFoldingArea * 0.25 / priv.listView.foldingAreaHeight)
244− }
245− return 1;
246− }
247−
248− brightness: {
249− // First/last items are special
250− if (index == 0 || index == priv.listView.count-1) {
251− if (priv.distanceFromEdge < 0) {
252− return -(-priv.distanceFromEdge / (priv.listView.foldingAreaHeight * 3))
253− }
254− return 0;
255− }
256− // Are we already completely outside the flickable? Fade from 0.7 to 0 in 2 items height
257− if (priv.distanceFromEdge < 0) {
258− return -0.3 - (-priv.distanceFromEdge * 0.1 / (priv.totalUnfoldedHeight*2))
259− }
260−
261− // We are overlapping with the folding area, fade out to 0.7
262− if (priv.overlapWithFoldingArea > 0) {
263− return - (priv.overlapWithFoldingArea * 0.3 / priv.listView.foldingAreaHeight)
264− }
265− return 0;
266− }
267− }
268− },
269−
270− State {
271− name: "moving"
272− PropertyChanges {
273− target: launcherDelegate;
274− offset: 0
275− angle: 0
276− }
277− PropertyChanges {
278− target: root
279− highlighted: true
280− dragging: true
281− }
282− PropertyChanges {
283− target: mouseArea
284− preventStealing: true
285− drag.target: root
286− }
287− }
288− ]
289}
203}
Launcher/LauncherPanel.qml+415−91
@@ -16,8 +16,10 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import Ubuntu.Components.ListItems 0.1 as ListItems
19import Unity 0.1
20import Unity 0.1
20import Unity.Launcher 0.1
21import Unity.Launcher 0.1
22+import Ubuntu.Components.Popups 0.1
21import "../Components/ListItems"
23import "../Components/ListItems"
22
24
23Item {
25Item {
@@ -29,53 +31,33 @@ Item {
29 property bool inverted: true
31 property bool inverted: true
30 property bool dragging: false
32 property bool dragging: false
31 property bool moving: launcherListView.moving || launcherListView.flicking
33 property bool moving: launcherListView.moving || launcherListView.flicking
32− property int dragPosition: 0
34+ property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
33 property int highlightIndex: -1
35 property int highlightIndex: -1
34
36
35 signal applicationSelected(string desktopFile)
37 signal applicationSelected(string desktopFile)
36 signal dashItemSelected(int index)
38 signal dashItemSelected(int index)
37
39
38− onDragPositionChanged: {
39− var effectiveDragPosition = root.inverted ? launcherListView.height - dragPosition : dragPosition - mainColumn.anchors.margins
40−
41− var hiddenContentHeight = launcherListView.contentHeight - launcherListView.height
42− // Shortening scrollable height because the first/last item needs to be fully expanded before reaching the top/bottom
43− var scrollableHeight = launcherListView.height - (launcherListView.itemSize + launcherColumn.spacing) *2
44− // As we shortened the scrollableHeight, lets move everything down by the itemSize
45− var shortenedEffectiveDragPosition = effectiveDragPosition - launcherListView.itemSize - launcherColumn.spacing
46− var newContentY = shortenedEffectiveDragPosition * hiddenContentHeight / scrollableHeight
47−
48− // limit top/bottom to prevent overshooting
49− launcherListView.contentY = Math.min(hiddenContentHeight, Math.max(0, newContentY));
50−
51− // Now calculate the current index:
52− // > the current mouse position + the hidden/scolled content on top is the mouse position in the averall view
53− // > adjust that removing all the margins
54− // > divide by itemSize to get index
55− highlightIndex = (effectiveDragPosition + launcherListView.contentY - mainColumn.anchors.margins*3 - launcherColumn.spacing/2) / (launcherListView.itemSize + launcherColumn.spacing)
56− }
57−
58 BorderImage {
40 BorderImage {
59 id: background
41 id: background
60 source: "graphics/launcher_bg.sci"
42 source: "graphics/launcher_bg.sci"
61 anchors.fill: parent
43 anchors.fill: parent
44+ anchors.rightMargin: root.inverted ? 0 : -units.gu(1)
45+ anchors.leftMargin: root.inverted ? -units.gu(1) : 0
46+ rotation: root.rotation
62 }
47 }
63
48
64 Column {
49 Column {
65 id: mainColumn
50 id: mainColumn
66 anchors {
51 anchors {
67 fill: parent
52 fill: parent
68− topMargin: units.gu(0.5)
69− bottomMargin: units.gu(1)
70 leftMargin: units.gu(0.5)
53 leftMargin: units.gu(0.5)
71 rightMargin: units.gu(0.5)
54 rightMargin: units.gu(0.5)
72 }
55 }
73− spacing: units.gu(0.5)
74
56
75 MouseArea {
57 MouseArea {
76 id: dashItem
58 id: dashItem
77 width: parent.width
59 width: parent.width
78− height: units.gu(6.5)
60+ height: units.gu(7)
79 onClicked: root.dashItemSelected(0)
61 onClicked: root.dashItemSelected(0)
80 z: 1
62 z: 1
81 Image {
63 Image {
@@ -100,87 +82,429 @@ Item {
100 anchors.left: parent.left
82 anchors.left: parent.left
101 anchors.right: parent.right
83 anchors.right: parent.right
102 height: parent.height - dashItem.height - parent.spacing*2
84 height: parent.height - dashItem.height - parent.spacing*2
103− ListView {
85+
104− id: launcherListView
86+ Item {
105− objectName: "launcherListView"
106 anchors.fill: parent
87 anchors.fill: parent
88+ clip: true
89+
90+ ListView {
91+ id: launcherListView
92+ objectName: "launcherListView"
107− anchors.topMargin: -itemSize
93+ anchors {
94+ fill: parent
95+ topMargin: -extensionSize + units.gu(0.5)
108− anchors.bottomMargin: -itemSize
96+ bottomMargin: -extensionSize + units.gu(1)
97+ }
109− topMargin: itemSize
98+ topMargin: extensionSize
110− bottomMargin: itemSize
99+ bottomMargin: extensionSize
111− height: parent.height - dashItem.height - parent.spacing*2
100+ height: parent.height - dashItem.height - parent.spacing*2
112− model: root.model
101+ model: root.model
113− cacheBuffer: itemSize * 3
102+ cacheBuffer: itemHeight * 3
114− snapMode: ListView.SnapToItem
103+ snapMode: interactive ? ListView.SnapToItem : ListView.NoSnap
115− highlightRangeMode: ListView.ApplyRange
104+ highlightRangeMode: ListView.ApplyRange
116− preferredHighlightBegin: (height - itemSize) / 2
105+ preferredHighlightBegin: (height - itemHeight) / 2
117− preferredHighlightEnd: (height + itemSize) / 2
106+ preferredHighlightEnd: (height + itemHeight) / 2
118−
107+ spacing: units.gu(0.5)
108+
109+ // The size of the area the ListView is extended to make sure items are not
110+ // destroyed when dragging them outside the list. This needs to be at least
111+ // itemHeight to prevent folded items from disappearing and DragArea limits
112+ // need to be smaller than this size to avoid breakage.
113+ property int extensionSize: itemHeight * 3
114+
119− // The height of the area where icons start getting folded
115+ // The height of the area where icons start getting folded
120− property int foldingAreaHeight: itemSize * 0.75
116+ property int foldingAreaHeight: itemHeight * 0.75
121− property int itemSize: width
117+ property int itemWidth: width
118+ property int itemHeight: width * 7.5 / 8
122− property int clickFlickSpeed: units.gu(60)
119+ property int clickFlickSpeed: units.gu(60)
123−
120+ property int draggedIndex: dndArea.draggedIndex
124− delegate: LauncherDelegate {
121+ property real realContentY: contentY - originY + topMargin
122+ property int realItemHeight: itemHeight + spacing
123+
124+ // In case the start dragging transition is running, we need to delay the
125+ // move because the displaced transition would clash with it and cause items
126+ // to be moved to wrong places
127+ property bool draggingTransitionRunning: false
128+ property int scheduledMoveTo: -1
129+
130+ displaced: Transition {
131+ NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
132+ }
133+
134+ delegate: FoldingLauncherDelegate {
125− id: launcherDelegate
135+ id: launcherDelegate
126− objectName: "launcherDelegate" + index
136+ objectName: "launcherDelegate" + index
127− width: launcherListView.itemSize
137+ itemHeight: launcherListView.itemHeight
128− height: launcherListView.itemSize
138+ itemWidth: launcherListView.itemWidth
139+ width: itemWidth
140+ height: itemHeight
129− iconName: model.icon
141+ iconName: model.icon
142+ count: model.count
143+ progress: model.progress
130− inverted: root.inverted
144+ inverted: root.inverted
131− highlighted: root.dragging && index === root.highlightIndex
145+ highlighted: dragging && index === root.highlightIndex
132− z: -Math.abs(offset)
146+ z: -Math.abs(offset)
133− state: "docked"
134− maxAngle: 60
147+ maxAngle: 60
135−
148+ property bool dragging: false
149+
150+ ThinDivider {
151+ id: dropIndicator
152+ objectName: "dropIndicator"
153+ anchors.centerIn: parent
154+ width: parent.width + mainColumn.anchors.leftMargin + mainColumn.anchors.rightMargin
155+ opacity: 0
156+ }
157+
158+ states: [
159+ State {
160+ name: "selected"
161+ when: dndArea.selectedItem === launcherDelegate && fakeDragItem.visible && !dragging
162+ PropertyChanges {
163+ target: launcherDelegate
164+ itemOpacity: 0
165+ }
166+ },
167+ State {
168+ name: "dragging"
169+ when: dragging
170+ PropertyChanges {
171+ target: launcherDelegate
172+ height: units.gu(1)
173+ itemOpacity: 0
174+ }
175+ PropertyChanges {
176+ target: dropIndicator
177+ opacity: 1
178+ }
179+ },
180+ State {
181+ name: "expanded"
182+ when: dndArea.draggedIndex >= 0 && (dndArea.preDragging || dndArea.dragging || dndArea.postDragging) && dndArea.draggedIndex != index
183+ PropertyChanges {
184+ target: launcherDelegate
185+ angle: 0
186+ offset: 0
187+ itemOpacity: 0.6
188+ }
189+ }
190+ ]
191+
192+ transitions: [
193+ Transition {
194+ from: ""
195+ to: "selected"
196+ NumberAnimation { properties: "itemOpacity"; duration: UbuntuAnimation.FastDuration }
197+ },
198+ Transition {
199+ from: "*"
200+ to: "expanded"
201+ NumberAnimation { properties: "itemOpacity"; duration: UbuntuAnimation.FastDuration }
202+ UbuntuNumberAnimation { properties: "angle,offset" }
203+ },
204+ Transition {
205+ from: "expanded"
206+ to: ""
207+ NumberAnimation { properties: "itemOpacity"; duration: UbuntuAnimation.BriskDuration }
208+ UbuntuNumberAnimation { properties: "angle,offset" }
209+ },
210+ Transition {
211+ id: draggingTransition
212+ from: "selected"
213+ to: "dragging"
214+ SequentialAnimation {
215+ PropertyAction { target: launcherListView; property: "draggingTransitionRunning"; value: true }
216+ ParallelAnimation {
217+ UbuntuNumberAnimation { properties: "height" }
218+ NumberAnimation { target: dropIndicator; properties: "opacity"; duration: UbuntuAnimation.FastDuration }
219+ }
220+ ScriptAction {
221+ script: {
222+ if (launcherListView.scheduledMoveTo > -1) {
223+ launcherListView.model.move(dndArea.draggedIndex, launcherListView.scheduledMoveTo)
224+ dndArea.draggedIndex = launcherListView.scheduledMoveTo
225+ launcherListView.scheduledMoveTo = -1
226+ }
227+ }
228+ }
229+ PropertyAction { target: launcherListView; property: "draggingTransitionRunning"; value: false }
230+ }
231+ },
232+ Transition {
233+ from: "dragging"
234+ to: "*"
235+ NumberAnimation { target: dropIndicator; properties: "opacity"; duration: UbuntuAnimation.FastDuration }
236+ NumberAnimation { properties: "itemOpacity"; duration: UbuntuAnimation.BriskDuration }
237+ SequentialAnimation {
238+ UbuntuNumberAnimation { properties: "height" }
239+ PropertyAction { target: dndArea; property: "postDragging"; value: false }
240+ PropertyAction { target: dndArea; property: "draggedIndex"; value: -1 }
241+ }
242+ }
243+ ]
244+ }
245+
246+ MouseArea {
247+ id: dndArea
248+ objectName: "dndArea"
249+ anchors {
250+ fill: parent
251+ topMargin: launcherListView.topMargin
252+ bottomMargin: launcherListView.bottomMargin
253+ }
254+ drag.minimumY: -launcherListView.topMargin
255+ drag.maximumY: height + launcherListView.bottomMargin
256+
257+ property int draggedIndex: -1
258+ property var selectedItem
259+ property bool preDragging: false
260+ property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging
261+ property bool postDragging: false
262+ property int startX
263+ property int startY
264+ property var quickListPopover: null
265+
266+ onPressed: {
267+ selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
268+ if (selectedItem !== null) {
269+ selectedItem.highlighted = true
270+ }
271+ }
272+
136− onClicked: {
273+ onClicked: {
274+ var index = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
275+ var clickedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
276+
277+ // Check if we actually clicked an item or only at the spacing in between
278+ if (clickedItem === null) {
279+ return;
280+ }
281+
137− // First/last item do the scrolling at more than 12 degrees
282+ // First/last item do the scrolling at more than 12 degrees
138− if (index == 0 || index == launcherListView.count -1) {
283+ if (index == 0 || index == launcherListView.count - 1) {
284+ if (clickedItem.angle > 12) {
285+ launcherListView.flick(0, -launcherListView.clickFlickSpeed);
286+ } else if (clickedItem.angle < -12) {
287+ launcherListView.flick(0, launcherListView.clickFlickSpeed);
288+ } else {
289+ root.applicationSelected(LauncherModel.get(index).desktopFile);
290+ }
291+ return;
292+ }
293+
294+ // the rest launches apps up to an angle of 30 degrees
139− if (angle > 12) {
295+ if (clickedItem.angle > 30) {
140 launcherListView.flick(0, -launcherListView.clickFlickSpeed);
296 launcherListView.flick(0, -launcherListView.clickFlickSpeed);
141− } else if (angle < -12) {
297+ } else if (clickedItem.angle < -30) {
142 launcherListView.flick(0, launcherListView.clickFlickSpeed);
298 launcherListView.flick(0, launcherListView.clickFlickSpeed);
143 } else {
299 } else {
144 root.applicationSelected(LauncherModel.get(index).desktopFile);
300 root.applicationSelected(LauncherModel.get(index).desktopFile);
145 }
301 }
146− return;
147 }
302 }
148
303
149− // the rest launches apps up to an angle of 30 degrees
304+ onCanceled: {
150− if (angle > 30) {
305+ selectedItem.highlighted = false
151− launcherListView.flick(0, -launcherListView.clickFlickSpeed);
306+ selectedItem = undefined;
152− } else if (angle < -30) {
307+ preDragging = false;
153− launcherListView.flick(0, launcherListView.clickFlickSpeed);
308+ postDragging = false;
154− } else {
309+ }
155− root.applicationSelected(LauncherModel.get(index).desktopFile);
310+
311+ onReleased: {
312+ var droppedIndex = draggedIndex;
313+ if (dragging) {
314+ postDragging = true;
315+ } else {
316+ draggedIndex = -1;
317+ }
318+
319+ if (!selectedItem) {
320+ return;
321+ }
322+
323+ selectedItem.highlighted = false
324+ selectedItem.dragging = false;
325+ selectedItem = undefined;
326+ preDragging = false;
327+
328+ drag.target = undefined
329+
330+ progressiveScrollingTimer.stop();
331+ launcherListView.interactive = true;
332+ if (droppedIndex >= launcherListView.count - 2 && postDragging) {
333+ launcherListView.flick(0, -launcherListView.clickFlickSpeed);
334+ }
335+ }
336+
337+ onPressAndHold: {
338+ if (Math.abs(selectedItem.angle) > 30) {
339+ return;
340+ }
341+
342+ draggedIndex = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
343+
344+ // Opening QuickList
345+ var quickListModel = launcherListView.model.get(draggedIndex).quickList
346+ var quickListAppId = launcherListView.model.get(draggedIndex).appId
347+
348+ quickListPopover = PopupUtils.open(popoverComponent, selectedItem,
349+ {model: quickListModel, appId: quickListAppId})
350+
351+ launcherListView.interactive = false
352+
353+ var yOffset = draggedIndex > 0 ? (mouseY + launcherListView.realContentY) % (draggedIndex * launcherListView.realItemHeight) : mouseY + launcherListView.realContentY
354+
355+ fakeDragItem.iconName = launcherListView.model.get(draggedIndex).icon
356+ fakeDragItem.x = 0
357+ fakeDragItem.y = mouseY - yOffset + launcherListView.anchors.topMargin + launcherListView.topMargin
358+ fakeDragItem.angle = selectedItem.angle * (root.inverted ? -1 : 1)
359+ fakeDragItem.offset = selectedItem.offset * (root.inverted ? -1 : 1)
360+ fakeDragItem.count = LauncherModel.get(draggedIndex).count
361+ fakeDragItem.progress = LauncherModel.get(draggedIndex).progress
362+ fakeDragItem.flatten()
363+ drag.target = fakeDragItem
364+
365+ startX = mouseX
366+ startY = mouseY
367+ }
368+
369+ onPositionChanged: {
370+ if (draggedIndex >= 0) {
371+ if (!selectedItem.dragging) {
372+ var distance = Math.max(Math.abs(mouseX - startX), Math.abs(mouseY - startY))
373+ if (!preDragging && distance > units.gu(1.5)) {
374+ preDragging = true;
375+ PopupUtils.close(quickListPopover)
376+ }
377+ if (distance > launcherListView.itemHeight) {
378+ selectedItem.dragging = true
379+ preDragging = false;
380+ }
381+ }
382+ if (!selectedItem.dragging) {
383+ return
384+ }
385+
386+ var itemCenterY = fakeDragItem.y + fakeDragItem.height / 2
387+
388+ // Move it down by the the missing size to compensate index calculation with only expanded items
389+ itemCenterY += (launcherListView.itemHeight - selectedItem.height) / 2
390+
391+ if (mouseY > launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin - launcherListView.realItemHeight) {
392+ progressiveScrollingTimer.downwards = false
393+ progressiveScrollingTimer.start()
394+ } else if (mouseY < launcherListView.realItemHeight) {
395+ progressiveScrollingTimer.downwards = true
396+ progressiveScrollingTimer.start()
397+ } else {
398+ progressiveScrollingTimer.stop()
399+ }
400+
401+ var newIndex = (itemCenterY + launcherListView.realContentY) / launcherListView.realItemHeight
402+
403+ if (newIndex > draggedIndex + 1) {
404+ newIndex = draggedIndex + 1
405+ } else if (newIndex < draggedIndex) {
406+ newIndex = draggedIndex -1
407+ } else {
408+ return
409+ }
410+
411+ if (newIndex >= 0 && newIndex < launcherListView.count) {
412+ if (launcherListView.draggingTransitionRunning) {
413+ launcherListView.scheduledMoveTo = newIndex
414+ } else {
415+ launcherListView.model.move(draggedIndex, newIndex)
416+ draggedIndex = newIndex
417+ }
418+ }
419+ }
420+ }
421+ }
422+ Timer {
423+ id: progressiveScrollingTimer
424+ interval: 2
425+ repeat: true
426+ running: false
427+ property bool downwards: true
428+ onTriggered: {
429+ if (downwards) {
430+ var minY = -launcherListView.topMargin
431+ if (launcherListView.contentY > minY) {
432+ launcherListView.contentY = Math.max(launcherListView.contentY - units.dp(2), minY)
433+ }
434+ } else {
435+ var maxY = launcherListView.contentHeight - launcherListView.height + launcherListView.topMargin + launcherListView.originY
436+ if (launcherListView.contentY < maxY) {
437+ launcherListView.contentY = Math.min(launcherListView.contentY + units.dp(2), maxY)
438+ }
439+ }
156 }
440 }
157 }
441 }
158 }
442 }
443+ }
159
444
160− MouseArea {
445+ LauncherDelegate {
161− id: topFoldingArea
446+ id: fakeDragItem
162− anchors {
447+ objectName: "fakeDragItem"
163− left: parent.left
448+ visible: dndArea.draggedIndex >= 0 && !dndArea.postDragging
164− right: parent.right
449+ itemWidth: launcherListView.itemWidth
165− top: parent.top
166− topMargin: launcherListView.topMargin
450+ itemHeight: launcherListView.itemHeight
167− }
451+ height: itemHeight
168− height: launcherListView.itemSize / 2
452+ width: itemWidth
169− enabled: launcherListView.contentY > -launcherListView.topMargin
453+ rotation: root.rotation
170− onClicked: launcherListView.flick(0, launcherListView.clickFlickSpeed)
454+ highlighted: true
455+ itemOpacity: 0.8
456+
457+ function flatten() {
458+ fakeDragItemAnimation.start();
171 }
459 }
172
460
173− MouseArea {
461+ UbuntuNumberAnimation {
174− id: bottomFoldingArea
462+ id: fakeDragItemAnimation
175− anchors {
463+ target: fakeDragItem;
176− left: parent.left
464+ properties: "angle,offset";
177− right: parent.right
465+ to: 0
178− bottom: parent.bottom
466+ }
179− bottomMargin: launcherListView.bottomMargin
467+ }
468+ }
469+ }
470+
471+ Component {
472+ id: popoverComponent
473+
474+ Popover {
475+ id: popover
476+ property var model
477+ property string appId
478+ contentWidth: quickListColumn.width
479+
480+ // FIXME: There's a bug in the Popover positioning that it covers the item in case it is rotated.
481+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1204470
482+ // For now, let's move the Popover around with callerMargin.
483+ // Remove popupMargin once the bug is fixed.
484+ property int popupMargin: root.inverted ? launcherListView.itemHeight : 0;
485+
486+ callerMargin: units.gu(1) + popupMargin
487+
488+ Column {
489+ id: quickListColumn
490+ width: units.gu(30)
491+ height: childrenRect.height
492+
493+ Repeater {
494+ model: popover.model
495+
496+ ListItems.Standard {
497+ objectName: "quickListEntry" + index
498+ text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
499+ highlightWhenPressed: model.clickable
500+ onClicked: {
501+ if (!model.clickable) {
502+ return;
503+ }
504+ LauncherModel.quickListActionInvoked(appId, index);
505+ PopupUtils.close(popover);
506+ }
180 }
507 }
181− height: launcherListView.itemSize / 2
182− enabled: launcherListView.contentHeight - launcherListView.height - launcherListView.contentY > -launcherListView.bottomMargin
183− onClicked: launcherListView.flick(0, -launcherListView.clickFlickSpeed)
184 }
508 }
185 }
509 }
186 }
510 }
Launcher/graphics/launcher_bg@18.pngdeleted

Binary file not shown.

Launcher/graphics/launcher_bg@18.scideleted−5
@@ -1,5 +0,0 @@
1−border.left: 0
2−border.top: 0
3−border.bottom: 0
4−border.right: 24
5−source: launcher_bg@18.png
Launcher/graphics/launcher_bg@30.pngnew file

Binary file not shown.

Launcher/graphics/launcher_bg@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 4
2+border.top: 4
3+border.bottom: 4
4+border.right: 66
5+source: launcher_bg@30.png
Launcher/graphics/notification@30.pngnew file

Binary file not shown.

Launcher/graphics/notification@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 36
2+border.top: 36
3+border.bottom: 36
4+border.right: 36
5+source: notification@30.png
Launcher/graphics/progressbar-fill@30.pngnew file

Binary file not shown.

Launcher/graphics/progressbar-fill@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 36
2+border.top: 16
3+border.bottom: 28
4+border.right: 36
5+source: progressbar-fill@30.png
Launcher/graphics/progressbar-trough@30.pngnew file

Binary file not shown.

Launcher/graphics/progressbar-trough@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 36
2+border.top: 16
3+border.bottom: 26
4+border.right: 36
5+source: progressbar-trough@30.png
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+2−10
@@ -33,7 +33,7 @@ Indicators.IndicatorsModel {
33 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
33 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
34 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
34 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
35 },
35 },
36− 'indicator-messaging' : {
36+ 'indicator-messages' : {
37 'title': 'Messaging',
37 'title': 'Messaging',
38 'description' : '',
38 'description' : '',
39 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/MessagingIndicatorWidget.qml',
39 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/MessagingIndicatorWidget.qml',
@@ -49,7 +49,7 @@ Indicators.IndicatorsModel {
49 'title': 'Networks',
49 'title': 'Networks',
50 'description' : '',
50 'description' : '',
51 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
51 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
52− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
52+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage2.qml'
53 },
53 },
54 'indicator-power' : {
54 'indicator-power' : {
55 'title': 'Battery',
55 'title': 'Battery',
@@ -68,14 +68,6 @@ Indicators.IndicatorsModel {
68 'description' : '',
68 'description' : '',
69 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
69 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
70 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
70 '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 }
71 }
80 }
72 }
81}
73}
Shell.qml+5−3
@@ -497,11 +497,13 @@ FocusScope {
497 }
497 }
498 }
498 }
499
499
500− onPowerStateChange: {
500+ onDisplayPowerStateChange: {
501− if (state == 0) { // suspend
501+ // We ignore any display-off signals when the proximity sensor
502+ // is active. This usually indicates something like a phone call.
503+ if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
502 powerConnection.setFocused(false);
504 powerConnection.setFocused(false);
503 greeter.show();
505 greeter.show();
504− } else if (state == 1) { // active
506+ } else if (status == Powerd.On) {
505 powerConnection.setFocused(true);
507 powerConnection.setFocused(true);
506 }
508 }
507 }
509 }
build+5−2
@@ -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
debian/changelog+106
@@ -1,3 +1,109 @@
1+unity8 (7.81.3+13.10.20130821.2-0ubuntu1) saucy; urgency=low
2+
3+ [ Michael Terry ]
4+ * Listen to display-power-changed rather than system-power-changed
5+ signals when showing the greeter; ignore such signals when the
6+ proximity sensor is active. (LP: #1214477)
7+
8+ [ Pawel Stolowski ]
9+ * Added a role for accessing progress-source property of categories.
10+
11+ [ Ubuntu daily release ]
12+ * Automatic snapshot from revision 233
13+
14+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 22:09:53 +0000
15+
16+unity8 (7.81.3+13.10.20130821.1-0ubuntu1) saucy; urgency=low
17+
18+ [ Michael Zanetti ]
19+ * allow testing at 11:13. Old code failed because the text actually
20+ says "11:13 AM".
21+ * add some checks if we actually clicked an item or in the spacing
22+ between them gets rid of some warnings printed by the launcher .
23+
24+ [ Michał Sawicz ]
25+ * Update runtime deps in the build script.
26+
27+ [ Nick Dedekind ]
28+ * Added/Updated legacy network indicator components to use with new
29+ indicator backend.
30+
31+ [ Albert Astals ]
32+ * Fix insertions/removals on the qlimitproxymodel . (LP: #1213959)
33+
34+ [ Ubuntu daily release ]
35+ * Automatic snapshot from revision 230
36+
37+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 15:04:59 +0000
38+
39+unity8 (7.81.3+13.10.20130821-0ubuntu1) saucy; urgency=low
40+
41+ [ Michael Zanetti ]
42+ * added support for count emblems and progress overlays on the
43+ launcher.
44+
45+ [ Pawel Stolowski ]
46+ * Add role for getting filter options model. Add method to activate
47+ option based on index or id.
48+
49+ [ Ted Gould ]
50+ * Mark indicators-client as providing an indicator-renderer.
51+
52+ [ Nick Dedekind ]
53+ * Added flatmenuproxymodel pass-through signals from qmenumodel.
54+ * Renamed indicator-messaging to indicator-messages.
55+
56+ [ Gerry Boland ]
57+ * Typo fix in FrequentlyUsedAppsModel.
58+
59+ [ Ubuntu daily release ]
60+ * Automatic snapshot from revision 224
61+
62+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 08:05:13 +0000
63+
64+unity8 (7.81.3+13.10.20130820.2-0ubuntu1) saucy; urgency=low
65+
66+ [ Michael Zanetti ]
67+ * initial support for quicklists For now they support pinning and
68+ removing of items.
69+
70+ [ Ubuntu daily release ]
71+ * Automatic snapshot from revision 217
72+
73+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 10:09:36 +0000
74+
75+unity8 (7.81.3+13.10.20130820-0ubuntu1) saucy; urgency=low
76+
77+ [ Andrea Cimitan ]
78+ * Implement background changing through gsettings. Make sure it does
79+ fallback to default background when needed.
80+
81+ [ Michael Zanetti ]
82+ * Implement background changing through gsettings. Make sure it does
83+ fallback to default background when needed.
84+ * Add Drag'n'drop support to Launcher As dragging an item pins it to
85+ the launcher this also contains initial quicklist and pinning
86+ support in the plugin part.
87+
88+ [ Michał Sawicz ]
89+ * Fix generic preview wrapping and force rich text parsing.
90+
91+ [ Michal Hruby ]
92+ * Remove the override for Apps available for download, click scope
93+ provides these now.
94+ * Hide rating widgets when scope provides rating set to < 0.0. Also
95+ fallback to regular preview image if there are no more_screenshots
96+ specified.
97+
98+ [ Albert Astals ]
99+ * Make sure minYExtent is updated before setting the new content
100+ height Otherwise bad things can happen in the positioning .
101+
102+ [ Ubuntu daily release ]
103+ * Automatic snapshot from revision 215
104+
105+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 02:08:42 +0000
106+
1unity8 (7.81.3+13.10.20130816.3-0ubuntu1) saucy; urgency=low
107unity8 (7.81.3+13.10.20130816.3-0ubuntu1) saucy; urgency=low
2
108
3 [ Michal Hruby ]
109 [ Michal Hruby ]
debian/control+2−1
@@ -20,7 +20,7 @@ Build-Depends: cmake,
20 libsigc++-2.0-dev,
20 libsigc++-2.0-dev,
21 libunity-api-dev (>= 7.80.3),
21 libunity-api-dev (>= 7.80.3),
22 libunity-core-6.0-dev (<< 7.80),
22 libunity-core-6.0-dev (<< 7.80),
23− libunity-dev (>= 7.0.3),
23+ libunity-dev (>= 7.0.12),
24 libunity-scopes-json-def-phone,
24 libunity-scopes-json-def-phone,
25 libupstart-dev,
25 libupstart-dev,
26 libusermetricsoutput1-dev,
26 libusermetricsoutput1-dev,
@@ -60,6 +60,7 @@ Description: Indicators client test application
60
60
61Package: unity8
61Package: unity8
62Architecture: any
62Architecture: any
63+Provides: indicator-renderer,
63Depends: gsettings-desktop-schemas,
64Depends: gsettings-desktop-schemas,
64 qmenumodel-qml (>= 0.2.7),
65 qmenumodel-qml (>= 0.2.7),
65 qtdeclarative5-dee-plugin,
66 qtdeclarative5-dee-plugin,
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/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>
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+62
@@ -0,0 +1,62 @@
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+ readonly property bool checked: menu ? menu.isToggled : false
29+
30+ onCheckedChanged: {
31+ // Can't rely on binding. Checked is assigned on click.
32+ checkBoxActive.checked = checked;
33+ }
34+
35+ // FIXME - we need to get the strength from menu.ext.xCanonicalWifiApStrengthAction
36+ // but UnityMenuModel doesnt support fetching actions not attached to menu item.
37+ function getNetworkIcon(data) {
38+ var imageName = "nm-signal-100"
39+
40+ if (data.ext.xCanonicalWifiApIsAdhoc) {
41+ imageName = "nm-adhoc";
42+ }
43+ if (data.ext.xCanonicalWifiApIsSecure) {
44+ imageName += "-secure";
45+ }
46+
47+ return "image://gicon/" + imageName;
48+ }
49+
50+ icon: menu ? getNetworkIcon(menu) : "image://gicon/wifi-none"
51+ iconFrame: false
52+ control: CheckBox {
53+ id: checkBoxActive
54+ height: units.gu(4)
55+ width: units.gu(4)
56+ anchors.centerIn: parent
57+
58+ onClicked: {
59+ accessPoint.activate();
60+ }
61+ }
62+}
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+ label: "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+28
@@ -0,0 +1,28 @@
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.Indicators 0.1 as Indicators
23+
24+Indicators.SectionMenuItem {
25+ id: wifiSectionMenu
26+
27+ busy: menu ? menu.ext.xCanonicalBusyAction : false
28+}
plugins/Unity/Indicators/Network/qml/qmldirnew file+9
@@ -0,0 +1,9 @@
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
9+WifiSection 0.1 WifiSection.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/qml/MenuItemFactory.qml+21
@@ -19,6 +19,7 @@
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
22
23
23Item {
24Item {
24 id: menuFactory
25 id: menuFactory
@@ -38,6 +39,9 @@ Item {
38
39
39 "com.canonical.unity.slider" : sliderMenu,
40 "com.canonical.unity.slider" : sliderMenu,
40 "com.canonical.unity.switch" : switchMenu,
41 "com.canonical.unity.switch" : switchMenu,
42+
43+ "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
44+ "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
41 }
45 }
42
46
43 Component {
47 Component {
@@ -57,6 +61,23 @@ Item {
57 Component { id: progressMenu; Indicators.ProgressMenuItem {} }
61 Component { id: progressMenu; Indicators.ProgressMenuItem {} }
58 Component { id: standardMenu; Indicators.StandardMenuItem {} }
62 Component { id: standardMenu; Indicators.StandardMenuItem {} }
59 Component { id: switchMenu; Indicators.SwitchMenuItem {} }
63 Component { id: switchMenu; Indicators.SwitchMenuItem {} }
64+ Component {
65+ id: wifiSection;
66+ ICNetwork.WifiSection {
67+ Component.onCompleted: {
68+ model.loadExtendedAttributes(modelIndex, {'x-canonical-busy-action': 'bool'});
69+ }
70+ }
71+ }
72+ Component {
73+ id: accessPoint;
74+ ICNetwork.AccessPoint {
75+ Component.onCompleted: {
76+ model.loadExtendedAttributes(modelIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
77+ 'x-canonical-wifi-ap-is-secure': 'bool'});
78+ }
79+ }
80+ }
60
81
61 function load(modelData) {
82 function load(modelData) {
62 if (modelData.type !== undefined) {
83 if (modelData.type !== undefined) {
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/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/flatmenuproxymodel.cpp+13
@@ -80,6 +80,19 @@ void FlatMenuProxyModel::setSourceModel(QAbstractItemModel *source)
80 SIGNAL(modelReset()),
80 SIGNAL(modelReset()),
81 SLOT(onModelReset()), Qt::QueuedConnection);
81 SLOT(onModelReset()), Qt::QueuedConnection);
82
82
83+ //Connection args
84+ connect(source,
85+ SIGNAL(busTypeChanged(DBusEnums::BusType)),
86+ SIGNAL(busTypeChanged()));
87+
88+ connect(source,
89+ SIGNAL(busNameChanged(const QString&)),
90+ SIGNAL(busNameChanged()));
91+
92+ connect(source,
93+ SIGNAL(objectPathChanged(const QString&)),
94+ SIGNAL(objectPathChanged()));
95+
83 connect(source,
96 connect(source,
84 SIGNAL(statusChanged(DBusEnums::ConnectionStatus)),
97 SIGNAL(statusChanged(DBusEnums::ConnectionStatus)),
85 SIGNAL(statusChanged()), Qt::QueuedConnection);
98 SIGNAL(statusChanged()), Qt::QueuedConnection);
plugins/Unity/Launcher/backend/launcherbackend.cpp+1−1
@@ -101,7 +101,7 @@ bool LauncherBackend::isPinned(const QString &appId) const
101{
101{
102 // TODO: return app's pinned state from settings
102 // TODO: return app's pinned state from settings
103 Q_UNUSED(appId)
103 Q_UNUSED(appId)
104− return true;
104+ return false;
105}
105}
106
106
107void LauncherBackend::setPinned(const QString &appId, bool pinned)
107void LauncherBackend::setPinned(const QString &appId, bool pinned)
plugins/Unity/Launcher/common/quicklistentry.cpp+5
@@ -52,3 +52,8 @@ void QuickListEntry::setIcon(const QString &icon)
52{
52{
53 m_icon = icon;
53 m_icon = icon;
54}
54}
55+
56+bool QuickListEntry::clickable() const
57+{
58+ return !m_actionId.isEmpty();
59+}
plugins/Unity/Launcher/common/quicklistentry.h+2
@@ -35,6 +35,8 @@ public:
35 QString icon() const;
35 QString icon() const;
36 void setIcon(const QString &icon);
36 void setIcon(const QString &icon);
37
37
38+ bool clickable() const;
39+
38private:
40private:
39 QString m_actionId;
41 QString m_actionId;
40 QString m_text;
42 QString m_text;
plugins/Unity/Launcher/launcheritem.cpp+11−1
@@ -33,7 +33,13 @@ LauncherItem::LauncherItem(const QString &appId, const QString &desktopFile, con
33 m_count(0),
33 m_count(0),
34 m_quickList(new QuickListModel(this))
34 m_quickList(new QuickListModel(this))
35{
35{
36−
36+ QuickListEntry nameAction;
37+ nameAction.setText(m_name);
38+ m_quickList->appendAction(nameAction);
39+ QuickListEntry pinningAction;
40+ pinningAction.setActionId("pin_item");
41+ pinningAction.setText("Pin to Launcher");
42+ m_quickList->appendAction(pinningAction);
37}
43}
38
44
39QString LauncherItem::appId() const
45QString LauncherItem::appId() const
@@ -65,6 +71,10 @@ void LauncherItem::setPinned(bool pinned)
65{
71{
66 if (m_pinned != pinned) {
72 if (m_pinned != pinned) {
67 m_pinned = pinned;
73 m_pinned = pinned;
74+ QuickListEntry entry;
75+ entry.setActionId("pin_item");
76+ entry.setText(pinned ? "Remove from Launcher": "Pin to Launcher");
77+ m_quickList->updateAction(entry);
68 Q_EMIT pinnedChanged(pinned);
78 Q_EMIT pinnedChanged(pinned);
69 }
79 }
70}
80}
plugins/Unity/Launcher/launchermodel.cpp+68−4
@@ -25,6 +25,9 @@ LauncherModel::LauncherModel(QObject *parent):
25 LauncherModelInterface(parent),
25 LauncherModelInterface(parent),
26 m_backend(new LauncherBackend(this))
26 m_backend(new LauncherBackend(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();
@@ -82,8 +89,25 @@ unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) con
82
89
83void LauncherModel::move(int oldIndex, int newIndex)
90void LauncherModel::move(int oldIndex, int newIndex)
84{
91{
85− // Perform the move in our list
92+ // Make sure its not moved outside the lists
93+ if (newIndex < 0) {
94+ newIndex = 0;
95+ }
96+ if (newIndex >= m_list.count()) {
97+ newIndex = m_list.count()-1;
98+ }
99+
100+ // Nothing to do?
101+ if (oldIndex == newIndex) {
102+ return;
103+ }
104+
105+ // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
106+ // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
107+ // adjust the model's index by +1 in case we're moving upwards
108+ int newModelIndex = newIndex > oldIndex ? newIndex+1 : newIndex;
109+
86− beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
110+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
87 m_list.move(oldIndex, newIndex);
111 m_list.move(oldIndex, newIndex);
88 endMoveRows();
112 endMoveRows();
89
113
@@ -140,13 +164,32 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
140 return;
164 return;
141 }
165 }
142
166
167+ LauncherItem *item = m_list.at(index);
143− QuickListModel *model = qobject_cast<QuickListModel*>(m_list.at(index)->quickList());
168+ QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
144 if (model) {
169 if (model) {
145 QString actionId = model->get(actionIndex).actionId();
170 QString actionId = model->get(actionIndex).actionId();
171+
172+ // Check if this is one of the launcher actions we handle ourselves
173+ if (actionId == "pin_item") {
174+ if (item->pinned()) {
175+ requestRemove(appId);
176+ } else {
177+ pin(appId);
178+ }
179+
180+ // Nope, we don't know this action, let the backend forward it to the application
181+ } else {
146− m_backend->triggerQuickListAction(appId, actionId);
182+ m_backend->triggerQuickListAction(appId, actionId);
183+ }
147 }
184 }
148}
185}
149
186
187+void LauncherModel::setUser(const QString &username)
188+{
189+ Q_UNUSED(username)
190+ // TODO: Implement this...
191+}
192+
150void LauncherModel::storeAppList()
193void LauncherModel::storeAppList()
151{
194{
152 QStringList appIds;
195 QStringList appIds;
@@ -168,3 +211,24 @@ int LauncherModel::findApplication(const QString &appId)
168 }
211 }
169 return -1;
212 return -1;
170}
213}
214+
215+void LauncherModel::progressChanged(const QString &appId, int progress)
216+{
217+ int idx = findApplication(appId);
218+ if (idx >= 0) {
219+ LauncherItem *item = m_list.at(idx);
220+ item->setProgress(progress);
221+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
222+ }
223+}
224+
225+
226+void LauncherModel::countChanged(const QString &appId, int count)
227+{
228+ int idx = findApplication(appId);
229+ if (idx >= 0) {
230+ LauncherItem *item = m_list.at(idx);
231+ item->setCount(count);
232+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
233+ }
234+}
plugins/Unity/Launcher/launchermodel.h+5
@@ -48,11 +48,16 @@ public:
48 Q_INVOKABLE void pin(const QString &appId, int index = -1);
48 Q_INVOKABLE void pin(const QString &appId, int index = -1);
49 Q_INVOKABLE void requestRemove(const QString &appId);
49 Q_INVOKABLE void requestRemove(const QString &appId);
50 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
50 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
51+ Q_INVOKABLE void setUser(const QString &username);
51
52
52private:
53private:
53 void storeAppList();
54 void storeAppList();
54 int findApplication(const QString &appId);
55 int findApplication(const QString &appId);
55
56
57+private Q_SLOTS:
58+ void progressChanged(const QString &appId, int progress);
59+ void countChanged(const QString &appId, int count);
60+
56private:
61private:
57 QList<LauncherItem*> m_list;
62 QList<LauncherItem*> m_list;
58 LauncherBackend *m_backend;
63 LauncherBackend *m_backend;
plugins/Unity/Launcher/plugin.cpp+2
@@ -41,7 +41,9 @@ void UnityLauncherPlugin::registerTypes(const char *uri)
41
41
42 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
42 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
43 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
43 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
44+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListInterface", "Abstract Interface. Cannot be instantiated.");
44
45
45 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
46 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
46 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", "Can't create new Launcher Items in QML. Get them from the LauncherModel.");
47 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", "Can't create new Launcher Items in QML. Get them from the LauncherModel.");
48+ qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", "Can't create a QuickListModel in QML. Get them from the LauncherItems.");
47}
49}
plugins/Unity/Launcher/quicklistmodel.cpp+13
@@ -37,6 +37,17 @@ void QuickListModel::appendAction(const QuickListEntry &entry)
37 endInsertRows();
37 endInsertRows();
38}
38}
39
39
40+void QuickListModel::updateAction(const QuickListEntry &entry)
41+{
42+ for (int i = 0; i < m_list.count(); ++i) {
43+ if (m_list.at(i).actionId() == entry.actionId()) {
44+ m_list.replace(i, entry);
45+ Q_EMIT dataChanged(index(i), index(i));
46+ return;
47+ }
48+ }
49+}
50+
40QuickListEntry QuickListModel::get(int index) const
51QuickListEntry QuickListModel::get(int index) const
41{
52{
42 return m_list.at(index);
53 return m_list.at(index);
@@ -55,6 +66,8 @@ QVariant QuickListModel::data(const QModelIndex &index, int role) const
55 return m_list.at(index.row()).text();
66 return m_list.at(index.row()).text();
56 case RoleIcon:
67 case RoleIcon:
57 return m_list.at(index.row()).icon();
68 return m_list.at(index.row()).icon();
69+ case RoleClickable:
70+ return m_list.at(index.row()).clickable();
58 }
71 }
59 return QVariant();
72 return QVariant();
60}
73}
plugins/Unity/Launcher/quicklistmodel.h+10
@@ -36,6 +36,16 @@ public:
36 ~QuickListModel();
36 ~QuickListModel();
37
37
38 void appendAction(const QuickListEntry &entry);
38 void appendAction(const QuickListEntry &entry);
39+
40+ /**
41+ * @brief Update an existing action
42+ * @param entry The new, updated entry
43+ *
44+ * This will only do something if entry.actionId is found in the model.
45+ * To add a new entry, use appendAction().
46+ */
47+ void updateAction(const QuickListEntry &entry);
48+
39 QuickListEntry get(int index) const;
49 QuickListEntry get(int index) const;
40
50
41 int rowCount(const QModelIndex &parent = QModelIndex()) const;
51 int rowCount(const QModelIndex &parent = QModelIndex()) const;
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/filters.cpp+4
@@ -23,6 +23,7 @@
23
23
24// Local
24// Local
25#include "filter.h"
25#include "filter.h"
26+#include "genericoptionsmodel.h"
26
27
27// Qt
28// Qt
28#include <QDebug>
29#include <QDebug>
@@ -77,6 +78,8 @@ QVariant Filters::data(const QModelIndex& index, int role) const
77 return filter->collapsed();
78 return filter->collapsed();
78 case Filters::RoleFiltering:
79 case Filters::RoleFiltering:
79 return filter->filtering();
80 return filter->filtering();
81+ case Filters::RoleOptions:
82+ return QVariant::fromValue(filter->options());
80 default:
83 default:
81 break;
84 break;
82 }
85 }
@@ -93,6 +96,7 @@ QHash<int, QByteArray> Filters::roleNames() const
93 roles[Filters::RoleVisible] = "visible";
96 roles[Filters::RoleVisible] = "visible";
94 roles[Filters::RoleCollapsed] = "collapsed";
97 roles[Filters::RoleCollapsed] = "collapsed";
95 roles[Filters::RoleFiltering] = "filtering";
98 roles[Filters::RoleFiltering] = "filtering";
99+ roles[Filters::RoleOptions] = "options";
96 return roles;
100 return roles;
97}
101}
98
102
plugins/Unity/filters.h+2−1
@@ -48,7 +48,8 @@ public:
48 RoleRendererName,
48 RoleRendererName,
49 RoleVisible,
49 RoleVisible,
50 RoleCollapsed,
50 RoleCollapsed,
51− RoleFiltering
51+ RoleFiltering,
52+ RoleOptions
52 };
53 };
53
54
54 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
55 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
plugins/Unity/genericoptionsmodel.cpp+18
@@ -76,6 +76,24 @@ 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)
80+{
81+ if (idx < m_options.size())
82+ {
83+ m_options[idx]->setActive(value);
84+ }
85+}
86+
87+void GenericOptionsModel::setActive(const QString &id, bool value)
88+{
89+ Q_FOREACH(auto opt, m_options) {
90+ if (opt->id() == id) {
91+ opt->setActive(value);
92+ break;
93+ }
94+ }
95+}
96+
79AbstractFilterOption* GenericOptionsModel::getRawOption(QVector<AbstractFilterOption *>::size_type idx) const
97AbstractFilterOption* GenericOptionsModel::getRawOption(QVector<AbstractFilterOption *>::size_type idx) const
80{
98{
81 if (idx < m_options.size())
99 if (idx < m_options.size())
plugins/Unity/genericoptionsmodel.h+3
@@ -49,6 +49,9 @@ 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);
53+ Q_INVOKABLE void setActive(const QString &id, bool value);
54+
52 AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
55 AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
53
56
54public Q_SLOTS:
57public Q_SLOTS:
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+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/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/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.indicator.messages+1−1
@@ -1,5 +1,5 @@
1[Indicator Service]
1[Indicator Service]
2−Name=indicator-messaging
2+Name=indicator-messages
3ObjectPath=/com/canonical/indicator/messages
3ObjectPath=/com/canonical/indicator/messages
4Position=50
4Position=50
5
5
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/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/Unity/Launcher/MockLauncherItem.cpp+2−2
@@ -31,8 +31,8 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
31 m_pinned(false),
31 m_pinned(false),
32 m_running(false),
32 m_running(false),
33 m_recent(false),
33 m_recent(false),
34− m_progress(8),
34+ m_progress(-1),
35− m_count(8),
35+ m_count(0),
36 m_quickList(new MockQuickListModel(this))
36 m_quickList(new MockQuickListModel(this))
37{
37{
38
38
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+38−4
@@ -23,22 +23,32 @@
23MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
23MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
24{
24{
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
26+ item->setProgress(0);
26 m_list.append(item);
27 m_list.append(item);
27 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
28 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29+ item->setProgress(10);
28 m_list.append(item);
30 m_list.append(item);
29 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
31 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
32+ item->setProgress(50);
30 m_list.append(item);
33 m_list.append(item);
31 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
34 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
35+ item->setProgress(150);
32 m_list.append(item);
36 m_list.append(item);
33 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
37 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
38+ item->setCount(1);
34 m_list.append(item);
39 m_list.append(item);
35 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
40 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
41+ item->setCount(12);
36 m_list.append(item);
42 m_list.append(item);
37 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
43 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
44+ item->setCount(123);
38 m_list.append(item);
45 m_list.append(item);
39 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
46 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
47+ item->setCount(1234567890);
40 m_list.append(item);
48 m_list.append(item);
41 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
49 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50+ item->setProgress(50);
51+ item->setCount(5);
42 m_list.append(item);
52 m_list.append(item);
43 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
53 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
44 m_list.append(item);
54 m_list.append(item);
@@ -91,9 +101,29 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
91
101
92void MockLauncherModel::move(int oldIndex, int newIndex)
102void MockLauncherModel::move(int oldIndex, int newIndex)
93{
103{
104+ // Make sure its not moved outside the lists
105+ if (newIndex < 0) {
106+ newIndex = 0;
107+ }
108+ if (newIndex >= m_list.count()) {
109+ newIndex = m_list.count()-1;
110+ }
111+
112+ // Nothing to do?
113+ if (oldIndex == newIndex) {
114+ return;
115+ }
116+
117+ // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
118+ // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
119+ // adjust the model's index by +1 in case we're moving upwards
120+ int newModelIndex = newIndex > oldIndex ? newIndex+1 : newIndex;
121+
94− beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
122+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
95 m_list.move(oldIndex, newIndex);
123 m_list.move(oldIndex, newIndex);
96 endMoveRows();
124 endMoveRows();
125+
126+ pin(m_list.at(newIndex)->appId());
97}
127}
98
128
99void MockLauncherModel::pin(const QString &appId, int index)
129void MockLauncherModel::pin(const QString &appId, int index)
@@ -131,9 +161,7 @@ void MockLauncherModel::requestRemove(const QString &appId)
131
161
132void MockLauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
162void MockLauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
133{
163{
134− Q_UNUSED(appId)
164+ Q_EMIT quickListTriggered(appId, actionIndex);
135− Q_UNUSED(actionIndex)
136− // Nothing to mock yet...
137}
165}
138
166
139int MockLauncherModel::findApp(const QString &appId)
167int MockLauncherModel::findApp(const QString &appId)
@@ -146,3 +174,9 @@ int MockLauncherModel::findApp(const QString &appId)
146 }
174 }
147 return -1;
175 return -1;
148}
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+4
@@ -43,6 +43,10 @@ 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);
47+
48+Q_SIGNALS:
49+ void quickListTriggered(const QString &appId, int index);
46
50
47private:
51private:
48 int findApp(const QString &appId);
52 int findApp(const QString &appId);
tests/mocks/Unity/Launcher/MockQuickListModel.cpp+2
@@ -35,6 +35,8 @@ QVariant MockQuickListModel::data(const QModelIndex &index, int role) const
35 return QLatin1String("test menu entry ") + QString::number(index.row());
35 return QLatin1String("test menu entry ") + QString::number(index.row());
36 case RoleIcon:
36 case RoleIcon:
37 return QLatin1String("copy.png");
37 return QLatin1String("copy.png");
38+ case RoleClickable:
39+ return index.row() == 1 ? false : true;
38 }
40 }
39 return QVariant();
41 return QVariant();
40}
42}
tests/mocks/Unity/Launcher/plugin.cpp+3
@@ -20,6 +20,7 @@
20#include "plugin.h"
20#include "plugin.h"
21#include "MockLauncherModel.h"
21#include "MockLauncherModel.h"
22#include "MockLauncherItem.h"
22#include "MockLauncherItem.h"
23+#include "MockQuickListModel.h"
23
24
24#include <unity/shell/launcher/LauncherModelInterface.h>
25#include <unity/shell/launcher/LauncherModelInterface.h>
25#include <unity/shell/launcher/LauncherItemInterface.h>
26#include <unity/shell/launcher/LauncherItemInterface.h>
@@ -39,7 +40,9 @@ void TestLauncherPlugin::registerTypes(const char* uri)
39 // @uri Unity.Launcher
40 // @uri Unity.Launcher
40 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
41 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
41 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
42 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
43+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
42
44
43 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
45 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
44 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
46 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
47+ qmlRegisterUncreatableType<MockQuickListModel>(uri, 0, 1, "QuickListModel", "Can't create QuickLists in QML. Get them from the LauncherItems");
45}
48}
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/Dash/Apps/tst_AppPreview.qml+17−3
@@ -35,7 +35,7 @@ Item {
35
35
36 property string commentary: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus a mi vitae augue rhoncus lobortis ut rutrum metus. Curabitur tortor leo, tristique sed mollis quis, condimentum venenatis nibh.";
36 property string commentary: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus a mi vitae augue rhoncus lobortis ut rutrum metus. Curabitur tortor leo, tristique sed mollis quis, condimentum venenatis nibh.";
37 QtObject { id: screenshots; property var value: ["fake_image1.png", "fake_image2.png", "fake_image3.png"] }
37 QtObject { id: screenshots; property var value: ["fake_image1.png", "fake_image2.png", "fake_image3.png"] }
38− QtObject { id: rating; property int value: 8 }
38+ QtObject { id: rating; property real value: 0.8 }
39 QtObject { id: rated; property int value: 120 }
39 QtObject { id: rated; property int value: 120 }
40 QtObject { id: reviews; property int value: 8 }
40 QtObject { id: reviews; property int value: 8 }
41 QtObject { id: progress; property string value: "source" }
41 QtObject { id: progress; property string value: "source" }
@@ -51,12 +51,12 @@ Item {
51 property string title: "Unity App"
51 property string title: "Unity App"
52 property string appIcon: "fake_image.png"
52 property string appIcon: "fake_image.png"
53 property string description: "This is an Application description"
53 property string description: "This is an Application description"
54+ property real rating: rating.value
55+ property int numRatings: reviews.value
54 property var execute: root.fake_call
56 property var execute: root.fake_call
55 property var infoMap: {
57 property var infoMap: {
56 "more-screenshots": screenshots,
58 "more-screenshots": screenshots,
57− "rating": rating,
58 "rated": rated,
59 "rated": rated,
59− "reviews": reviews,
60 "comments": comments
60 "comments": comments
61 }
61 }
62 property var actions: [
62 property var actions: [
@@ -88,6 +88,7 @@ Item {
88 var reviewField = findChild(appPreview, "reviewField");
88 var reviewField = findChild(appPreview, "reviewField");
89 reviewField.focus = false;
89 reviewField.focus = false;
90 reviewField.text = "";
90 reviewField.text = "";
91+ data.rating = rating.value;
91 }
92 }
92
93
93 function test_actions() {
94 function test_actions() {
@@ -148,5 +149,18 @@ Item {
148 compare(comment, comments.value[i][3], "Comment don't match");
149 compare(comment, comments.value[i][3], "Comment don't match");
149 }
150 }
150 }
151 }
152+
153+ function test_negative_rating() {
154+ data.rating = -1.0;
155+ var rated = findChild(appPreview, "reviewsLabel");
156+ verify(rated.visible == false);
157+ var commentsArea = findChild(appPreview, "commentsArea");
158+ verify(commentsArea.visible == false);
159+ var appReviews = findChild(appPreview, "appReviews");
160+ verify(appReviews.visible == false);
161+ var buttons = findChild(appPreview, "gridButtons");
162+ verify(buttons.visible == true);
163+ }
164+
151 }
165 }
152}
166}
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+175
@@ -19,6 +19,7 @@ import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20import ".."
20import ".."
21import "../../../Launcher"
21import "../../../Launcher"
22+import Unity.Launcher 0.1
22
23
23/* Nothing is shown at first. If you drag from left edge you will bring up the
24/* Nothing is shown at first. If you drag from left edge you will bring up the
24 launcher. */
25 launcher. */
@@ -48,6 +49,11 @@ Item {
48 property int maxPanelX: 0
49 property int maxPanelX: 0
49 }
50 }
50
51
52+ SignalSpy {
53+ id: signalSpy
54+ target: LauncherModel
55+ }
56+
51 Connections {
57 Connections {
52 target: testCase.findChild(launcher, "launcherPanel")
58 target: testCase.findChild(launcher, "launcherPanel")
53
59
@@ -176,6 +182,22 @@ Item {
176 revealer.waitUntilLauncherDisappears();
182 revealer.waitUntilLauncherDisappears();
177 launcher.available = true;
183 launcher.available = true;
178 }
184 }
185+
186+ function test_countEmblems() {
187+ var launcherListView = findChild(launcher, "launcherListView");
188+ for (var i = 0; i < launcherListView.count; ++i) {
189+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
190+ compare(findChild(delegate, "countEmblem").visible, LauncherModel.get(i).count > 0)
191+ }
192+ }
193+
194+ function test_progressOverlays() {
195+ var launcherListView = findChild(launcher, "launcherListView");
196+ for (var i = 0; i < launcherListView.count; ++i) {
197+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
198+ compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
199+ }
200+ }
179 }
201 }
180
202
181 UT.UnityTestCase {
203 UT.UnityTestCase {
@@ -247,4 +269,157 @@ Item {
247 }
269 }
248 */
270 */
249 }
271 }
272+
273+ UT.UnityTestCase {
274+ id: dndTestCase
275+ name: "Drag and Drop"
276+ when: windowShown && initTestCase.completed
277+
278+ function test_dragndrop_data() {
279+ return [
280+ {tag: "startDrag", fullDrag: false },
281+ {tag: "fullDrag horizontal", fullDrag: true, orientation: ListView.Horizontal },
282+ {tag: "fullDrag vertical", fullDrag: true, orientation: ListView.Vertical },
283+ ];
284+ }
285+
286+ function test_dragndrop(data) {
287+ revealer.dragLauncherIntoView();
288+ var draggedItem = findChild(launcher, "launcherDelegate5")
289+ var item0 = findChild(launcher, "launcherDelegate0")
290+ var fakeDragItem = findChild(launcher, "fakeDragItem")
291+ var initialItemHeight = draggedItem.height
292+ var item5 = LauncherModel.get(5).appId
293+ var item4 = LauncherModel.get(4).appId
294+
295+ // Initial state
296+ compare(draggedItem.itemOpacity, 1, "Item's opacity is not 1 at beginning")
297+ compare(fakeDragItem.visible, false, "FakeDragItem isn't invisible at the beginning")
298+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
299+
300+ // Doing longpress
301+ var currentMouseX = draggedItem.width / 2
302+ var currentMouseY = draggedItem.height / 2
303+ mousePress(draggedItem, currentMouseX, currentMouseY)
304+ // DraggedItem needs to hide and fakeDragItem become visible
305+ tryCompare(draggedItem, "itemOpacity", 0)
306+ tryCompare(fakeDragItem, "visible", true)
307+
308+ // Dragging a bit (> 1.5 gu)
309+ currentMouseX -= units.gu(2)
310+ mouseMove(draggedItem, currentMouseX, currentMouseY)
311+ // Other items need to expand and become 0.6 opaque
312+ tryCompare(item0, "angle", 0)
313+ tryCompare(item0, "itemOpacity", 0.6)
314+
315+ if (data.fullDrag) {
316+ // Dragging a bit more
317+ if (data.orientation == ListView.Horizontal) {
318+ currentMouseX -= units.gu(15)
319+ mouseMove(draggedItem, currentMouseX, currentMouseY, 100)
320+
321+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
322+ tryCompare(draggedItem, "height", units.gu(1))
323+
324+ // Dragging downwards. Item needs to move in the model
325+ currentMouseY -= initialItemHeight * 1.5
326+ mouseMove(draggedItem, currentMouseX, currentMouseY)
327+ } else if (data.orientation == ListView.Vertical) {
328+ currentMouseY -= initialItemHeight * 1.5
329+ mouseMove(draggedItem, currentMouseX, currentMouseY, 100)
330+
331+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
332+ tryCompare(draggedItem, "height", units.gu(1))
333+ }
334+
335+ waitForRendering(draggedItem)
336+ compare(LauncherModel.get(5).appId, item4)
337+ compare(LauncherModel.get(4).appId, item5)
338+ }
339+
340+ // Releasing and checking if initial values are restored
341+ mouseRelease(draggedItem)
342+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
343+ tryCompare(draggedItem, "itemOpacity", 1)
344+ tryCompare(fakeDragItem, "visible", false)
345+
346+ // Click somewhere in the empty space to make it hide in case it isn't
347+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
348+ revealer.waitUntilLauncherDisappears();
349+ }
350+
351+ function test_quicklist_dismiss() {
352+ revealer.dragLauncherIntoView();
353+ var draggedItem = findChild(launcher, "launcherDelegate5")
354+ var item0 = findChild(launcher, "launcherDelegate0")
355+ var fakeDragItem = findChild(launcher, "fakeDragItem")
356+ var dndArea = findChild(launcher, "dndArea")
357+
358+ // Initial state
359+ compare(dndArea.quickListPopover, null)
360+
361+ // Doing longpress
362+ mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
363+ tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
364+ verify(dndArea.quickListPopover != null)
365+
366+ // Dragging a bit (> 1.5 gu)
367+ mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
368+
369+ // QuickList needs to be closed when a drag operation starts
370+ tryCompare(dndArea, "quickListPopover", null)
371+
372+ mouseRelease(draggedItem);
373+ }
374+
375+ function test_quicklist_click_data() {
376+ return [
377+ {tag: "non-clickable", index: 1, clickable: false },
378+ {tag: "clickable", index: 2, clickable: true },
379+ ];
380+ }
381+
382+ function test_quicklist_click(data) {
383+ revealer.dragLauncherIntoView();
384+ var clickedItem = findChild(launcher, "launcherDelegate5")
385+ var dndArea = findChild(launcher, "dndArea")
386+
387+ // Initial state
388+ compare(dndArea.quickListPopover, null)
389+
390+ // Doing longpress
391+ mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
392+ tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
393+ verify(dndArea.quickListPopover != null)
394+
395+ mouseRelease(clickedItem);
396+
397+ var quickListEntry = findChild(dndArea.quickListPopover, "quickListEntry" + data.index)
398+
399+ signalSpy.clear();
400+ signalSpy.signalName = "quickListTriggered"
401+
402+ mouseClick(quickListEntry, quickListEntry.width / 2, quickListEntry.height / 2)
403+
404+ if (data.clickable) {
405+ // QuickList needs to be closed when some clickable item is clicked
406+ tryCompare(dndArea, "quickListPopover", null)
407+
408+ compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
409+ compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
410+ compare(signalSpy.signalArguments[0][1], 2)
411+
412+ } else {
413+
414+ // QuickList must not be closed when a non-clickable item is clicked
415+ verify(dndArea.quickListPopover != null)
416+
417+ compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
418+
419+ // Click somewhere in the empty space to dismiss the quicklist
420+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
421+ tryCompare(dndArea, "quickListPopover", null)
422+ }
423+ }
424+ }
250}
425}
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 }