on xenial help sign in
merged with trunk
commit14857379a211f5f387a1d2ac83bc88e5f22864f2 authorNick Dedekind <nicholas.dedekind@gmail.com> dateThu 22 Aug 2013, 19:38 (13 years ago) parent6048b25691883c
138 files changed+3171−558
Components/CrossFadeImage.qml+3−1 Dash/Apps/AppInfo.qml+1 Dash/Apps/AppPreview.qml+37−32 Dash/Apps/FrequentlyUsedAppsModel.qml+1−1 Dash/DashApps.qml−9 Dash/Generic/GenericPreview.qml+10−2 Greeter/GreeterContent.qml−1 Launcher/FoldingLauncherDelegate.qml+133 Launcher/Launcher.qml+4−4 Launcher/LauncherDelegate.qml+89−175 Launcher/LauncherPanel.qml+415−91 Launcher/graphics/launcher_bg@18.png Launcher/graphics/launcher_bg@18.sci−5 Launcher/graphics/launcher_bg@30.png Launcher/graphics/launcher_bg@30.sci+5 Launcher/graphics/notification@30.png Launcher/graphics/notification@30.sci+5 Launcher/graphics/progressbar-fill@30.png Launcher/graphics/progressbar-fill@30.sci+5 Launcher/graphics/progressbar-trough@30.png Launcher/graphics/progressbar-trough@30.sci+5 Panel/Indicators/NetworkIndicatorPage.qml+2−2 Panel/Indicators/NetworkIndicatorPage2.qml+82 Panel/IndicatorsDataModel.qml+2−28 Shell.qml+42−15 build+5−2 debian/changelog+145 debian/control+5−2 plugins/ListViewWithPageHeader/listviewwithpageheader.cpp+1 plugins/Powerd/Powerd.cpp+2−2 plugins/Powerd/Powerd.h+15−1 plugins/Unity/Indicators/CMakeLists.txt+1 plugins/Unity/Indicators/Network/CMakeLists.txt+45 plugins/Unity/Indicators/Network/networkagent.cpp+146 plugins/Unity/Indicators/Network/networkagent.h+65 plugins/Unity/Indicators/Network/plugin.cpp+31 plugins/Unity/Indicators/Network/plugin.h+32 plugins/Unity/Indicators/Network/qml/AccessPoint.qml+62 plugins/Unity/Indicators/Network/qml/CMakeLists.txt+5 plugins/Unity/Indicators/Network/qml/PasswordPage.qml+88 plugins/Unity/Indicators/Network/qml/PasswordTextField.qml+68 plugins/Unity/Indicators/Network/qml/WifiSection.qml+28 plugins/Unity/Indicators/Network/qml/qmldir+9 plugins/Unity/Indicators/Network/secret-agent.c+382 plugins/Unity/Indicators/Network/secret-agent.h+102 plugins/Unity/Indicators/qml/MenuItemFactory.qml+21 plugins/Unity/IndicatorsLegacy/Network/CMakeLists.txt+8−8 plugins/Unity/IndicatorsLegacy/Network/qml/qmldir+1−1 plugins/Unity/IndicatorsLegacy/flatmenuproxymodel.cpp+13 plugins/Unity/Launcher/backend/launcherbackend.cpp+1−1 plugins/Unity/Launcher/common/quicklistentry.cpp+5 plugins/Unity/Launcher/common/quicklistentry.h+2 plugins/Unity/Launcher/launcheritem.cpp+11−1 plugins/Unity/Launcher/launchermodel.cpp+34−4 plugins/Unity/Launcher/plugin.cpp+2 plugins/Unity/Launcher/quicklistmodel.cpp+13 plugins/Unity/Launcher/quicklistmodel.h+10 plugins/Unity/categories.cpp+6 plugins/Unity/categories.h+1 plugins/Unity/filters.cpp+4 plugins/Unity/filters.h+2−1 plugins/Unity/genericoptionsmodel.cpp+18 plugins/Unity/genericoptionsmodel.h+3 plugins/Utils/qlimitproxymodelqml.cpp+90−4 plugins/Utils/qlimitproxymodelqml.h+2 po/ar.po+2−2 po/az.po+2−2 po/bg.po+2−2 po/ca.po+2−2 po/de.po+2−2 po/el.po+2−2 po/en_AU.po+2−2 po/en_GB.po+2−2 po/eo.po+2−2 po/es.po+2−2 po/fi.po+2−2 po/fo.po+2−2 po/fr.po+2−2 po/gl.po+2−2 po/he.po+2−2 po/hr.po+2−2 po/hu.po+2−2 po/hy.po+2−2 po/id.po+2−2 po/it.po+2−2 po/ja.po+2−2 po/kn.po+2−2 po/ko.po+2−2 po/ky.po+2−2 po/lv.po+2−2 po/my.po+2−2 po/nb.po+2−2 po/nl.po+2−2 po/pl.po+2−2 po/pt_BR.po+2−2 po/ro.po+2−2 po/ru.po+2−2 po/si.po+2−2 po/sl.po+2−2 po/sr.po+2−2 po/sv.po+2−2 po/te.po+2−2 po/tr.po+2−2 po/ug.po+2−2 po/uk.po+2−2 po/zh_CN.po+2−2 po/zh_TW.po+2−2 src/Panel/Indicators/servicefiles/CMakeLists.txt−4 src/Panel/Indicators/servicefiles/com.canonical.indicator.battery−10 src/Panel/Indicators/servicefiles/com.canonical.indicator.messages+1−1 src/Panel/Indicators/servicefiles/com.canonical.indicator.time−10 src/Panel/Indicators/servicefiles/com.canonical.settings.network−10 src/Panel/Indicators/servicefiles/com.canonical.settings.sound−10 tests/autopilot/unity8/indicators_client/tests/test_battery.py+14−8 tests/autopilot/unity8/shell/tests/test_lock_screen.py+13−2 tests/data/unity/backgrounds/blue.png tests/data/unity/backgrounds/red.png tests/mocks/CMakeLists.txt+1 tests/mocks/GSettings.1.0/CMakeLists.txt+23 tests/mocks/GSettings.1.0/fake_gsettings.cpp+102 tests/mocks/GSettings.1.0/fake_gsettings.h+90 tests/mocks/GSettings.1.0/plugin.cpp+33 tests/mocks/GSettings.1.0/plugin.h+31 tests/mocks/GSettings.1.0/qmldir+2 tests/mocks/Powerd/Powerd.h+15−2 tests/mocks/Unity/Launcher/MockLauncherItem.cpp+2−2 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+32−4 tests/mocks/Unity/Launcher/MockLauncherModel.h+3 tests/mocks/Unity/Launcher/MockQuickListModel.cpp+2 tests/mocks/Unity/Launcher/plugin.cpp+3 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp+22−1 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsection.cpp+22−1 tests/plugins/Utils/qlimitproxymodeltest.cpp+133−8 tests/qmltests/CMakeLists.txt+1−1 tests/qmltests/Dash/Apps/tst_AppPreview.qml+17−3 tests/qmltests/Greeter/tst_Clock.qml+1−1 tests/qmltests/Launcher/tst_Launcher.qml+175 tests/qmltests/tst_Shell.qml+24−3
Components/CrossFadeImage.qml+3−1
@@ -85,7 +85,7 @@ Item {
8585 Connections {
8686 target: __nextImage
8787 onStatusChanged: {
88− if (__nextImage.status == Image.Ready) {
88+ if (__nextImage.status == Image.Ready || __nextImage.status == Image.Error) {
8989 swapImages();
9090 }
9191 }
@@ -96,6 +96,7 @@ Item {
9696 anchors.fill: parent
9797 cache: false
9898 asynchronous: true
99+ fillMode: Image.PreserveAspectCrop
99100 z: 1
100101 }
101102
@@ -104,6 +105,7 @@ Item {
104105 anchors.fill: parent
105106 cache: false
106107 asynchronous: true
108+ fillMode: Image.PreserveAspectCrop
107109 z: 0
108110 }
109111
Dash/Apps/AppInfo.qml+1
@@ -53,6 +53,7 @@ Row {
5353 }
5454
5555 Row {
56+ visible: root.rating >= 0
5657 spacing: units.gu(1)
5758
5859 RatingStars {
Dash/Apps/AppPreview.qml+37−32
@@ -38,7 +38,7 @@ DashPreview {
3838 right: parent.right
3939 margins: units.gu(1)
4040 }
41− model: previewData.infoMap["more-screenshots"].value
41+ model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
4242
4343 delegate: UbuntuShape {
4444 id: shape
@@ -138,9 +138,9 @@ DashPreview {
138138
139139 appName: root.previewData.title
140140 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
142143 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
143− reviews: root.previewData.infoMap["reviews"] ? root.previewData.infoMap["reviews"].value : 0
144144 }
145145
146146 Label {
@@ -154,42 +154,47 @@ DashPreview {
154154 styleColor: "black"
155155 }
156156
157− ListItem.ThinDivider {}
158−
159− Item {
157+ Column {
158+ visible: root.previewData.rating >= 0
160159 anchors { left: parent.left; right: parent.right }
161− height: rateLabel.height
162−
163− Label {
164− id: rateLabel
165− fontSize: "medium"
166− color: "white"
167− style: Text.Raised
168− styleColor: "black"
169− opacity: .9
170− text: i18n.tr("Rate this")
171−
172− anchors.left: parent.left
173− anchors.verticalCenter: parent.verticalCenter
174− }
160+ spacing: parent.spacing
161+
162+ ListItem.ThinDivider { }
163+
164+ Item {
165+ anchors { left: parent.left; right: parent.right }
166+ height: rateLabel.height
167+
168+ Label {
169+ id: rateLabel
170+ fontSize: "medium"
171+ color: "white"
172+ style: Text.Raised
173+ styleColor: "black"
174+ opacity: .9
175+ text: i18n.tr("Rate this")
176+
177+ anchors.left: parent.left
178+ anchors.verticalCenter: parent.verticalCenter
179+ }
175180
176− // FIXME these need to be made interactive and connected to the scope
177− RatingStars {
178− anchors.right: parent.right
179− anchors.verticalCenter: parent.verticalCenter
181+ // FIXME these need to be made interactive and connected to the scope
182+ RatingStars {
183+ anchors.right: parent.right
184+ anchors.verticalCenter: parent.verticalCenter
185+ }
180186 }
181− }
182187
183− ListItem.ThinDivider {}
188+ ListItem.ThinDivider { }
184189
185− AppReviews {
186− objectName: "appReviews"
187− anchors { left: parent.left; right: parent.right }
190+ AppReviews {
191+ objectName: "appReviews"
192+ anchors { left: parent.left; right: parent.right }
188193
189− model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
194+ model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
190195
191− onSendReview: root.sendUserReview(review);
196+ onSendReview: root.sendUserReview(review);
197+ }
192198 }
193−
194199 }
195200}
Dash/Apps/FrequentlyUsedAppsModel.qml+1−1
@@ -38,7 +38,7 @@ ListModel {
3838 metadata: ""
3939 }
4040 ListElement {
41− uri: "application:///usr/share/applications/galery-app.desktop"
41+ uri: "application:///usr/share/applications/gallery-app.desktop"
4242 icon: "../../graphics/applicationIcons/gallery.png"
4343 category: 0
4444 mimetype: "application/x-desktop"
Dash/DashApps.qml−9
@@ -29,14 +29,6 @@ GenericScopeView {
2929 property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications
3030 property var sideStageApplicationModel: shell.applicationManager.sideStageApplications
3131
32− SearchableResultModel {
33− id: appsAvailableForDownloadModel
34−
35− model: AppsAvailableForDownloadModel {}
36− filterRole: 3
37− searchQuery: scopeView.scope.searchQuery
38− }
39−
4032 ListModel {
4133 id: dummyVisibilityModifier
4234
@@ -59,6 +51,5 @@ GenericScopeView {
5951
6052 onScopeChanged: {
6153 scopeView.scope.categories.overrideResults("recent", runningApplicationsModel);
62− scopeView.scope.categories.overrideResults("more", appsAvailableForDownloadModel);
6354 }
6455}
Dash/Generic/GenericPreview.qml+10−2
@@ -74,25 +74,33 @@ DashPreview {
7474 spacing: units.gu(2)
7575
7676 Label {
77+ anchors { left: parent.left; right: parent.right }
7778 visible: text != ""
7879 fontSize: "medium"
7980 opacity: 0.6
8081 color: "white"
81− text: previewData.subtitle
82+ text: previewData.subtitle.replace(/[\r\n]+/g, "<br />")
8283 style: Text.Raised
8384 styleColor: "black"
8485 wrapMode: Text.WordWrap
86+ textFormat: Text.RichText
87+ // FIXME: workaround for https://bugreports.qt-project.org/browse/QTBUG-33020
88+ onWidthChanged: { wrapMode = Text.NoWrap; wrapMode = Text.WordWrap }
8589 }
8690
8791 Label {
92+ anchors { left: parent.left; right: parent.right }
8893 visible: text != ""
8994 fontSize: "small"
9095 opacity: 0.6
9196 color: "white"
92− text: previewData.description
97+ text: previewData.description.replace(/[\r\n]+/g, "<br />")
9398 style: Text.Raised
9499 styleColor: "black"
95100 wrapMode: Text.WordWrap
101+ textFormat: Text.RichText
102+ // FIXME: workaround for https://bugreports.qt-project.org/browse/QTBUG-33020
103+ onWidthChanged: { wrapMode = Text.NoWrap; wrapMode = Text.WordWrap }
96104 }
97105 }
98106}
Greeter/GreeterContent.qml−1
@@ -37,7 +37,6 @@ MouseArea {
3737
3838 source: shell.background
3939 anchors.fill: parent
40− crossFade: false
4140 fadeInFirst: false
4241 }
4342
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 {
7373 id: dismissTimer
7474 interval: 5000
7575 onTriggered: {
76− if (!panel.moving) {
76+ if (!panel.preventHiding) {
7777 root.state = ""
7878 } else {
7979 dismissTimer.restart()
@@ -135,9 +135,9 @@ Item {
135135 id: backgroundShade
136136 anchors.fill: parent
137137 color: "black"
138− opacity: root.state == "visible" ? 0.4 : 0
138+ opacity: root.state == "visible" ? 0.6 : 0
139139
140− Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuad} }
140+ Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
141141 }
142142
143143 LauncherPanel {
@@ -162,7 +162,7 @@ Item {
162162 root.dashItemSelected(index)
163163 }
164164
165− onMovingChanged: {
165+ onPreventHidingChanged: {
166166 if (dismissTimer.running) {
167167 dismissTimer.restart();
168168 }
Launcher/LauncherDelegate.qml+89−175
@@ -21,29 +21,33 @@ Item {
2121 id: root
2222
2323 property string iconName
24−
25− property real angle: 0
24+ property int count: -1
25+ property int progress: -1
2626 property bool highlighted: false
27− property real offset: 0
28− property alias brightness: transformEffect.brightness
29− property real itemOpacity: 1
30−
3127 property real maxAngle: 0
3228 property bool inverted: false
3329
34− readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * height
35− readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * height
30+ readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
31+ readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
3632
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
3841
39− signal clicked()
40− signal longtap()
41− signal released()
42+ onIconNameChanged: shaderEffectSource.scheduleUpdate();
43+ onCountChanged: shaderEffectSource.scheduleUpdate();
44+ onProgressChanged: shaderEffectSource.scheduleUpdate();
45+ onHighlightedChanged: shaderEffectSource.scheduleUpdate();
4246
4347 Item {
4448 id: iconItem
45− width: parent.width
46− height: parent.height
49+ width: parent.itemWidth
50+ height: parent.itemHeight
4751 anchors.centerIn: parent
4852
4953 UbuntuShape {
@@ -58,31 +62,79 @@ Item {
5862 sourceSize.height: iconShape.height
5963 source: "../graphics/applicationIcons/" + root.iconName + ".png"
6064 }
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− }
7665 }
66+
7767 BorderImage {
7868 id: overlayHighlight
7969 anchors.centerIn: iconItem
8070 rotation: inverted ? 180 : 0
81− source: isSelected ? "graphics/selected.sci" : "graphics/non-selected.sci"
82− width: root.width + units.gu(0.5)
83− height: width
84− property bool isSelected: root.highlighted || mouseArea.pressed
85− onIsSelectedChanged: shaderEffectSource.scheduleUpdate();
71+ source: root.highlighted ? "graphics/selected.sci" : "graphics/non-selected.sci"
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)))
84+ height: units.gu(3)
85+ source: "graphics/notification.sci"
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+ }
86138 }
87139 }
88140
@@ -90,14 +142,13 @@ Item {
90142 id: transformEffect
91143 anchors.centerIn: parent
92144 anchors.verticalCenterOffset: root.offset
93− width: parent.width
94− height: parent.height
145+ width: parent.itemWidth
146+ height: parent.itemHeight
95147 property real itemOpacity: root.itemOpacity
96− property real brightness: 0
148+ property real brightness: Math.max(-1, root.brightness)
97149 property real angle: root.angle
98150 rotation: root.inverted ? 180 : 0
99151
100−
101152 property variant source: ShaderEffectSource {
102153 id: shaderEffectSource
103154 sourceItem: iconItem
@@ -149,141 +200,4 @@ Item {
149200 gl_FragColor = sourceColor;
150201 }"
151202 }
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− ]
289203}
Launcher/LauncherPanel.qml+415−91
@@ -16,8 +16,10 @@
1616
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
19+import Ubuntu.Components.ListItems 0.1 as ListItems
1920import Unity 0.1
2021import Unity.Launcher 0.1
22+import Ubuntu.Components.Popups 0.1
2123import "../Components/ListItems"
2224
2325Item {
@@ -29,53 +31,33 @@ Item {
2931 property bool inverted: true
3032 property bool dragging: false
3133 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
3335 property int highlightIndex: -1
3436
3537 signal applicationSelected(string desktopFile)
3638 signal dashItemSelected(int index)
3739
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−
5840 BorderImage {
5941 id: background
6042 source: "graphics/launcher_bg.sci"
6143 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
6247 }
6348
6449 Column {
6550 id: mainColumn
6651 anchors {
6752 fill: parent
68− topMargin: units.gu(0.5)
69− bottomMargin: units.gu(1)
7053 leftMargin: units.gu(0.5)
7154 rightMargin: units.gu(0.5)
7255 }
73− spacing: units.gu(0.5)
7456
7557 MouseArea {
7658 id: dashItem
7759 width: parent.width
78− height: units.gu(6.5)
60+ height: units.gu(7)
7961 onClicked: root.dashItemSelected(0)
8062 z: 1
8163 Image {
@@ -100,87 +82,429 @@ Item {
10082 anchors.left: parent.left
10183 anchors.right: parent.right
10284 height: parent.height - dashItem.height - parent.spacing*2
103− ListView {
104− id: launcherListView
105− objectName: "launcherListView"
85+
86+ Item {
10687 anchors.fill: parent
107− anchors.topMargin: -itemSize
108− anchors.bottomMargin: -itemSize
109− topMargin: itemSize
110− bottomMargin: itemSize
111− height: parent.height - dashItem.height - parent.spacing*2
112− model: root.model
113− cacheBuffer: itemSize * 3
114− snapMode: ListView.SnapToItem
115− highlightRangeMode: ListView.ApplyRange
116− preferredHighlightBegin: (height - itemSize) / 2
117− preferredHighlightEnd: (height + itemSize) / 2
118−
119− // The height of the area where icons start getting folded
120− property int foldingAreaHeight: itemSize * 0.75
121− property int itemSize: width
122− property int clickFlickSpeed: units.gu(60)
123−
124− delegate: LauncherDelegate {
125− id: launcherDelegate
126− objectName: "launcherDelegate" + index
127− width: launcherListView.itemSize
128− height: launcherListView.itemSize
129− iconName: model.icon
130− inverted: root.inverted
131− highlighted: root.dragging && index === root.highlightIndex
132− z: -Math.abs(offset)
133− state: "docked"
134− maxAngle: 60
135−
136− onClicked: {
137− // First/last item do the scrolling at more than 12 degrees
138− if (index == 0 || index == launcherListView.count -1) {
139− if (angle > 12) {
88+ clip: true
89+
90+ ListView {
91+ id: launcherListView
92+ objectName: "launcherListView"
93+ anchors {
94+ fill: parent
95+ topMargin: -extensionSize + units.gu(0.5)
96+ bottomMargin: -extensionSize + units.gu(1)
97+ }
98+ topMargin: extensionSize
99+ bottomMargin: extensionSize
100+ height: parent.height - dashItem.height - parent.spacing*2
101+ model: root.model
102+ cacheBuffer: itemHeight * 3
103+ snapMode: interactive ? ListView.SnapToItem : ListView.NoSnap
104+ highlightRangeMode: ListView.ApplyRange
105+ preferredHighlightBegin: (height - itemHeight) / 2
106+ preferredHighlightEnd: (height + itemHeight) / 2
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+
115+ // The height of the area where icons start getting folded
116+ property int foldingAreaHeight: itemHeight * 0.75
117+ property int itemWidth: width
118+ property int itemHeight: width * 7.5 / 8
119+ property int clickFlickSpeed: units.gu(60)
120+ property int draggedIndex: dndArea.draggedIndex
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 {
135+ id: launcherDelegate
136+ objectName: "launcherDelegate" + index
137+ itemHeight: launcherListView.itemHeight
138+ itemWidth: launcherListView.itemWidth
139+ width: itemWidth
140+ height: itemHeight
141+ iconName: model.icon
142+ count: model.count
143+ progress: model.progress
144+ inverted: root.inverted
145+ highlighted: dragging && index === root.highlightIndex
146+ z: -Math.abs(offset)
147+ maxAngle: 60
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+
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+
282+ // First/last item do the scrolling at more than 12 degrees
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
295+ if (clickedItem.angle > 30) {
140296 launcherListView.flick(0, -launcherListView.clickFlickSpeed);
141− } else if (angle < -12) {
297+ } else if (clickedItem.angle < -30) {
142298 launcherListView.flick(0, launcherListView.clickFlickSpeed);
143299 } else {
144300 root.applicationSelected(LauncherModel.get(index).desktopFile);
145301 }
146− return;
147302 }
148303
149− // the rest launches apps up to an angle of 30 degrees
150− if (angle > 30) {
151− launcherListView.flick(0, -launcherListView.clickFlickSpeed);
152− } else if (angle < -30) {
153− launcherListView.flick(0, launcherListView.clickFlickSpeed);
154− } else {
155− root.applicationSelected(LauncherModel.get(index).desktopFile);
304+ onCanceled: {
305+ selectedItem.highlighted = false
306+ selectedItem = undefined;
307+ preDragging = false;
308+ postDragging = false;
309+ }
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+ }
156440 }
157441 }
158442 }
443+ }
159444
160− MouseArea {
161− id: topFoldingArea
162− anchors {
163− left: parent.left
164− right: parent.right
165− top: parent.top
166− topMargin: launcherListView.topMargin
167− }
168− height: launcherListView.itemSize / 2
169− enabled: launcherListView.contentY > -launcherListView.topMargin
170− onClicked: launcherListView.flick(0, launcherListView.clickFlickSpeed)
445+ LauncherDelegate {
446+ id: fakeDragItem
447+ objectName: "fakeDragItem"
448+ visible: dndArea.draggedIndex >= 0 && !dndArea.postDragging
449+ itemWidth: launcherListView.itemWidth
450+ itemHeight: launcherListView.itemHeight
451+ height: itemHeight
452+ width: itemWidth
453+ rotation: root.rotation
454+ highlighted: true
455+ itemOpacity: 0.8
456+
457+ function flatten() {
458+ fakeDragItemAnimation.start();
171459 }
172460
173− MouseArea {
174− id: bottomFoldingArea
175− anchors {
176− left: parent.left
177− right: parent.right
178− bottom: parent.bottom
179− bottomMargin: launcherListView.bottomMargin
461+ UbuntuNumberAnimation {
462+ id: fakeDragItemAnimation
463+ target: fakeDragItem;
464+ properties: "angle,offset";
465+ to: 0
466+ }
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+ }
180507 }
181− height: launcherListView.itemSize / 2
182− enabled: launcherListView.contentHeight - launcherListView.height - launcherListView.contentY > -launcherListView.bottomMargin
183− onClicked: launcherListView.flick(0, -launcherListView.clickFlickSpeed)
184508 }
185509 }
186510 }
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/NetworkIndicatorPage.qml+2−2
@@ -23,7 +23,7 @@ import Unity.IndicatorsLegacy.Network 0.1 as ICNetwork
2323import Ubuntu.Components 0.1
2424
2525PageStack {
26− id: network
26+ id: networkPage
2727 // Stops the search for a parent tree node.
2828 // We don't want it going up to the indicator tabs.
2929 // FIXME: need a better way of doing this.
@@ -60,7 +60,7 @@ PageStack {
6060 id: networkAgent
6161
6262 onSecretRequested: {
63− _network.push(passwordPageComponent, {"token": token});
63+ networkPage.push(passwordPageComponent, {"token": token});
6464 }
6565 }
6666
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−28
@@ -33,7 +33,7 @@ Indicators.IndicatorsModel {
3333 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
3434 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
3535 },
36− 'indicator-messaging' : {
36+ 'indicator-messages' : {
3737 'title': 'Messaging',
3838 'description' : '',
3939 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/MessagingIndicatorWidget.qml',
@@ -49,7 +49,7 @@ Indicators.IndicatorsModel {
4949 'title': 'Networks',
5050 'description' : '',
5151 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
52− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
52+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage2.qml'
5353 },
5454 'indicator-power' : {
5555 'title': 'Battery',
@@ -68,32 +68,6 @@ Indicators.IndicatorsModel {
6868 'description' : '',
6969 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
7070 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
71− },
72−
73− // OLD FORMAT INDICATORS
74− 'chewie-sound' : {
75− 'title': 'Sound',
76− 'description' : '',
77− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/SoundIndicatorWidget.qml',
78− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage.qml'
79− },
80− 'chewie-network' : {
81− 'title': 'Networks',
82− 'description' : '',
83− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorWidget.qml',
84− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage.qml'
85− },
86− 'indicator-battery' : {
87− 'title': 'Battery',
88− 'description' : '',
89− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget.qml',
90− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage.qml'
91− },
92− 'indicator-time' : {
93− 'title': 'Date and Time',
94− 'description' : '',
95− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DatetimeIndicatorWidget.qml',
96− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DatetimeIndicatorPage.qml'
9771 }
9872 }
9973}
Shell.qml+42−15
@@ -15,6 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18+import GSettings 1.0
1819import Ubuntu.Application 0.1
1920import Ubuntu.Components 0.1
2021import Ubuntu.Gestures 0.1
@@ -41,8 +42,8 @@ FocusScope {
4142 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
4243
4344 property real edgeSize: units.gu(2)
44− property url default_background: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
45− property url background: default_background
45+ property url defaultBackground: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
46+ property url background
4647 readonly property real panelHeight: panel.panelHeight
4748
4849 property bool dashShown: dash.shown
@@ -115,6 +116,36 @@ FocusScope {
115116 }
116117 }
117118
119+ GSettings {
120+ id: backgroundSettings
121+ schema.id: "org.gnome.desktop.background"
122+ }
123+ property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
124+ onGSettingsPictureChanged: {
125+ shell.background = gSettingsPicture
126+ }
127+
128+ // This is a dummy image that is needed to determine if the picture url
129+ // in backgroundSettings points to a valid picture file.
130+ // We can't do this with the real background image because setting a
131+ // new source in onStatusChanged triggers a binding loop detection
132+ // inside Image, which causes it not to render even though a valid source
133+ // would be set. We don't mind about this image staying black and just
134+ // use it for verification to populate the source for the real
135+ // background image.
136+ Image {
137+ source: shell.background
138+ height: 0
139+ width: 0
140+ sourceSize.height: 0
141+ sourceSize.width: 0
142+ onStatusChanged: {
143+ if (status == Image.Error && source != shell.defaultBackground) {
144+ shell.background = defaultBackground
145+ }
146+ }
147+ }
148+
118149 VolumeControl {
119150 id: volumeControl
120151 }
@@ -139,12 +170,12 @@ FocusScope {
139170 // through the translucent parts of the shell surface.
140171 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
141172
142− Image {
173+ CrossFadeImage {
143174 id: backgroundImage
144− source: shell.background
145− sourceSize.width: parent.width
146− sourceSize.height: parent.height
175+ objectName: "backgroundImage"
176+
147177 anchors.fill: parent
178+ source: shell.background
148179 }
149180
150181 Rectangle {
@@ -189,7 +220,6 @@ FocusScope {
189220 }
190221 }
191222
192−
193223 Item {
194224 id: stagesOuterContainer
195225
@@ -254,7 +284,6 @@ FocusScope {
254284 ignoreUnknownSignals: true
255285 }
256286
257−
258287 Stage {
259288 id: mainStage
260289
@@ -429,10 +458,6 @@ FocusScope {
429458 }
430459
431460 onUnlocked: greeter.hide()
432− onSelected: {
433− var bgPath = greeter.model.data(uid, LightDM.UserRoles.BackgroundPathRole)
434− shell.background = bgPath ? bgPath : default_background
435− }
436461
437462 onLeftTeaserPressedChanged: {
438463 if (leftTeaserPressed) {
@@ -472,11 +497,13 @@ FocusScope {
472497 }
473498 }
474499
475− onPowerStateChange: {
476− if (state == 0) { // suspend
500+ onDisplayPowerStateChange: {
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) {
477504 powerConnection.setFocused(false);
478505 greeter.show();
479− } else if (state == 1) { // active
506+ } else if (status == Powerd.On) {
480507 powerConnection.setFocused(true);
481508 }
482509 }
build+5−2
@@ -40,11 +40,14 @@ install_dependencies() {
4040 echo "Installing Unity 8 dependencies.."
4141 sudo apt-get install devscripts \
4242 equivs \
43+ gsettings-desktop-schemas \
4344 qmenumodel-qml \
44− qtdeclarative5-ubuntu-ui-toolkit-plugin \
4545 qtdeclarative5-dee-plugin \
46+ qtdeclarative5-gsettings1.0 \
47+ qtdeclarative5-ubuntu-ui-toolkit-plugin \
48+ unity-notifications-impl \
49+ qtdeclarative5-xmllistmodel-plugin \
4650 ubuntu-mobile-icons \
47− unity-notifications-impl-1 \
4851 unity-scope-home \
4952 unity-lens-applications \
5053 || exit 5
debian/changelog+145
@@ -1,3 +1,148 @@
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+
107+unity8 (7.81.3+13.10.20130816.3-0ubuntu1) saucy; urgency=low
108+
109+ [ Michal Hruby ]
110+ * Implement overrideResults() method, which allows us to seamlessly
111+ combine real scope data with mocked data.
112+
113+ [ Michał Sawicz ]
114+ * Wait for activeFocus before typing in autopilot tests. (LP:
115+ #1212580)
116+
117+ [ Nick Dedekind ]
118+ * Fixed network indicator password dialog not appearing. (LP:
119+ #1212730)
120+ * Remove time & battery indicator service info.
121+
122+ [ Albert Astals ]
123+ * Dash category expansion.
124+ * Fix crash in the shell test Give the item a parent, otherwise the
125+ qml engine decides to adopt it and when we do the deleteLater on
126+ them, they have been already deleted. Since we are the parents, we
127+ don't need to call the deleteLAter eiether they'll be properly
128+ deleted on our deletion.
129+
130+ [ Michael Zanetti ]
131+ * make entering text in lockscreen tests more robust. (LP: #1212580)
132+
133+ [ Michael Terry ]
134+ * Define the 'build' target as PHONY so make doesn't get confused by
135+ our 'build' script.
136+ * Add a test for the Powerd plugin shell support.
137+
138+ [ Pawel Stolowski ]
139+ * Bindings for filters.
140+
141+ [ Ubuntu daily release ]
142+ * Automatic snapshot from revision 208
143+
144+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 16 Aug 2013 14:11:35 +0000
145+
1146unity8 (7.81.3+13.10.20130814.3-0ubuntu1) saucy; urgency=low
2147
3148 [ Ted Gould ]
debian/control+5−2
@@ -20,7 +20,7 @@ Build-Depends: cmake,
2020 libsigc++-2.0-dev,
2121 libunity-api-dev (>= 7.80.3),
2222 libunity-core-6.0-dev (<< 7.80),
23− libunity-dev (>= 7.0.3),
23+ libunity-dev (>= 7.0.12),
2424 libunity-scopes-json-def-phone,
2525 libupstart-dev,
2626 libusermetricsoutput1-dev,
@@ -60,8 +60,11 @@ Description: Indicators client test application
6060
6161Package: unity8
6262Architecture: any
63−Depends: qmenumodel-qml (>= 0.2.7),
63+Provides: indicator-renderer,
64+Depends: gsettings-desktop-schemas,
65+ qmenumodel-qml (>= 0.2.7),
6466 qtdeclarative5-dee-plugin,
67+ qtdeclarative5-gsettings1.0,
6568 qtdeclarative5-ubuntu-ui-toolkit-plugin,
6669 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
6770 qtdeclarative5-xmllistmodel-plugin,
plugins/ListViewWithPageHeader/listviewwithpageheader.cpp+1
@@ -1244,6 +1244,7 @@ void ListViewWithPageHeader::updatePolish()
12441244 }
12451245
12461246 m_contentHeightDirty = false;
1247+ adjustMinYExtent();
12471248 setContentHeight(contentHeight);
12481249 }
12491250}
plugins/Powerd/Powerd.cpp+2−2
@@ -30,7 +30,7 @@ Powerd::Powerd(QObject* parent)
3030 powerd->connection().connect("com.canonical.powerd",
3131 "/com/canonical/powerd",
3232 "com.canonical.powerd",
33− "SysPowerStateChange",
33+ "DisplayPowerStateChange",
3434 this,
35− SIGNAL(powerStateChange(int)));
35+ SIGNAL(displayPowerStateChange(int, unsigned int)));
3636}
plugins/Powerd/Powerd.h+15−1
@@ -26,12 +26,26 @@
2626class Powerd: public QObject
2727{
2828 Q_OBJECT
29+ Q_ENUMS(Status)
30+ Q_FLAGS(DisplayFlag DisplayFlags)
2931
3032public:
33+ enum DisplayFlag {
34+ UseProximity = 1, // Use proximity sensor to override screen state
35+ DisableAutoBrightness = 2, // Force autobrightness to be disabled
36+ Bright = 4, // Request the screen to stay bright
37+ };
38+ Q_DECLARE_FLAGS(DisplayFlags, DisplayFlag)
39+
40+ enum Status {
41+ Off,
42+ On,
43+ };
44+
3145 explicit Powerd(QObject *parent = 0);
3246
3347Q_SIGNALS:
34− void powerStateChange(int state);
48+ void displayPowerStateChange(int status, unsigned int flags);
3549
3650private:
3751 QDBusInterface *powerd;
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -12,6 +12,7 @@ find_package(Qt5Core REQUIRED)
1212find_package(Qt5Quick REQUIRED)
1313
1414add_subdirectory(qml)
15+add_subdirectory(Network)
1516
1617include_directories(
1718 ${CMAKE_CURRENT_SOURCE_DIR}
plugins/Unity/Indicators/Network/CMakeLists.txtnew file+45
@@ -0,0 +1,45 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+project(IndicatorsNetworkQml)
5+
6+pkg_check_modules(GLIB glib-2.0>=2.32)
7+pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
8+pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
9+
10+if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
11+ set(INDICATORS_NETWORK_SRC
12+ networkagent.cpp
13+ plugin.cpp
14+ secret-agent.c
15+ )
16+
17+ add_library(IndicatorsNetworkQml MODULE
18+ ${INDICATORS_NETWORK_SRC}
19+ )
20+
21+ qt5_use_modules(IndicatorsNetworkQml Core Qml Widgets)
22+
23+ set_target_properties(IndicatorsNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
24+
25+ include_directories(
26+ ${CMAKE_CURRENT_SOURCE_DIR}
27+ ${GLIB_INCLUDE_DIRS}
28+ ${LIBNM_GLIB_INCLUDE_DIRS}
29+ ${LIBNM_UTIL_INCLUDE_DIRS}
30+ )
31+
32+ target_link_libraries(IndicatorsNetworkQml
33+ ${GLIB_LIBRARIES}
34+ ${LIBNM_GLIB_LIBRARIES}
35+ ${LIBNM_UTIL_LIBRARIES}
36+ )
37+
38+ # export the qmltypes and plugin files
39+ export_qmlplugin(Unity.Indicators.Network 0.1 Unity/Indicators/Network TARGETS IndicatorsNetworkQml)
40+
41+else()
42+ message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
43+endif()
44+
45+add_subdirectory(qml)
plugins/Unity/Indicators/Network/networkagent.cppnew file+146
@@ -0,0 +1,146 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#include "networkagent.h"
22+
23+#include <QVariant>
24+#include <QApplication>
25+#include <QDebug>
26+
27+class NetworkAgentToken
28+{
29+public:
30+ NMConnection *connection;
31+ NMSettingWirelessSecurity *wisec;
32+ guint id;
33+ QByteArray keyMgmt;
34+
35+ NetworkAgentToken(NMConnection *connection,
36+ NMSettingWirelessSecurity *wisec,
37+ guint id,
38+ QByteArray keyMgmt)
39+ : connection(connection),
40+ wisec(wisec),
41+ id(id),
42+ keyMgmt(keyMgmt)
43+ {
44+ if (connection) {
45+ g_object_ref(connection);
46+ } else {
47+ qWarning() << "invalid connection object";
48+ }
49+ }
50+
51+ ~NetworkAgentToken()
52+ {
53+ if (connection) {
54+ g_object_unref(connection);
55+ }
56+ }
57+};
58+
59+NetworkAgent::NetworkAgent(QObject *parent)
60+ : QObject(parent)
61+{
62+ m_agent = unity_settings_secret_agent_new();
63+ g_signal_connect(G_OBJECT(m_agent),
64+ UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
65+ G_CALLBACK(onSecretRequested),
66+ this);
67+
68+ g_signal_connect(G_OBJECT(m_agent),
69+ UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
70+ G_CALLBACK(onSecretRequestCancelled),
71+ this);
72+}
73+
74+NetworkAgent::~NetworkAgent()
75+{
76+ nm_secret_agent_unregister(NM_SECRET_AGENT(m_agent));
77+ g_object_unref(m_agent);
78+}
79+
80+void NetworkAgent::authenticate(const QVariant &token, const QString &key)
81+{
82+ NetworkAgentToken *pToken = (NetworkAgentToken *) token.value<void *>();
83+ if (pToken == NULL) {
84+ return;
85+ }
86+
87+ if ((pToken->keyMgmt == "wpa-none") || (pToken->keyMgmt == "wpa-psk")) {
88+ g_object_set(G_OBJECT(pToken->wisec),
89+ NM_SETTING_WIRELESS_SECURITY_PSK, qPrintable(key),
90+ NULL);
91+ } else if (pToken->keyMgmt == "none") {
92+ g_object_set(G_OBJECT(pToken->wisec),
93+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, qPrintable(key),
94+ NULL);
95+ }
96+
97+ GHashTable *settings = nm_connection_to_hash(pToken->connection,
98+ NM_SETTING_HASH_FLAG_ALL);
99+
100+ unity_settings_secret_agent_provide_secret(m_agent,
101+ pToken->id, settings);
102+ g_hash_table_unref(settings);
103+ delete pToken;
104+}
105+
106+void NetworkAgent::cancel(const QVariant &token)
107+{
108+ NetworkAgentToken *pToken = (NetworkAgentToken *) token.value<void *>();
109+ if (pToken != NULL) {
110+ unity_settings_secret_agent_cancel_request(m_agent, pToken->id);
111+ delete pToken;
112+ }
113+}
114+
115+void NetworkAgent::onSecretRequested(UnitySettingsSecretAgent* /*agent*/,
116+ guint id,
117+ NMConnection* connection,
118+ const char* /*setting_name*/,
119+ const char** /*hints*/,
120+ NMSecretAgentGetSecretsFlags /*flags*/,
121+ NetworkAgent* self)
122+{
123+ NetworkAgentToken *token = new NetworkAgentToken(connection,
124+ NULL,
125+ id,
126+ NULL);
127+
128+ token->wisec = nm_connection_get_setting_wireless_security(connection);
129+ if (token->wisec) {
130+ token->keyMgmt = nm_setting_wireless_security_get_key_mgmt(token->wisec);
131+ }
132+
133+ QVariant varToken = qVariantFromValue((void *) token);
134+ Q_EMIT self->secretRequested(varToken);
135+}
136+
137+void NetworkAgent::onSecretRequestCancelled(UnitySettingsSecretAgent* /*agent*/,
138+ guint /*id*/,
139+ NMConnection* /*connection*/,
140+ const char* /*setting_name*/,
141+ const char** /*hints*/,
142+ NMSecretAgentGetSecretsFlags /*flags*/,
143+ NetworkAgent* /*self*/)
144+{
145+ //Do nothing for now
146+}
plugins/Unity/Indicators/Network/networkagent.hnew file+65
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#ifndef NETWORKAGENT_H
22+#define NETWORKAGENT_H
23+
24+#include "secret-agent.h"
25+
26+#include <QObject>
27+
28+class NetworkAgentToken;
29+
30+class NetworkAgent : public QObject
31+{
32+ Q_OBJECT
33+public:
34+ NetworkAgent(QObject *parent=0);
35+ ~NetworkAgent();
36+
37+ Q_INVOKABLE void authenticate(const QVariant &token, const QString &key);
38+ Q_INVOKABLE void cancel(const QVariant &token);
39+
40+Q_SIGNALS:
41+ void secretRequested(const QVariant &token);
42+ void secretRequestCancelled();
43+
44+private:
45+ UnitySettingsSecretAgent *m_agent;
46+
47+ static void onSecretRequested(UnitySettingsSecretAgent *agent,
48+ guint id,
49+ NMConnection *connection,
50+ const char *setting_name,
51+ const char **hints,
52+ NMSecretAgentGetSecretsFlags flags,
53+ NetworkAgent *self);
54+
55+ static void onSecretRequestCancelled(UnitySettingsSecretAgent *agent,
56+ guint id,
57+ NMConnection *connection,
58+ const char *setting_name,
59+ const char **hints,
60+ NMSecretAgentGetSecretsFlags flags,
61+ NetworkAgent *self);
62+
63+};
64+
65+#endif
plugins/Unity/Indicators/Network/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2012 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+// Qt
20+#include <QtQml>
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 @@
1919
2020import QtQuick 2.0
2121import Unity.Indicators 0.1 as Indicators
22+import Unity.Indicators.Network 0.1 as ICNetwork
2223
2324Item {
2425 id: menuFactory
@@ -38,6 +39,9 @@ Item {
3839
3940 "com.canonical.unity.slider" : sliderMenu,
4041 "com.canonical.unity.switch" : switchMenu,
42+
43+ "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
44+ "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
4145 }
4246
4347 Component {
@@ -57,6 +61,23 @@ Item {
5761 Component { id: progressMenu; Indicators.ProgressMenuItem {} }
5862 Component { id: standardMenu; Indicators.StandardMenuItem {} }
5963 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+ }
6081
6182 function load(modelData) {
6283 if (modelData.type !== undefined) {
plugins/Unity/IndicatorsLegacy/Network/CMakeLists.txt+8−8
@@ -1,26 +1,26 @@
11# export_qmlplugin macro
22include(Plugins)
33
4−project(IndicatorsNetworkQml)
4+project(IndicatorsLegacyNetworkQml)
55
66pkg_check_modules(GLIB glib-2.0>=2.32)
77pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
88pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
99
1010if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
11− set(INDICATORS_NETWORK_SRC
11+ set(INDICATORS_LEGACY_NETWORK_SRC
1212 networkagent.cpp
1313 plugin.cpp
1414 secret-agent.c
1515 )
1616
17− add_library(IndicatorsNetworkQml MODULE
18− ${INDICATORS_NETWORK_SRC}
17+ add_library(IndicatorsLegacyNetworkQml MODULE
18+ ${INDICATORS_LEGACY_NETWORK_SRC}
1919 )
2020
21− qt5_use_modules(IndicatorsNetworkQml Core Qml Widgets)
21+ qt5_use_modules(IndicatorsLegacyNetworkQml Core Qml Widgets)
2222
23− set_target_properties(IndicatorsNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
23+ set_target_properties(IndicatorsLegacyNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
2424
2525 include_directories(
2626 ${CMAKE_CURRENT_SOURCE_DIR}
@@ -29,14 +29,14 @@ if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
2929 ${LIBNM_UTIL_INCLUDE_DIRS}
3030 )
3131
32− target_link_libraries(IndicatorsNetworkQml
32+ target_link_libraries(IndicatorsLegacyNetworkQml
3333 ${GLIB_LIBRARIES}
3434 ${LIBNM_GLIB_LIBRARIES}
3535 ${LIBNM_UTIL_LIBRARIES}
3636 )
3737
3838 # export the qmltypes and plugin files
39− export_qmlplugin(Unity.IndicatorsLegacy.Network 0.1 Unity/IndicatorsLegacy/Network TARGETS IndicatorsNetworkQml)
39+ export_qmlplugin(Unity.IndicatorsLegacy.Network 0.1 Unity/IndicatorsLegacy/Network TARGETS IndicatorsLegacyNetworkQml)
4040
4141else()
4242 message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
plugins/Unity/IndicatorsLegacy/Network/qml/qmldir+1−1
@@ -1,5 +1,5 @@
11module Unity.IndicatorsLegacy.Network
2−plugin IndicatorsNetworkQml
2+plugin IndicatorsLegacyNetworkQml
33typeinfo plugin.qmltypes
44
55NetworkPage 0.1 NetworkPage.qml
plugins/Unity/IndicatorsLegacy/flatmenuproxymodel.cpp+13
@@ -80,6 +80,19 @@ void FlatMenuProxyModel::setSourceModel(QAbstractItemModel *source)
8080 SIGNAL(modelReset()),
8181 SLOT(onModelReset()), Qt::QueuedConnection);
8282
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+
8396 connect(source,
8497 SIGNAL(statusChanged(DBusEnums::ConnectionStatus)),
8598 SIGNAL(statusChanged()), Qt::QueuedConnection);
plugins/Unity/Launcher/backend/launcherbackend.cpp+1−1
@@ -101,7 +101,7 @@ bool LauncherBackend::isPinned(const QString &appId) const
101101{
102102 // TODO: return app's pinned state from settings
103103 Q_UNUSED(appId)
104− return true;
104+ return false;
105105}
106106
107107void LauncherBackend::setPinned(const QString &appId, bool pinned)
plugins/Unity/Launcher/common/quicklistentry.cpp+5
@@ -52,3 +52,8 @@ void QuickListEntry::setIcon(const QString &icon)
5252{
5353 m_icon = icon;
5454}
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:
3535 QString icon() const;
3636 void setIcon(const QString &icon);
3737
38+ bool clickable() const;
39+
3840private:
3941 QString m_actionId;
4042 QString m_text;
plugins/Unity/Launcher/launcheritem.cpp+11−1
@@ -33,7 +33,13 @@ LauncherItem::LauncherItem(const QString &appId, const QString &desktopFile, con
3333 m_count(0),
3434 m_quickList(new QuickListModel(this))
3535{
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);
3743}
3844
3945QString LauncherItem::appId() const
@@ -65,6 +71,10 @@ void LauncherItem::setPinned(bool pinned)
6571{
6672 if (m_pinned != pinned) {
6773 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);
6878 Q_EMIT pinnedChanged(pinned);
6979 }
7080}
plugins/Unity/Launcher/launchermodel.cpp+34−4
@@ -82,8 +82,25 @@ unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) con
8282
8383void LauncherModel::move(int oldIndex, int newIndex)
8484{
85− // Perform the move in our list
86− beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
85+ // Make sure its not moved outside the lists
86+ if (newIndex < 0) {
87+ newIndex = 0;
88+ }
89+ if (newIndex >= m_list.count()) {
90+ newIndex = m_list.count()-1;
91+ }
92+
93+ // Nothing to do?
94+ if (oldIndex == newIndex) {
95+ return;
96+ }
97+
98+ // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
99+ // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
100+ // adjust the model's index by +1 in case we're moving upwards
101+ int newModelIndex = newIndex > oldIndex ? newIndex+1 : newIndex;
102+
103+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
87104 m_list.move(oldIndex, newIndex);
88105 endMoveRows();
89106
@@ -140,10 +157,23 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
140157 return;
141158 }
142159
143− QuickListModel *model = qobject_cast<QuickListModel*>(m_list.at(index)->quickList());
160+ LauncherItem *item = m_list.at(index);
161+ QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
144162 if (model) {
145163 QString actionId = model->get(actionIndex).actionId();
146− m_backend->triggerQuickListAction(appId, actionId);
164+
165+ // Check if this is one of the launcher actions we handle ourselves
166+ if (actionId == "pin_item") {
167+ if (item->pinned()) {
168+ requestRemove(appId);
169+ } else {
170+ pin(appId);
171+ }
172+
173+ // Nope, we don't know this action, let the backend forward it to the application
174+ } else {
175+ m_backend->triggerQuickListAction(appId, actionId);
176+ }
147177 }
148178}
149179
plugins/Unity/Launcher/plugin.cpp+2
@@ -41,7 +41,9 @@ void UnityLauncherPlugin::registerTypes(const char *uri)
4141
4242 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
4343 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
44+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListInterface", "Abstract Interface. Cannot be instantiated.");
4445
4546 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
4647 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.");
4749}
plugins/Unity/Launcher/quicklistmodel.cpp+13
@@ -37,6 +37,17 @@ void QuickListModel::appendAction(const QuickListEntry &entry)
3737 endInsertRows();
3838}
3939
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+
4051QuickListEntry QuickListModel::get(int index) const
4152{
4253 return m_list.at(index);
@@ -55,6 +66,8 @@ QVariant QuickListModel::data(const QModelIndex &index, int role) const
5566 return m_list.at(index.row()).text();
5667 case RoleIcon:
5768 return m_list.at(index.row()).icon();
69+ case RoleClickable:
70+ return m_list.at(index.row()).clickable();
5871 }
5972 return QVariant();
6073}
plugins/Unity/Launcher/quicklistmodel.h+10
@@ -36,6 +36,16 @@ public:
3636 ~QuickListModel();
3737
3838 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+
3949 QuickListEntry get(int index) const;
4050
4151 int rowCount(const QModelIndex &parent = QModelIndex()) const;
plugins/Unity/categories.cpp+6
@@ -39,6 +39,7 @@ Categories::Categories(QObject* parent)
3939 m_roles[Categories::RoleIcon] = "icon";
4040 m_roles[Categories::RoleRenderer] = "renderer";
4141 m_roles[Categories::RoleContentType] = "contentType";
42+ m_roles[Categories::RoleProgressSource] = "progressSource";
4243 m_roles[Categories::RoleHints] = "hints";
4344 m_roles[Categories::RoleResults] = "results";
4445 m_roles[Categories::RoleCount] = "count";
@@ -198,6 +199,11 @@ Categories::data(const QModelIndex& index, int role) const
198199 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
199200 return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
200201 }
202+ case RoleProgressSource:
203+ {
204+ auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
205+ return hints.contains("progress-source") ? hints["progress-source"] : QVariant();
206+ }
201207 case RoleHints:
202208 return DeeListModel::data(index, CategoryColumn::HINTS);
203209 case RoleResults:
plugins/Unity/categories.h+1
@@ -47,6 +47,7 @@ public:
4747 RoleIcon,
4848 RoleRenderer,
4949 RoleContentType,
50+ RoleProgressSource,
5051 RoleHints,
5152 RoleResults,
5253 RoleCount,
plugins/Unity/filters.cpp+4
@@ -23,6 +23,7 @@
2323
2424// Local
2525#include "filter.h"
26+#include "genericoptionsmodel.h"
2627
2728// Qt
2829#include <QDebug>
@@ -77,6 +78,8 @@ QVariant Filters::data(const QModelIndex& index, int role) const
7778 return filter->collapsed();
7879 case Filters::RoleFiltering:
7980 return filter->filtering();
81+ case Filters::RoleOptions:
82+ return QVariant::fromValue(filter->options());
8083 default:
8184 break;
8285 }
@@ -93,6 +96,7 @@ QHash<int, QByteArray> Filters::roleNames() const
9396 roles[Filters::RoleVisible] = "visible";
9497 roles[Filters::RoleCollapsed] = "collapsed";
9598 roles[Filters::RoleFiltering] = "filtering";
99+ roles[Filters::RoleOptions] = "options";
96100 return roles;
97101}
98102
plugins/Unity/filters.h+2−1
@@ -48,7 +48,8 @@ public:
4848 RoleRendererName,
4949 RoleVisible,
5050 RoleCollapsed,
51− RoleFiltering
51+ RoleFiltering,
52+ RoleOptions
5253 };
5354
5455 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
7676 return m_options.size ();
7777}
7878
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+
7997AbstractFilterOption* GenericOptionsModel::getRawOption(QVector<AbstractFilterOption *>::size_type idx) const
8098{
8199 if (idx < m_options.size())
plugins/Unity/genericoptionsmodel.h+3
@@ -49,6 +49,9 @@ public:
4949 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
5050 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
5151
52+ Q_INVOKABLE void setActive(QVector<AbstractFilterOption *>::size_type idx, bool value);
53+ Q_INVOKABLE void setActive(const QString &id, bool value);
54+
5255 AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
5356
5457public Q_SLOTS:
plugins/Utils/qlimitproxymodelqml.cpp+90−4
@@ -25,6 +25,8 @@ QLimitProxyModelQML::QLimitProxyModelQML(QObject *parent)
2525 , m_limit(-1)
2626 , m_sourceInserting(false)
2727 , m_sourceRemoving(false)
28+ , m_dataChangedBegin(-1)
29+ , m_dataChangedEnd(-1)
2830{
2931 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
3032 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
@@ -130,8 +132,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeInserted(const QModelIndex &parent, int
130132 beginInsertRows(mapFromSource(parent), start, end);
131133 m_sourceInserting = true;
132134 } else if (start < m_limit) {
133− beginInsertRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
134− m_sourceInserting = true;
135+ const int nSourceAddedItems = end - start + 1;
136+ const int currentCount = QIdentityProxyModel::rowCount();
137+ if (currentCount + nSourceAddedItems <= m_limit) {
138+ // After Inserting items we will be under the limit
139+ // so just proceed with the insertion normally
140+ beginInsertRows(mapFromSource(parent), start, end);
141+ m_sourceInserting = true;
142+ } else if (currentCount >= m_limit) {
143+ // We are already over the limit so to our users we are not adding items, just
144+ // changing it's data, i.e we had something like
145+ // A B C D E
146+ // with a limit of 5
147+ // after inserting (let's say three 'F' at position 1) we will have
148+ // A F F F B
149+ // so we just need to signal a dataChanged from 1 to 4
150+ m_dataChangedBegin = start;
151+ m_dataChangedEnd = m_limit - 1;
152+ } else { // currentCount < m_limit && currentCount + nSourceAddedItems > m_limit
153+ // We have less items than the limit but after adding them we will be over
154+ // To our users this means we need to insert some items and change the
155+ // data of some others, i.e we had something like
156+ // A B C
157+ // with a limit of 5
158+ // after inserting (let's say three 'F' at position 1) we will have
159+ // A F F F B
160+ // so we need to signal an insetion from position 1 to 2, instead of from
161+ // position 1 to 3 and a after that a data changed from 3 to 4
162+ const int nItemsToInsert = m_limit - currentCount;
163+ beginInsertRows(mapFromSource(parent), start, start + nItemsToInsert - 1);
164+ m_sourceInserting = true;
165+ m_dataChangedBegin = start + nItemsToInsert;
166+ m_dataChangedEnd = m_limit - 1;
167+ if (m_dataChangedBegin > m_dataChangedEnd) {
168+ // Just in case we were empty and insert 6 items with a limit of 5
169+ // We don't want to signal a dataChanged from 5 to 4
170+ m_dataChangedBegin = -1;
171+ m_dataChangedEnd = -1;
172+ }
173+ }
135174 }
136175}
137176
@@ -142,8 +181,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int s
142181 beginRemoveRows(mapFromSource(parent), start, end);
143182 m_sourceRemoving = true;
144183 } else if (start < m_limit) {
145− beginRemoveRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
146− m_sourceRemoving = true;
184+ const int nSourceRemovedItems = end - start + 1;
185+ const int currentCount = QIdentityProxyModel::rowCount();
186+ if (currentCount <= m_limit) {
187+ // We are already under the limit so
188+ // so just proceed with the removal normally
189+ beginRemoveRows(mapFromSource(parent), start, end);
190+ m_sourceRemoving = true;
191+ } else if (currentCount - nSourceRemovedItems >= m_limit) {
192+ // Even after removing items we will be at or over the limit
193+ // So to our users we are not removing anything, just changing the data
194+ // i.e. we had a internal model with
195+ // A B C D E F G H
196+ // and a limit of 5, our users just see
197+ // A B C D E
198+ // so if we remove 3 items starting at 1 we have to expose
199+ // A E F G H
200+ // that is, a dataChanged from 1 to 4
201+ m_dataChangedBegin = start;
202+ m_dataChangedEnd = m_limit - 1;
203+ } else { // currentCount > m_limit && currentCount - nSourceRemovedItems < m_limit
204+ // We have more items than the limit but after removing we will be below it
205+ // So to our users we both removing and changing the data
206+ // i.e. we had a internal model with
207+ // A B C D E F G
208+ // and a limit of 5, our users just see
209+ // A B C D E
210+ // so if we remove items from 1 to 3 we have to expose
211+ // A E F G
212+ // that is, a remove from 4 to 4 and a dataChanged from 1 to 3
213+ const int nItemsToRemove = m_limit - (currentCount - nSourceRemovedItems);
214+ beginRemoveRows(mapFromSource(parent), m_limit - nItemsToRemove, m_limit - 1);
215+ m_sourceRemoving = true;
216+ m_dataChangedBegin = start;
217+ m_dataChangedEnd = m_limit - nItemsToRemove - 1;
218+ if (m_dataChangedBegin > m_dataChangedEnd) {
219+ m_dataChangedBegin = -1;
220+ m_dataChangedEnd = -1;
221+ }
222+ }
147223 }
148224}
149225
@@ -154,6 +230,11 @@ QLimitProxyModelQML::sourceRowsInserted(const QModelIndex & /*parent*/, int /*st
154230 endInsertRows();
155231 m_sourceInserting = false;
156232 }
233+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
234+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
235+ m_dataChangedBegin = -1;
236+ m_dataChangedEnd = -1;
237+ }
157238}
158239
159240void
@@ -163,4 +244,9 @@ QLimitProxyModelQML::sourceRowsRemoved(const QModelIndex & /*parent*/, int /*sta
163244 endRemoveRows();
164245 m_sourceRemoving = false;
165246 }
247+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
248+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
249+ m_dataChangedBegin = -1;
250+ m_dataChangedEnd = -1;
251+ }
166252}
plugins/Utils/qlimitproxymodelqml.h+2
@@ -55,6 +55,8 @@ private:
5555 int m_limit;
5656 bool m_sourceInserting;
5757 bool m_sourceRemoving;
58+ int m_dataChangedBegin;
59+ int m_dataChangedEnd;
5860};
5961
6062#endif // QLIMITPROXYMODELQML_H
po/ar.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
1818"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/az.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/bg.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/ca.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/de.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/el.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/en_AU.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/en_GB.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/eo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/es.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/fi.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/fo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/fr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/gl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/he.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/hr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/hu.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/hy.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/id.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/it.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ja.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/kn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ko.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ky.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/lv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/my.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/nb.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/nl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/pl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
1818"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/pt_BR.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ro.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
1818"== 0) && (n != 0))) ? 2: 1));\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/ru.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/si.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/sl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
1818"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/sr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/sv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/te.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/tr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/ug.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:25+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/uk.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
20−"X-Generator: Launchpad (build 16723)\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
po/zh_CN.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
po/zh_TW.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-08-14 06:26+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
src/Panel/Indicators/servicefiles/CMakeLists.txt−4
@@ -1,9 +1,5 @@
11file(GLOB INDICATOR_SERVICE_FILES
2− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.battery
32 ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.messages
4− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.time
5− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.settings.network
6− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.settings.sound
73)
84
95# copy indicators files into build directory for shadow builds
src/Panel/Indicators/servicefiles/com.canonical.indicator.batterydeleted−10
@@ -1,10 +0,0 @@
1−[Indicator Service]
2−Name=indicator-battery
3−ObjectPath=/com/canonical/indicator/battery
4−Position=40
5−
6−[phone]
7−ObjectPath=/com/canonical/indicator/battery/phone
8−
9−[desktop]
10−ObjectPath=/com/canonical/indicator/battery/phone
src/Panel/Indicators/servicefiles/com.canonical.indicator.messages+1−1
@@ -1,5 +1,5 @@
11[Indicator Service]
2−Name=indicator-messaging
2+Name=indicator-messages
33ObjectPath=/com/canonical/indicator/messages
44Position=50
55
src/Panel/Indicators/servicefiles/com.canonical.indicator.timedeleted−10
@@ -1,10 +0,0 @@
1−[Indicator Service]
2−Name=indicator-time
3−ObjectPath=/com/canonical/indicator/time
4−Position=20
5−
6−[phone]
7−ObjectPath=/com/canonical/indicator/time/phone
8−
9−[desktop]
10−ObjectPath=/com/canonical/indicator/time/desktop
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
src/Panel/Indicators/servicefiles/com.canonical.settings.sounddeleted−10
@@ -1,10 +0,0 @@
1−[Indicator Service]
2−Name=chewie-sound
3−ObjectPath=/com/canonical/settings/sound
4−Position=30
5−
6−[phone]
7−ObjectPath=/com/canonical/settings/sound/phone
8−
9−[desktop]
10−ObjectPath=/com/canonical/settings/sound/desktop
tests/autopilot/unity8/indicators_client/tests/test_battery.py+14−8
@@ -15,11 +15,21 @@ from unity8.indicators_client.tests import IndicatorsTestCase
1515from time import sleep
1616import random
1717import math
18+import unittest
1819
1920class TestDisplayMenus(IndicatorsTestCase):
2021 def setUp(self):
2122 super(TestDisplayMenus, self).setUp(self.geometry, self.grid_unit)
2223
24+ def tearDown(self):
25+ # self.main_window.get_indicators_client().reset()
26+ super(TestDisplayMenus, self).tearDown()
27+
28+ # START SECTION
29+ # We need to move these to emulators once autopilot supports emulators for
30+ # applications started with AutopilotTestCase.launch_test_application
31+
32+ def open_battery_menu(self):
2333 # This opens the messaging menu so you don't have to do that in
2434 # every test case
2535 self.assertThat(lambda: self.main_window.get_battery_menu(), Eventually(NotEquals(None)));
@@ -31,14 +41,6 @@ class TestDisplayMenus(IndicatorsTestCase):
3141 page_loader = fn_loader();
3242 self.assertThat(page_loader.progress, Eventually(Equals(1.0)))
3343
34− def tearDown(self):
35− # self.main_window.get_indicators_client().reset()
36− super(TestDisplayMenus, self).tearDown()
37−
38− # START SECTION
39− # We need to move these to emulators once autopilot supports emulators for
40− # applications started with AutopilotTestCase.launch_test_application
41−
4244 def switch_auto_brightness(self, auto_brightness):
4345 ab_switch = auto_brightness.select_single("CheckBox");
4446 self.assertThat(ab_switch, NotEquals(None))
@@ -65,8 +67,10 @@ class TestDisplayMenus(IndicatorsTestCase):
6567
6668 old_ab_value = brightness_slider.value
6769
70+ @unittest.skip("Battery indicator has been removed.")
6871 def test_auto_bright_switch(self):
6972 """Test the auto-bright switch"""
73+ self.open_battery_menu()
7074
7175 # wait for the switch menu item
7276 fn_auto_brightness = lambda: self.app.select_single("SwitchMenuItem", objectName="auto-brightness");
@@ -84,8 +88,10 @@ class TestDisplayMenus(IndicatorsTestCase):
8488 # TODO - should check backend when it's introspectable.
8589 self.assertThat(auto_brightness.checked, Eventually(NotEquals(old_ab_value)))
8690
91+ @unittest.skip("Battery indicator has been removed.")
8792 def test_brightness_slider(self):
8893 """Test the auto-bright switch"""
94+ self.open_battery_menu()
8995
9096 fn_brightness_menu = lambda: self.app.select_single("SliderMenuItem", objectName="brightness");
9197 # FIXME: this should go away when we switch to indicator-power
tests/autopilot/unity8/shell/tests/test_lock_screen.py+13−2
@@ -146,7 +146,8 @@ class TestLockscreen(UnityTestCase):
146146 pinentryField = self.main_window.get_pinentryField()
147147 self.touch.tap_object(pinentryField)
148148 self.assertThat(pinentryField.activeFocus, Eventually(Equals(True)))
149− self.keyboard.type(passphrase)
149+ for character in passphrase:
150+ self._type_character(character, pinentryField)
150151 logger.debug("Typed passphrase: %s", pinentryField.text)
151152 self.keyboard.type("\n")
152153
@@ -164,6 +165,16 @@ class TestLockscreen(UnityTestCase):
164165 prompt = self.main_window.get_greeter().get_prompt()
165166 self.touch.tap_object(prompt)
166167 self.assertThat(prompt.activeFocus, Eventually(Equals(True)))
167− self.keyboard.type(passphrase)
168+ for character in passphrase:
169+ self._type_character(character, prompt)
168170 logger.debug("Typed passphrase: %s", prompt.text)
169171 self.keyboard.type("\n")
172+
173+ def _type_character(self, character, prompt, retries=5):
174+ current_text = prompt.text
175+ self.keyboard.type(character)
176+ try:
177+ self.assertThat(prompt.text, Eventually(Equals(current_text + character)))
178+ except AssertionError:
179+ if retries > 0:
180+ self._type_character(character, prompt, retries-1)
tests/data/unity/backgrounds/blue.pngnew file

Binary file not shown.

tests/data/unity/backgrounds/red.pngnew file

Binary file not shown.

tests/mocks/CMakeLists.txt+1
@@ -1,3 +1,4 @@
1+add_subdirectory(GSettings.1.0)
12add_subdirectory(HudClient)
23add_subdirectory(libusermetrics)
34add_subdirectory(LightDM)
tests/mocks/GSettings.1.0/CMakeLists.txtnew file+23
@@ -0,0 +1,23 @@
1+include_directories(
2+ ${CMAKE_CURRENT_BINARY_DIR}
3+ ${Qt5Core_INCLUDE_DIRS}
4+ ${Qt5Quick_INCLUDE_DIRS}
5+)
6+
7+set(GSettingsQML_SOURCES
8+ fake_gsettings.cpp
9+ plugin.cpp
10+)
11+
12+add_library(FakeGSettingsQml MODULE ${GSettingsQML_SOURCES})
13+target_link_libraries(FakeGSettingsQml
14+ ${Qt5Core_LIBRARIES}
15+ ${Qt5Quick_LIBRARIES}
16+)
17+
18+qt5_use_modules(FakeGSettingsQml Qml)
19+
20+# copy qmldir file into build directory for shadow builds
21+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
22+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
23+ )
tests/mocks/GSettings.1.0/fake_gsettings.cppnew 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+
17+#include "fake_gsettings.h"
18+
19+#include <QList>
20+
21+GSettingsControllerQml* GSettingsControllerQml::s_controllerInstance = 0;
22+
23+GSettingsControllerQml::GSettingsControllerQml() {
24+}
25+
26+GSettingsControllerQml::~GSettingsControllerQml() {
27+ s_controllerInstance = 0;
28+}
29+
30+GSettingsControllerQml* GSettingsControllerQml::instance() {
31+ if (!s_controllerInstance) {
32+ s_controllerInstance = new GSettingsControllerQml();
33+ }
34+ return s_controllerInstance;
35+}
36+
37+void GSettingsControllerQml::registerSettingsObject(GSettingsQml *obj) {
38+ m_registeredGSettings.append(obj);
39+}
40+
41+void GSettingsControllerQml::unRegisterSettingsObject(GSettingsQml *obj) {
42+ m_registeredGSettings.removeOne(obj);
43+}
44+
45+void GSettingsControllerQml::setPictureUri(const QString &str) {
46+ Q_FOREACH (GSettingsQml *obj, m_registeredGSettings) {
47+ obj->setPictureUri(str);
48+ }
49+}
50+
51+GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {
52+}
53+
54+QByteArray GSettingsSchemaQml::id() const {
55+ return m_id;
56+}
57+
58+void GSettingsSchemaQml::setId(const QByteArray &id) {
59+ if (m_id.isEmpty()) {
60+ qWarning("GSettings.schema.id may only be set on construction");
61+ return;
62+ }
63+
64+ m_id = id;
65+}
66+
67+QByteArray GSettingsSchemaQml::path() const {
68+ return m_path;
69+}
70+
71+void GSettingsSchemaQml::setPath(const QByteArray &path) {
72+ if (m_path.isEmpty()) {
73+ qWarning("GSettings.schema.path may only be set on construction");
74+ return;
75+ }
76+
77+ m_path = path;
78+}
79+
80+GSettingsQml::GSettingsQml(QObject *parent): QObject(parent) {
81+ m_schema = new GSettingsSchemaQml(this);
82+ GSettingsControllerQml::instance()->registerSettingsObject(this);
83+}
84+
85+GSettingsQml::~GSettingsQml() {
86+ GSettingsControllerQml::instance()->unRegisterSettingsObject(this);
87+}
88+
89+GSettingsSchemaQml * GSettingsQml::schema() const {
90+ return m_schema;
91+}
92+
93+QString GSettingsQml::pictureUri() const {
94+ return m_pictureUri;
95+}
96+
97+void GSettingsQml::setPictureUri(const QString &str) {
98+ if (str != m_pictureUri) {
99+ m_pictureUri = str;
100+ Q_EMIT pictureUriChanged(m_pictureUri);
101+ }
102+}
tests/mocks/GSettings.1.0/fake_gsettings.hnew file+90
@@ -0,0 +1,90 @@
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+
17+#ifndef FAKE_GSETTINGS_H
18+#define FAKE_GSETTINGS_H
19+
20+#include <QList>
21+#include <QtQml>
22+
23+class GSettingsSchemaQml: public QObject
24+{
25+ Q_OBJECT
26+
27+ Q_PROPERTY(QByteArray id READ id WRITE setId)
28+
29+public:
30+ GSettingsSchemaQml(QObject *parent = NULL);
31+
32+ QByteArray id() const;
33+ void setId(const QByteArray &id);
34+
35+ QByteArray path() const;
36+ void setPath(const QByteArray &path);
37+
38+private:
39+ QByteArray m_id;
40+ QByteArray m_path;
41+};
42+
43+class GSettingsQml: public QObject
44+{
45+ Q_OBJECT
46+
47+ Q_PROPERTY(GSettingsSchemaQml* schema READ schema NOTIFY schemaChanged)
48+ Q_PROPERTY(QString pictureUri READ pictureUri WRITE setPictureUri NOTIFY pictureUriChanged)
49+
50+public:
51+ GSettingsQml(QObject *parent = NULL);
52+ ~GSettingsQml();
53+
54+ GSettingsSchemaQml * schema() const;
55+ QString pictureUri() const;
56+
57+ void setPictureUri(const QString &str);
58+
59+Q_SIGNALS:
60+ void schemaChanged();
61+ void pictureUriChanged(const QString&);
62+
63+private:
64+ GSettingsSchemaQml* m_schema;
65+
66+ QString m_pictureUri;
67+
68+ friend class GSettingsSchemaQml;
69+};
70+
71+class GSettingsControllerQml: public QObject
72+{
73+ Q_OBJECT
74+
75+public:
76+ static GSettingsControllerQml* instance();
77+ ~GSettingsControllerQml();
78+
79+ void registerSettingsObject(GSettingsQml *obj);
80+ void unRegisterSettingsObject(GSettingsQml *obj);
81+ Q_INVOKABLE void setPictureUri(const QString &str);
82+
83+private:
84+ GSettingsControllerQml();
85+
86+ static GSettingsControllerQml* s_controllerInstance;
87+ QList<GSettingsQml *> m_registeredGSettings;
88+};
89+
90+#endif // FAKE_GSETTINGS_H
tests/mocks/GSettings.1.0/plugin.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "plugin.h"
18+#include "fake_gsettings.h"
19+
20+#include <QtQml>
21+
22+static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
23+{
24+ return GSettingsControllerQml::instance();
25+}
26+
27+void FakeGSettingsQmlPlugin::registerTypes(const char *uri)
28+{
29+ qmlRegisterSingletonType<GSettingsControllerQml>(uri, 1, 0, "GSettingsController", controllerProvider);
30+ qmlRegisterType<GSettingsQml>(uri, 1, 0, "GSettings");
31+ qmlRegisterUncreatableType<GSettingsSchemaQml>(uri, 1, 0, "GSettingsSchema",
32+ "GSettingsSchema can only be used inside of a GSettings component");
33+}
tests/mocks/GSettings.1.0/plugin.hnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef PLUGIN_H
18+#define PLUGIN_H
19+
20+#include <QtQml/QQmlExtensionPlugin>
21+
22+class FakeGSettingsQmlPlugin : public QQmlExtensionPlugin
23+{
24+ Q_OBJECT
25+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26+
27+public:
28+ void registerTypes(const char *uri);
29+};
30+
31+#endif // PLUGIN_H
tests/mocks/GSettings.1.0/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module GSettings
2+plugin FakeGSettingsQml
tests/mocks/Powerd/Powerd.h+15−2
@@ -20,17 +20,30 @@
2020#define UNITY_MOCK_POWERD_H
2121
2222#include <QtCore/QObject>
23−#include <QtDBus/QDBusInterface>
2423
2524class Powerd: public QObject
2625{
2726 Q_OBJECT
27+ Q_ENUMS(Status)
28+ Q_FLAGS(DisplayFlag DisplayFlags)
2829
2930public:
31+ enum DisplayFlag {
32+ UseProximity = 1, // Use proximity sensor to override screen state
33+ DisableAutoBrightness = 2, // Force autobrightness to be disabled
34+ Bright = 4, // Request the screen to stay bright
35+ };
36+ Q_DECLARE_FLAGS(DisplayFlags, DisplayFlag)
37+
38+ enum Status {
39+ Off,
40+ On,
41+ };
42+
3043 explicit Powerd(QObject *parent = 0);
3144
3245Q_SIGNALS:
33− void powerStateChange(int state);
46+ void displayPowerStateChange(int status, unsigned int flags);
3447};
3548
3649#endif
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+2−2
@@ -31,8 +31,8 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
3131 m_pinned(false),
3232 m_running(false),
3333 m_recent(false),
34− m_progress(8),
35− m_count(8),
34+ m_progress(-1),
35+ m_count(0),
3636 m_quickList(new MockQuickListModel(this))
3737{
3838
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+32−4
@@ -23,22 +23,32 @@
2323MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
2424{
2525 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
26+ item->setProgress(0);
2627 m_list.append(item);
2728 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29+ item->setProgress(10);
2830 m_list.append(item);
2931 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
32+ item->setProgress(50);
3033 m_list.append(item);
3134 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
35+ item->setProgress(150);
3236 m_list.append(item);
3337 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
38+ item->setCount(1);
3439 m_list.append(item);
3540 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
41+ item->setCount(12);
3642 m_list.append(item);
3743 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
44+ item->setCount(123);
3845 m_list.append(item);
3946 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
47+ item->setCount(1234567890);
4048 m_list.append(item);
4149 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50+ item->setProgress(50);
51+ item->setCount(5);
4252 m_list.append(item);
4353 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
4454 m_list.append(item);
@@ -91,9 +101,29 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
91101
92102void MockLauncherModel::move(int oldIndex, int newIndex)
93103{
94− beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
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+
122+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
95123 m_list.move(oldIndex, newIndex);
96124 endMoveRows();
125+
126+ pin(m_list.at(newIndex)->appId());
97127}
98128
99129void MockLauncherModel::pin(const QString &appId, int index)
@@ -131,9 +161,7 @@ void MockLauncherModel::requestRemove(const QString &appId)
131161
132162void MockLauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
133163{
134− Q_UNUSED(appId)
135− Q_UNUSED(actionIndex)
136− // Nothing to mock yet...
164+ Q_EMIT quickListTriggered(appId, actionIndex);
137165}
138166
139167int MockLauncherModel::findApp(const QString &appId)
tests/mocks/Unity/Launcher/MockLauncherModel.h+3
@@ -44,6 +44,9 @@ public:
4444 Q_INVOKABLE void requestRemove(const QString &appId);
4545 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
4646
47+Q_SIGNALS:
48+ void quickListTriggered(const QString &appId, int index);
49+
4750private:
4851 int findApp(const QString &appId);
4952
tests/mocks/Unity/Launcher/MockQuickListModel.cpp+2
@@ -35,6 +35,8 @@ QVariant MockQuickListModel::data(const QModelIndex &index, int role) const
3535 return QLatin1String("test menu entry ") + QString::number(index.row());
3636 case RoleIcon:
3737 return QLatin1String("copy.png");
38+ case RoleClickable:
39+ return index.row() == 1 ? false : true;
3840 }
3941 return QVariant();
4042}
tests/mocks/Unity/Launcher/plugin.cpp+3
@@ -20,6 +20,7 @@
2020#include "plugin.h"
2121#include "MockLauncherModel.h"
2222#include "MockLauncherItem.h"
23+#include "MockQuickListModel.h"
2324
2425#include <unity/shell/launcher/LauncherModelInterface.h>
2526#include <unity/shell/launcher/LauncherItemInterface.h>
@@ -39,7 +40,9 @@ void TestLauncherPlugin::registerTypes(const char* uri)
3940 // @uri Unity.Launcher
4041 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
4142 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
43+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
4244
4345 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
4446 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");
4548}
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp+22−1
@@ -19,6 +19,7 @@
1919#include <QAbstractItemModel>
2020#include <QQmlEngine>
2121#include <QQuickView>
22+#include <QSignalSpy>
2223#include <QtTestGui>
2324#pragma GCC diagnostic push
2425#pragma GCC diagnostic ignored "-pedantic"
@@ -1521,7 +1522,7 @@ private Q_SLOTS:
15211522 QCOMPARE(lvwph->m_firstVisibleIndex, 1);
15221523 verifyItem(0, -458., 1000., false);
15231524 verifyItem(1, 542., 350., true);
1524− QCOMPARE(lvwph->m_minYExtent, 362.5);
1525+ QCOMPARE(lvwph->m_minYExtent, 525.);
15251526 QCOMPARE(lvwph->m_clipItem->y(), 658.);
15261527 QCOMPARE(lvwph->m_clipItem->clip(), false);
15271528 QCOMPARE(lvwph->m_headerItem->y(), 0.);
@@ -1634,6 +1635,26 @@ private Q_SLOTS:
16341635 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
16351636 }
16361637
1638+ void addingRemoveItemsShouldNotChangeContentY()
1639+ {
1640+ QSignalSpy spy(lvwph, SIGNAL(contentYChanged()));
1641+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 150));
1642+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 1), Q_ARG(QVariant, 6));
1643+
1644+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 1);
1645+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1646+ verifyItem(0, 50., 150., false);
1647+ QCOMPARE(lvwph->m_minYExtent, 0.);
1648+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
1649+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1650+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1651+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1652+ QCOMPARE(lvwph->contentY(), 0.);
1653+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1654+
1655+ QCOMPARE(spy.count(), 0);
1656+ }
1657+
16371658private:
16381659 QQuickView *view;
16391660 ListViewWithPageHeader *lvwph;
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsection.cpp+22−1
@@ -19,6 +19,7 @@
1919#include <QAbstractItemModel>
2020#include <QQmlEngine>
2121#include <QQuickView>
22+#include <QSignalSpy>
2223#include <QtTestGui>
2324#pragma GCC diagnostic push
2425#pragma GCC diagnostic ignored "-pedantic"
@@ -1875,7 +1876,7 @@ private Q_SLOTS:
18751876 QCOMPARE(lvwph->m_firstVisibleIndex, 1);
18761877 verifyItem(0, -498., 1040., false, "Regular", true);
18771878 verifyItem(1, 542., 390., true, "Mild", true);
1878− QCOMPARE(lvwph->m_minYExtent, 1250/3.);
1879+ QCOMPARE(lvwph->m_minYExtent, 525.);
18791880 QCOMPARE(lvwph->m_clipItem->y(), 738.);
18801881 QCOMPARE(lvwph->m_clipItem->clip(), false);
18811882 QCOMPARE(lvwph->m_headerItem->y(), 0.);
@@ -2055,6 +2056,26 @@ private Q_SLOTS:
20552056 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
20562057 }
20572058
2059+ void addingRemoveItemsShouldNotChangeContentY()
2060+ {
2061+ QSignalSpy spy(lvwph, SIGNAL(contentYChanged()));
2062+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 150), Q_ARG(QVariant, "Agressive"));
2063+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 1), Q_ARG(QVariant, 6));
2064+
2065+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 1);
2066+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
2067+ verifyItem(0, 50., 190., false, "Agressive", false);
2068+ QCOMPARE(lvwph->m_minYExtent, 0.);
2069+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
2070+ QCOMPARE(lvwph->m_clipItem->clip(), false);
2071+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
2072+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
2073+ QCOMPARE(lvwph->contentY(), 0.);
2074+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
2075+
2076+ QCOMPARE(spy.count(), 0);
2077+ }
2078+
20582079private:
20592080 QQuickView *view;
20602081 ListViewWithPageHeader *lvwph;
tests/plugins/Utils/qlimitproxymodeltest.cpp+133−8
@@ -58,10 +58,17 @@ public:
5858 m_roles = roles;
5959 }
6060
61+ void clear()
62+ {
63+ beginRemoveRows(QModelIndex(), 0, m_list.count() - 1);
64+ m_list.clear();
65+ endRemoveRows();
66+ }
67+
6168 bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
6269 beginInsertRows(parent, row, row+count-1);
6370 for (int i=0; i<count; i++) {
64− m_list.insert(i+row, "test"+i);
71+ m_list.insert(i+row, QString::number(i));
6572 }
6673 endInsertRows();
6774 return true;
@@ -88,6 +95,60 @@ private:
8895 QHash<int, QByteArray> m_roles;
8996};
9097
98+class MirrorModel : public QObject
99+{
100+ Q_OBJECT
101+
102+public:
103+ MirrorModel(QAbstractItemModel *model) : m_mirror(model)
104+ {
105+ connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)));
106+ connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)));
107+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
108+ }
109+
110+ void check()
111+ {
112+ QCOMPARE(m_list.count(), m_mirror->rowCount());
113+ for (int i = 0; i < m_list.count(); ++i) {
114+ QCOMPARE(m_list[i], m_mirror->data(m_mirror->index(i, 0)).toString());
115+ }
116+ }
117+
118+private Q_SLOTS:
119+ void rowsInserted(const QModelIndex &parent, int start, int end)
120+ {
121+ QVERIFY(!parent.isValid());
122+ for (int i = start; i <= end; ++i) {
123+ m_list.insert(i, m_mirror->data(m_mirror->index(i, 0)).toString());
124+ }
125+ }
126+
127+ void rowsRemoved(const QModelIndex &parent, int start, int end)
128+ {
129+ QVERIFY(!parent.isValid());
130+ for (int i = end; i >= start; --i) {
131+ m_list.removeAt(i);
132+ }
133+ }
134+
135+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
136+ {
137+ QVERIFY(topLeft.isValid());
138+ QVERIFY(bottomRight.isValid());
139+ QVERIFY(!topLeft.parent().isValid());
140+ QVERIFY(!bottomRight.parent().isValid());
141+ for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
142+ m_list[i] = m_mirror->data(m_mirror->index(i, 0)).toString();
143+ }
144+ }
145+
146+
147+private:
148+ QStringList m_list;
149+ QAbstractItemModel *m_mirror;
150+};
151+
91152class QLimitProxyModelTest : public QObject
92153{
93154 Q_OBJECT
@@ -401,15 +462,10 @@ private Q_SLOTS:
401462 QCOMPARE(spyOnCountChanged.count(), 0);
402463
403464 model.removeRows(2, 2);
404− QCOMPARE(spyOnRowsRemoved.count(), 1);
465+ QCOMPARE(spyOnRowsRemoved.count(), 0);
405466 QCOMPARE(spyOnRowsInserted.count(), 0);
406− QCOMPARE(spyOnCountChanged.count(), 1);
407− arguments = spyOnRowsRemoved.takeFirst();
408− QCOMPARE(arguments.at(1).toInt(), 2);
409− QCOMPARE(arguments.at(2).toInt(), 3);
467+ QCOMPARE(spyOnCountChanged.count(), 0);
410468 QCOMPARE(proxy.rowCount(), 7);
411− spyOnRowsInserted.clear();
412− spyOnCountChanged.clear();
413469
414470 model.removeRows(0, 7);
415471 QCOMPARE(spyOnRowsRemoved.count(), 1);
@@ -479,6 +535,75 @@ private Q_SLOTS:
479535 QCOMPARE(proxy.rowCount(), 5);
480536 QCOMPARE(spyOnCountChanged.count(), 1);
481537 }
538+
539+ void testMirrorModel() {
540+ QLimitProxyModelQML proxy;
541+ MockListModel model;
542+ MirrorModel mirror(&proxy);
543+
544+ proxy.setModel(&model);
545+ proxy.setLimit(5);
546+
547+ // Checks what happens when the model already has
548+ // more items than the limit and we add stuff to
549+ // the front
550+ model.insertRows(0, 7);
551+ mirror.check();
552+ model.insertRows(1, 3);
553+ mirror.check();
554+ model.clear();
555+ mirror.check();
556+
557+ // Checks what happens when the model does
558+ // not has more items than limit but adding
559+ // stuff to its front makes it go past the limit
560+ model.insertRows(0, 3);
561+ mirror.check();
562+ model.insertRows(1, 3);
563+ mirror.check();
564+ model.clear();
565+ mirror.check();
566+
567+ // Checks what happens when the model does
568+ // not has more items than limit and adding
569+ // stuff to its front makes it not go past the limit
570+ model.insertRows(0, 1);
571+ mirror.check();
572+ model.insertRows(1, 3);
573+ mirror.check();
574+ model.clear();
575+ mirror.check();
576+
577+ // Checks what happens when the model already has
578+ // more items than the limit and we remove stuff from
579+ // the front but it still has more than the limit
580+ model.insertRows(0, 10);
581+ mirror.check();
582+ model.removeRows(1, 3);
583+ mirror.check();
584+ model.clear();
585+ mirror.check();
586+
587+ // Checks what happens when the model already has
588+ // more items than the limit and we remove stuff from
589+ // the front but it has less than the limit
590+ model.insertRows(0, 6);
591+ mirror.check();
592+ model.removeRows(1, 3);
593+ mirror.check();
594+ model.clear();
595+ mirror.check();
596+
597+ // Checks what happens when the model has
598+ // less items than the limit and we remove stuff from
599+ // the front
600+ model.insertRows(0, 4);
601+ mirror.check();
602+ model.removeRows(1, 3);
603+ mirror.check();
604+ model.clear();
605+ mirror.check();
606+ }
482607};
483608
484609QTEST_GUILESS_MAIN(QLimitProxyModelTest)
tests/qmltests/CMakeLists.txt+1−1
@@ -26,7 +26,7 @@ set(qmltest_DEFAULT_TARGETS qmluitests)
2626set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
2727set(qmltest_DEFAULT_PROPERTIES "")
2828
29−add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
29+add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
3030 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
3131add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
3232add_qml_test(Components DraggingArea)
tests/qmltests/Dash/Apps/tst_AppPreview.qml+17−3
@@ -35,7 +35,7 @@ Item {
3535
3636 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.";
3737 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 }
3939 QtObject { id: rated; property int value: 120 }
4040 QtObject { id: reviews; property int value: 8 }
4141 QtObject { id: progress; property string value: "source" }
@@ -51,12 +51,12 @@ Item {
5151 property string title: "Unity App"
5252 property string appIcon: "fake_image.png"
5353 property string description: "This is an Application description"
54+ property real rating: rating.value
55+ property int numRatings: reviews.value
5456 property var execute: root.fake_call
5557 property var infoMap: {
5658 "more-screenshots": screenshots,
57− "rating": rating,
5859 "rated": rated,
59− "reviews": reviews,
6060 "comments": comments
6161 }
6262 property var actions: [
@@ -88,6 +88,7 @@ Item {
8888 var reviewField = findChild(appPreview, "reviewField");
8989 reviewField.focus = false;
9090 reviewField.text = "";
91+ data.rating = rating.value;
9192 }
9293
9394 function test_actions() {
@@ -148,5 +149,18 @@ Item {
148149 compare(comment, comments.value[i][3], "Comment don't match");
149150 }
150151 }
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+
151165 }
152166}
tests/qmltests/Greeter/tst_Clock.qml+1−1
@@ -70,7 +70,7 @@ Rectangle {
7070 compare(clock.__timerRunning, true, "Timer should be running")
7171 wait(0) // spin event loop to trigger the timer
7272 verify(dateLabel.text !== dateString)
73− if (timeLabel.text == "11:13") wait(60000) // next test will fail at 11:13, wait 1 minute
73+ if (timeLabel.text.indexOf("11:13") != -1) wait(60000) // next test will fail at 11:13, wait 1 minute
7474 verify(timeLabel.text !== timeString)
7575 }
7676
tests/qmltests/Launcher/tst_Launcher.qml+175
@@ -19,6 +19,7 @@ import QtTest 1.0
1919import Unity.Test 0.1 as UT
2020import ".."
2121import "../../../Launcher"
22+import Unity.Launcher 0.1
2223
2324/* Nothing is shown at first. If you drag from left edge you will bring up the
2425 launcher. */
@@ -48,6 +49,11 @@ Item {
4849 property int maxPanelX: 0
4950 }
5051
52+ SignalSpy {
53+ id: signalSpy
54+ target: LauncherModel
55+ }
56+
5157 Connections {
5258 target: testCase.findChild(launcher, "launcherPanel")
5359
@@ -176,6 +182,22 @@ Item {
176182 revealer.waitUntilLauncherDisappears();
177183 launcher.available = true;
178184 }
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+ }
179201 }
180202
181203 UT.UnityTestCase {
@@ -247,4 +269,157 @@ Item {
247269 }
248270 */
249271 }
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+ }
250425}
tests/qmltests/tst_Shell.qml+24−3
@@ -19,6 +19,7 @@
1919
2020import QtQuick 2.0
2121import QtTest 1.0
22+import GSettings 1.0
2223import Ubuntu.Application 0.1
2324import Unity.Test 0.1 as UT
2425import Powerd 0.1
@@ -124,13 +125,17 @@ Item {
124125 var mainApp = ApplicationManager.mainStageFocusedApplication;
125126 tryCompareFunction(function() { return mainApp != null; }, true);
126127
127− // Now suspend
128− Powerd.powerStateChange(0);
128+ // Try to suspend while proximity is engaged...
129+ Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
130+ tryCompare(greeter, "showProgress", 0);
131+
132+ // Now really suspend
133+ Powerd.displayPowerStateChange(Powerd.Off, 0);
129134 tryCompare(greeter, "showProgress", 1);
130135 tryCompare(ApplicationManager, "mainStageFocusedApplication", null);
131136
132137 // And wake up
133− Powerd.powerStateChange(1);
138+ Powerd.displayPowerStateChange(Powerd.On, 0);
134139 tryCompare(ApplicationManager, "mainStageFocusedApplication", mainApp);
135140 tryCompare(greeter, "showProgress", 1);
136141 }
@@ -315,5 +320,21 @@ Item {
315320 }
316321 } while (!isStill);
317322 }
323+
324+ function test_wallpaper_data() {
325+ return [
326+ {tag: "red", url: "tests/data/unity/backgrounds/red.png", expectedUrl: "tests/data/unity/backgrounds/red.png"},
327+ {tag: "blue", url: "tests/data/unity/backgrounds/blue.png", expectedUrl: "tests/data/unity/backgrounds/blue.png"},
328+ {tag: "invalid", url: "invalid", expectedUrl: shell.defaultBackground},
329+ {tag: "empty", url: "", expectedUrl: shell.defaultBackground}
330+ ]
331+ }
332+
333+ function test_wallpaper(data) {
334+ var backgroundImage = findChild(shell, "backgroundImage")
335+ GSettingsController.setPictureUri(data.url)
336+ tryCompareFunction(function() { return backgroundImage.source.toString().indexOf(data.expectedUrl) !== -1; }, true)
337+ tryCompare(backgroundImage, "status", Image.Ready)
338+ }
318339 }
319340}