on xenial help sign in
Merge from trunk
00ec395Michael Terry
Bottombar/Bottombar.qml+60−6
@@ -18,6 +18,7 @@ import QtQuick 2.0
18import "../Components"
18import "../Components"
19import "../Components/Math.js" as MathLocal
19import "../Components/Math.js" as MathLocal
20import Unity 0.1
20import Unity 0.1
21+import Ubuntu.Application 0.1
21import Ubuntu.Gestures 0.1
22import Ubuntu.Gestures 0.1
22
23
23Item {
24Item {
@@ -28,15 +29,46 @@ Item {
28
29
29 property variant theHud
30 property variant theHud
30 property bool enabled: false
31 property bool enabled: false
32+ property bool preventHiding: dragArea.dragging
31 readonly property real bottomEdgeButtonCenterDistance: units.gu(34)
33 readonly property real bottomEdgeButtonCenterDistance: units.gu(34)
32
34
33− state: dragArea.status === DirectionalDragArea.Recognized ? "shown" : "hidden"
35+ state: "hidden"
36+
37+ onStateChanged: {
38+ if (state == "hidden") {
39+ dismissTimer.stop()
40+ } else {
41+ dismissTimer.restart()
42+ }
43+ }
44+
45+ onPreventHidingChanged: {
46+ if (dismissTimer.running) {
47+ dismissTimer.restart();
48+ }
49+ }
50+
51+ Timer {
52+ id: dismissTimer
53+ interval: 5000
54+ onTriggered: {
55+ if (!bottombar.preventHiding) {
56+ bottombar.state = "hidden"
57+ } else {
58+ dismissTimer.restart()
59+ }
60+ }
61+ }
34
62
35 HudButton {
63 HudButton {
36 id: hudButton
64 id: hudButton
37
65
66+ readonly property bool centeredHud: parent.width < units.gu(68) // Nexus 7 has 67 gu width
67+
38− x: MathLocal.clamp(dragArea.touchStartX - (width / 2), 0, bottombar.width - width)
68+ x: centeredHud ? parent.width / 2 - width / 2 : MathLocal.clamp(dragArea.touchStartX - (width / 2), 0, bottombar.width - width)
39 y: bottombar.height - bottomEdgeButtonCenterDistance - (height / 2) - bottomMargin
69 y: bottombar.height - bottomEdgeButtonCenterDistance - (height / 2) - bottomMargin
70+ z: 1
71+ visible: opacity != 0
40
72
41 mouseOver: {
73 mouseOver: {
42 if (dragArea.status === DirectionalDragArea.Recognized) {
74 if (dragArea.status === DirectionalDragArea.Recognized) {
@@ -55,13 +87,18 @@ Item {
55 }
87 }
56
88
57 Behavior on opacity {
89 Behavior on opacity {
58− NumberAnimation{ duration: 200; easing.type: Easing.OutCubic}
90+ NumberAnimation{duration: 200; easing.type: Easing.OutCubic}
59 }
91 }
60 }
92 }
61
93
62 Connections {
94 Connections {
63 target: theHud
95 target: theHud
96+ onShownChanged: {
64− onShownChanged: bottomBarVisibilityCommunicatorShell.forceHidden = theHud.shown
97+ bottomBarVisibilityCommunicatorShell.forceHidden = theHud.shown
98+ if (theHud.shown) {
99+ bottombar.state = "hidden"
100+ }
101+ }
65 }
102 }
66
103
67 EdgeDragArea {
104 EdgeDragArea {
@@ -70,6 +107,7 @@ Item {
70 height: distanceThreshold
107 height: distanceThreshold
71 anchors.bottom: parent.bottom
108 anchors.bottom: parent.bottom
72
109
110+ distanceThreshold: units.gu(8)
73 enabled: !theHud.shown && bottombar.enabled && applicationIsOnForeground
111 enabled: !theHud.shown && bottombar.enabled && applicationIsOnForeground
74 direction: Direction.Upwards
112 direction: Direction.Upwards
75
113
@@ -84,12 +122,29 @@ Item {
84 }
122 }
85 }
123 }
86 } else if (status === DirectionalDragArea.Undecided) {
124 } else if (status === DirectionalDragArea.Undecided) {
125+ if (!hudButton.centeredHud) {
87− touchStartX = touchX
126+ touchStartX = touchX
127+ }
128+ } else if (status === DirectionalDragArea.Recognized) {
129+ bottombar.state = "shown"
88 }
130 }
89 previousStatus = status
131 previousStatus = status
90 }
132 }
91 }
133 }
92
134
135+ MouseArea {
136+ anchors.fill: parent
137+ enabled: bottombar.state == "shown"
138+ onPressed: {
139+ bottombar.state = "hidden"
140+ }
141+ }
142+
143+ InputFilterArea {
144+ anchors.fill: parent
145+ blockInput: hudButton.visible
146+ }
147+
93 states: [
148 states: [
94 State {
149 State {
95 name: "hidden"
150 name: "hidden"
@@ -102,5 +157,4 @@ Item {
102 PropertyChanges { target: hudButton; bottomMargin: units.gu(0)}
157 PropertyChanges { target: hudButton; bottomMargin: units.gu(0)}
103 }
158 }
104 ]
159 ]
105−
106}
160}
Bottombar/HudButton.qml+8−8
@@ -33,24 +33,24 @@ Item {
33
33
34 Item {
34 Item {
35 id: hudButton
35 id: hudButton
36− property alias actionButton: actionButton
37
36
38 anchors.centerIn: parent
37 anchors.centerIn: parent
39 height: units.gu(12)
38 height: units.gu(12)
40 width: height
39 width: height
41− opacity: item.mouseOver ? 1 : 0.7
40+ opacity: item.mouseOver || abstractButton.pressed ? 1 : 0.7
42− scale: item.mouseOver ? scaleOnMouseOver : 1
41+ scale: item.mouseOver || abstractButton.pressed ? scaleOnMouseOver : 1
43 Behavior on opacity {NumberAnimation{duration: 200; easing.type: Easing.OutQuart}}
42 Behavior on opacity {NumberAnimation{duration: 200; easing.type: Easing.OutQuart}}
44 Behavior on scale {NumberAnimation{duration: 200; easing.type: Easing.OutQuart}}
43 Behavior on scale {NumberAnimation{duration: 200; easing.type: Easing.OutQuart}}
45
44
46− UbuntuShape {
45+ AbstractButton {
47− id: actionButton
46+ id: abstractButton
48 anchors.fill: parent
47 anchors.fill: parent
49−
50− borderSource: ""
51− image: Image {
48+ style: Image {
49+ anchors.fill: parent
52 source: "graphics/hud_invoke_button_active.png"
50 source: "graphics/hud_invoke_button_active.png"
53 }
51 }
52+
53+ onClicked: item.clicked()
54 }
54 }
55
55
56 Image {
56 Image {
Components/CrossFadeImage.qml+3−1
@@ -85,7 +85,7 @@ Item {
85 Connections {
85 Connections {
86 target: __nextImage
86 target: __nextImage
87 onStatusChanged: {
87 onStatusChanged: {
88− if (__nextImage.status == Image.Ready) {
88+ if (__nextImage.status == Image.Ready || __nextImage.status == Image.Error) {
89 swapImages();
89 swapImages();
90 }
90 }
91 }
91 }
@@ -96,6 +96,7 @@ Item {
96 anchors.fill: parent
96 anchors.fill: parent
97 cache: false
97 cache: false
98 asynchronous: true
98 asynchronous: true
99+ fillMode: Image.PreserveAspectCrop
99 z: 1
100 z: 1
100 }
101 }
101
102
@@ -104,6 +105,7 @@ Item {
104 anchors.fill: parent
105 anchors.fill: parent
105 cache: false
106 cache: false
106 asynchronous: true
107 asynchronous: true
108+ fillMode: Image.PreserveAspectCrop
107 z: 0
109 z: 0
108 }
110 }
109
111
Components/FilterGrid.qml+31−61
@@ -35,12 +35,18 @@ Item {
35
35
36 /* Whether, when collapsed, a button should be displayed enabling the user to expand
36 /* Whether, when collapsed, a button should be displayed enabling the user to expand
37 the grid to its full size. */
37 the grid to its full size. */
38− property bool expandable: true
38+ readonly property bool expandable: model.count > collapsedRowCount * iconTileGrid.columns
39
39
40 property var model: null
40 property var model: null
41
41
42 /* Maximum number of rows to be show when filter=true. */
42 /* Maximum number of rows to be show when filter=true. */
43 property int collapsedRowCount: 2
43 property int collapsedRowCount: 2
44+ readonly property int collapsedHeight: {
45+ return iconTileGrid.contentHeightForRows(Math.min(collapsedRowCount, Math.ceil(model.count / columns)))
46+ }
47+ readonly property int uncollapsedHeight: {
48+ return iconTileGrid.contentHeightForRows(Math.ceil(model.count / columns))
49+ }
44
50
45 property alias minimumHorizontalSpacing: iconTileGrid.minimumHorizontalSpacing
51 property alias minimumHorizontalSpacing: iconTileGrid.minimumHorizontalSpacing
46 property alias maximumNumberOfColumns: iconTileGrid.maximumNumberOfColumns
52 property alias maximumNumberOfColumns: iconTileGrid.maximumNumberOfColumns
@@ -55,7 +61,29 @@ Item {
55 readonly property alias moving: iconTileGrid.moving
61 readonly property alias moving: iconTileGrid.moving
56 readonly property alias pressDelay: iconTileGrid.pressDelay
62 readonly property alias pressDelay: iconTileGrid.pressDelay
57
63
58− height: childrenRect.height
64+ height: !filterAnimation.running ? childrenRect.height : height
65+ clip: filterAnimation.running
66+
67+ NumberAnimation {
68+ property bool filterEndValue
69+ id: filterAnimation
70+ target: root
71+ property: "height"
72+ to: filterEndValue ? root.collapsedHeight : root.uncollapsedHeight
73+ // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation
74+ // otherwise since both animations can run at the same time you'll get
75+ // some visual weirdness.
76+ duration: 200
77+ easing.type: Easing.InOutQuad
78+ onStopped: {
79+ root.filter = filterEndValue;
80+ }
81+ }
82+
83+ function startFilterAnimation(filter) {
84+ filterAnimation.filterEndValue = filter
85+ filterAnimation.start();
86+ }
59
87
60 ResponsiveGridView {
88 ResponsiveGridView {
61 id: iconTileGrid
89 id: iconTileGrid
@@ -72,66 +100,8 @@ Item {
72
100
73 model: LimitProxyModel {
101 model: LimitProxyModel {
74 model: root.model
102 model: root.model
75− limit: (filter) ? collapsedRowCount * iconTileGrid.columns : -1
103+ limit: (filter && !filterAnimation.running) ? collapsedRowCount * iconTileGrid.columns : -1
76 }
104 }
77
105
78 }
106 }
79−
80− Item {
81− anchors {
82− left: parent.left
83− right: parent.right
84− top: iconTileGrid.bottom
85− }
86− visible: (expandable && filter && model.count > collapsedRowCount * iconTileGrid.columns)
87− height: (visible) ? childrenRect.height + units.gu(2) : 0
88−
89− AbstractButton {
90− id: button
91− objectName: "filterToggleButton"
92−
93− anchors {
94− top: parent.top
95− horizontalCenter: parent.horizontalCenter
96− }
97− width: units.gu(22)
98− height: units.gu(4)
99−
100− UbuntuShape {
101− anchors.fill: parent
102− color: "#33ffffff" // FIXME no palette
103− radius: "small"
104− }
105−
106− UbuntuShape {
107− id: borderPressed
108−
109− anchors.fill: parent
110− radius: "small"
111− borderSource: "radius_pressed.sci"
112− opacity: button.pressed ? 1.0 : 0.0
113− Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
114− }
115−
116− Label {
117− anchors {
118− verticalCenter: parent.verticalCenter
119− left: parent.left
120− right: parent.right
121− leftMargin: units.gu(1)
122− rightMargin: units.gu(1)
123− }
124− text: (filter) ? "+ View all (" + model.count + ")" : "- Show fewer"
125− fontSize: "small"
126− color: Theme.palette.selected.backgroundText
127− opacity: 0.6
128− style: Text.Raised
129− styleColor: "black"
130− elide: Text.ElideMiddle
131− horizontalAlignment: Text.AlignHCenter
132− }
133−
134− onClicked: filter = !filter
135− }
136− }
137}
107}
Components/ListItems/Header.qml+19−2
@@ -24,6 +24,9 @@ Item {
24 \qmlproperty string text
24 \qmlproperty string text
25 */
25 */
26 property alias text: label.text
26 property alias text: label.text
27+ property alias image: image.source
28+
29+ signal clicked(variant mouse)
27
30
28 height: units.gu(4.5)
31 height: units.gu(4.5)
29
32
@@ -31,6 +34,7 @@ Item {
31 anchors {
34 anchors {
32 left: parent.left
35 left: parent.left
33 right: parent.right
36 right: parent.right
37+ rightMargin: units.gu(0.5)
34 top: parent.top
38 top: parent.top
35 bottom: bottomBorder.top
39 bottom: bottomBorder.top
36 }
40 }
@@ -40,8 +44,6 @@ Item {
40 anchors {
44 anchors {
41 left: parent.left
45 left: parent.left
42 leftMargin: units.gu(2)
46 leftMargin: units.gu(2)
43− right: parent.right
44− rightMargin: units.gu(0.5)
45 verticalCenter: parent.verticalCenter
47 verticalCenter: parent.verticalCenter
46 }
48 }
47
49
@@ -53,6 +55,16 @@ Item {
53 textFormat: Text.PlainText
55 textFormat: Text.PlainText
54 style: Text.Raised
56 style: Text.Raised
55 styleColor: "black"
57 styleColor: "black"
58+ width: parent.width - image.width - image.leftMargin - anchors.leftMargin
59+ }
60+
61+ Image {
62+ id: image
63+ readonly property double leftMargin: units.gu(1)
64+ x: label.x + label.contentWidth + leftMargin
65+ anchors {
66+ verticalCenter: parent.verticalCenter
67+ }
56 }
68 }
57 }
69 }
58
70
@@ -64,4 +76,9 @@ Item {
64 bottom: parent.bottom
76 bottom: parent.bottom
65 }
77 }
66 }
78 }
79+
80+ MouseArea {
81+ anchors.fill: parent
82+ onClicked: parent.clicked(mouse)
83+ }
67}
84}
Components/ResponsiveGridView.qml+7−1
@@ -32,12 +32,18 @@ Item {
32 property alias delegate: gridView.delegate
32 property alias delegate: gridView.delegate
33 readonly property int cellWidth: gridView.cellWidth
33 readonly property int cellWidth: gridView.cellWidth
34 readonly property int cellHeight: gridView.cellHeight
34 readonly property int cellHeight: gridView.cellHeight
35− readonly property int totalContentHeight: Math.ceil(gridView.model.count / columns) * cellHeight + verticalSpacing
35+ readonly property int totalContentHeight: {
36+ return contentHeightForRows(Math.ceil(gridView.model.count / columns))
37+ }
36 property alias interactive: gridView.interactive
38 property alias interactive: gridView.interactive
37 readonly property alias flicking: gridView.flicking
39 readonly property alias flicking: gridView.flicking
38 readonly property alias moving: gridView.moving
40 readonly property alias moving: gridView.moving
39 readonly property alias pressDelay: gridView.pressDelay
41 readonly property alias pressDelay: gridView.pressDelay
40
42
43+ function contentHeightForRows(rows) {
44+ return rows * cellHeight + verticalSpacing
45+ }
46+
41 GridView {
47 GridView {
42 id: gridView
48 id: gridView
43 objectName: "responsiveGridViewGrid"
49 objectName: "responsiveGridViewGrid"
Components/Tile.qml−6
@@ -84,10 +84,4 @@ AbstractButton {
84 horizontalAlignment: Text.AlignHCenter
84 horizontalAlignment: Text.AlignHCenter
85 }
85 }
86 }
86 }
87−
88− GridView.onRemove: SequentialAnimation {
89− PropertyAction { target: root; property: "GridView.delayRemove"; value: true }
90− NumberAnimation { target: root; property: "opacity"; from: 1; to: 0; duration: 200; easing.type: Easing.InOutQuad }
91− PropertyAction { target: root; property: "GridView.delayRemove"; value: false }
92− }
93}
87}
Dash/Apps/AppInfo.qml+1
@@ -53,6 +53,7 @@ Row {
53 }
53 }
54
54
55 Row {
55 Row {
56+ visible: root.rating >= 0
56 spacing: units.gu(1)
57 spacing: units.gu(1)
57
58
58 RatingStars {
59 RatingStars {
Dash/Apps/AppPreview.qml+37−32
@@ -38,7 +38,7 @@ DashPreview {
38 right: parent.right
38 right: parent.right
39 margins: units.gu(1)
39 margins: units.gu(1)
40 }
40 }
41− model: previewData.infoMap["more-screenshots"].value
41+ model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
42
42
43 delegate: UbuntuShape {
43 delegate: UbuntuShape {
44 id: shape
44 id: shape
@@ -138,9 +138,9 @@ DashPreview {
138
138
139 appName: root.previewData.title
139 appName: root.previewData.title
140 icon: IconUtil.from_gicon(root.previewData.appIcon)
140 icon: IconUtil.from_gicon(root.previewData.appIcon)
141− rating: root.previewData.infoMap["rating"] ? root.previewData.infoMap["rating"].value : 0
141+ rating: Math.round(root.previewData.rating * 10)
142+ reviews: root.previewData.numRatings
142 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
143 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
143− reviews: root.previewData.infoMap["reviews"] ? root.previewData.infoMap["reviews"].value : 0
144 }
144 }
145
145
146 Label {
146 Label {
@@ -154,42 +154,47 @@ DashPreview {
154 styleColor: "black"
154 styleColor: "black"
155 }
155 }
156
156
157− ListItem.ThinDivider {}
157+ Column {
158−
158+ visible: root.previewData.rating >= 0
159− Item {
160 anchors { left: parent.left; right: parent.right }
159 anchors { left: parent.left; right: parent.right }
160+ spacing: parent.spacing
161+
162+ ListItem.ThinDivider { }
163+
164+ Item {
165+ anchors { left: parent.left; right: parent.right }
161− height: rateLabel.height
166+ height: rateLabel.height
162−
167+
163− Label {
168+ Label {
164− id: rateLabel
169+ id: rateLabel
165− fontSize: "medium"
170+ fontSize: "medium"
166− color: "white"
171+ color: "white"
167− style: Text.Raised
172+ style: Text.Raised
168− styleColor: "black"
173+ styleColor: "black"
169− opacity: .9
174+ opacity: .9
170− text: i18n.tr("Rate this")
175+ text: i18n.tr("Rate this")
171−
176+
172− anchors.left: parent.left
177+ anchors.left: parent.left
173− anchors.verticalCenter: parent.verticalCenter
178+ anchors.verticalCenter: parent.verticalCenter
174− }
179+ }
175
180
176− // FIXME these need to be made interactive and connected to the scope
181+ // FIXME these need to be made interactive and connected to the scope
177− RatingStars {
182+ RatingStars {
178− anchors.right: parent.right
183+ anchors.right: parent.right
179− anchors.verticalCenter: parent.verticalCenter
184+ anchors.verticalCenter: parent.verticalCenter
185+ }
180 }
186 }
181− }
182
187
183− ListItem.ThinDivider {}
188+ ListItem.ThinDivider { }
184
189
185− AppReviews {
190+ AppReviews {
186− objectName: "appReviews"
191+ objectName: "appReviews"
187− anchors { left: parent.left; right: parent.right }
192+ anchors { left: parent.left; right: parent.right }
188
193
189− model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
194+ model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
190
195
191− onSendReview: root.sendUserReview(review);
196+ onSendReview: root.sendUserReview(review);
197+ }
192 }
198 }
193−
194 }
199 }
195}
200}
Dash/Apps/ApplicationsFilterGrid.qml−1
@@ -23,7 +23,6 @@ FilterGrid {
23 id: filterGrid
23 id: filterGrid
24
24
25 filter: false
25 filter: false
26− expandable: false
27 minimumHorizontalSpacing: units.gu(0.5)
26 minimumHorizontalSpacing: units.gu(0.5)
28 maximumNumberOfColumns: 6
27 maximumNumberOfColumns: 6
29 delegateWidth: units.gu(11)
28 delegateWidth: units.gu(11)
Dash/Apps/AppsAvailableForDownloadModel.qml+61−6
@@ -15,12 +15,67 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import "../../Applications"
18
19
19ListModel {
20ListModel {
21+ ListElement {
22+ uri: "application://evernote.desktop"
23+ icon: "../../graphics/applicationIcons/evernote.png"
24+ category: 0
25+ mimetype: "application/x-desktop"
26+ title: "Evernote"
27+ comment: ""
20− ListElement { desktopFile: "/usr/share/applications/evernote.desktop"; }
28+ dndUri: "file:///usr/share/applications/evernote.desktop"
29+ metadata: ""
30+ }
31+ ListElement {
32+ uri: "application://map.desktop"
33+ icon: "../../graphics/applicationIcons/map.png"
34+ category: 0
35+ mimetype: "application/x-desktop"
36+ title: "Map"
37+ comment: ""
21− ListElement { desktopFile: "/usr/share/applications/map.desktop"; }
38+ dndUri: "file:///usr/share/applications/map.desktop"
39+ metadata: ""
40+ }
41+ ListElement {
42+ uri: "application://pinterest.desktop"
43+ icon: "../../graphics/applicationIcons/pinterest.png"
44+ category: 0
45+ mimetype: "application/x-desktop"
46+ title: "Pinterest"
47+ comment: ""
22− ListElement { desktopFile: "/usr/share/applications/pinterest.desktop"; }
48+ dndUri: "file:///usr/share/applications/pinterest.desktop"
49+ metadata: ""
50+ }
51+ ListElement {
52+ uri: "application://soundcloud.desktop"
53+ icon: "../../graphics/applicationIcons/soundcloud.png"
54+ category: 0
55+ mimetype: "application/x-desktop"
56+ title: "Soundcloud"
57+ comment: ""
23− ListElement { desktopFile: "/usr/share/applications/soundcloud.desktop"; }
58+ dndUri: "file:///usr/share/applications/soundcloud.desktop"
59+ metadata: ""
60+ }
61+ ListElement {
62+ uri: "application://wikipedia.desktop"
63+ icon: "../../graphics/applicationIcons/wikipedia.png"
64+ category: 0
65+ mimetype: "application/x-desktop"
66+ title: "Wikipedia"
67+ comment: ""
24− ListElement { desktopFile: "/usr/share/applications/wikipedia.desktop"; }
68+ dndUri: "file:///usr/share/applications/wikipedia.desktop"
69+ metadata: ""
70+ }
71+ ListElement {
72+ uri: "application://youtube.desktop"
73+ icon: "../../graphics/applicationIcons/youtube.png"
74+ category: 0
75+ mimetype: "application/x-desktop"
76+ title: "Youtube"
77+ comment: ""
25− ListElement { desktopFile: "/usr/share/applications/youtube.desktop"; }
78+ dndUri: "file:///usr/share/applications/youtube.desktop"
79+ metadata: ""
80+ }
26}
81}
Dash/Apps/FrequentlyUsedAppsModel.qml+60−6
@@ -17,10 +17,64 @@
17import QtQuick 2.0
17import QtQuick 2.0
18
18
19ListModel {
19ListModel {
20+ ListElement {
21+ uri: "application:///usr/share/applications/camera-app.desktop"
22+ icon: "../../graphics/applicationIcons/camera.png"
23+ category: 0
24+ mimetype: "application/x-desktop"
25+ title: "Camera"
26+ comment: ""
27+ dndUri: "file:///usr/share/applications/camera-app.desktop"
28+ metadata: ""
29+ }
30+ ListElement {
31+ uri: "application:///usr/share/applications/phone-app.desktop"
32+ icon: "../../graphics/applicationIcons/phone-app.png"
33+ category: 0
34+ mimetype: "application/x-desktop"
35+ title: "Phone"
36+ comment: ""
37+ dndUri: "file:///usr/share/applications/phone-app.desktop"
38+ metadata: ""
39+ }
40+ ListElement {
41+ uri: "application:///usr/share/applications/gallery-app.desktop"
42+ icon: "../../graphics/applicationIcons/gallery.png"
43+ category: 0
44+ mimetype: "application/x-desktop"
45+ title: "Gallery"
46+ comment: ""
47+ dndUri: "file:///usr/share/applications/gallery.desktop"
48+ metadata: ""
49+ }
50+ ListElement {
20− ListElement { desktopFile: "/usr/share/applications/camera-app.desktop"; }
51+ uri: "application:///usr/share/applications/facebook-webapp.desktop"
52+ icon: "../../graphics/applicationIcons/facebook.png"
53+ category: 0
54+ mimetype: "application/x-desktop"
55+ title: "Facebook"
56+ comment: ""
21− ListElement { desktopFile: "/usr/share/applications/phone-app.desktop"; }
57+ dndUri: "file:///usr/share/applications/facebook-webapp.desktop"
58+ metadata: ""
59+ }
60+ ListElement {
22− ListElement { desktopFile: "/usr/share/applications/gallery-app.desktop"; }
61+ uri: "application:///usr/share/applications/webbrowser-app.desktop"
62+ icon: "../../graphics/applicationIcons/browser.png"
63+ category: 0
64+ mimetype: "application/x-desktop"
65+ title: "Browser"
66+ comment: ""
23− ListElement { desktopFile: "/usr/share/applications/facebook-webapp.desktop"; }
67+ dndUri: "file:///usr/share/applications/webbrowser-app.desktop"
68+ metadata: ""
69+ }
70+ ListElement {
24− ListElement { desktopFile: "/usr/share/applications/webbrowser-app.desktop"; }
71+ uri: "application:///usr/share/applications/gmail-webapp.desktop"
72+ icon: "../../graphics/applicationIcons/gmail.png"
73+ category: 0
74+ mimetype: "application/x-desktop"
75+ title: "GMail"
76+ comment: ""
25− ListElement { desktopFile: "/usr/share/applications/gmail-webapp.desktop"; }
77+ dndUri: "file:///usr/share/applications/gmail-webapp.desktop"
78+ metadata: ""
79+ }
26}
80}
Dash/DashApps.qml+16−225
@@ -19,246 +19,37 @@ import Ubuntu.Components 0.1
19import Utils 0.1
19import Utils 0.1
20import "../Components"
20import "../Components"
21import "../Components/ListItems"
21import "../Components/ListItems"
22+import "../Applications"
22import "Apps"
23import "Apps"
23
24
24−ScopeView {
25+GenericScopeView {
25 id: scopeView
26 id: scopeView
26
27
27 // FIXME: a way to aggregate these models would be ideal
28 // FIXME: a way to aggregate these models would be ideal
28 property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications
29 property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications
29 property var sideStageApplicationModel: shell.applicationManager.sideStageApplications
30 property var sideStageApplicationModel: shell.applicationManager.sideStageApplications
30
31
31− onMovementStarted: categoryView.showHeader()
32− onPositionedAtBeginning: {
33− if (isCurrent) {
34− categoryView.positionAtBeginning()
35− }
36− }
37−
38− onIsCurrentChanged: {
39− pageHeader.resetSearch();
40− }
41−
42− Binding {
43− target: scopeView.scope
44− property: "searchQuery"
45− value: pageHeader.searchQuery
46− }
47−
48− Connections {
49− target: panel
50− onSearchClicked: if (isCurrent) {
51− pageHeader.triggerSearch()
52− categoryView.showHeader()
53− }
54− }
55−
56 ListModel {
32 ListModel {
57− id: categoryListModel
33+ id: dummyVisibilityModifier
58− // specifies app categories, type of delegate and model used in each category
59− ListElement { category: "Running apps"; component: "Apps/RunningApplicationsGrid.qml"; modelName: "RunningApplicationsModel" }
60− ListElement { category: "Frequently used"; component: "Apps/ApplicationsFilterGrid.qml"; modelName: "FrequentlyUsedAppsModel" }
61− ListElement { category: "Installed"; component: "Apps/ApplicationsFilterGrid.qml"; modelName: "InstalledApplicationsModel" }
62− ListElement { category: "Available for download"; component: "Apps/ApplicationsFilterGrid.qml"; modelName: "AppsAvailableForDownloadModel" }
63− function getCategory(category1) {
64− if (category1 === "Running apps") {
65− return i18n.tr("Running apps");
66− }
67− if (category1 === "Frequently used") {
68− return i18n.tr("Frequently used");
69− }
70− if (category1 === "Installed") {
71− return i18n.tr("Installed");
72− }
73− if (category1 === "Available for download") {
74− return i18n.tr("Available for download");
75− }
76− return ""
77− }
78− }
79−
80− FrequentlyUsedAppsModel { id: frequentlyUsedAppsModel }
81− AppsAvailableForDownloadModel { id: appsAvailableForDownloadModel }
82−
83− // FIXME this should not be needed, the backend should handle all that itself
84− property var installedModel: null
85−
86− Repeater {
87− model: scopeView.scope.categories
88−
89− delegate: Item {
90− Component.onCompleted: if (categoryId == "installed") scopeView.installedModel = Qt.binding(function() { return model.results; });
91− }
92− }
93−
94− property var categoryModels: {
95− "FrequentlyUsedAppsModel": frequentlyUsedAppsModel,
96− "InstalledApplicationsModel": installedModel,
97− "AppsAvailableForDownloadModel": appsAvailableForDownloadModel,
98− }
99−
100− ScopeListView {
101− id: categoryView
102− anchors.fill: parent
103− model: SortFilterProxyModel {
104− model: categoryListModel
105− filterRole: 0 // 0 == modelName
106− // FIXME: need to use invertMatch here, otherwise the filter won't update correctly
107− // if filterRegExp is set before invertMatch. Bug in SortFilterProxyModel?
108− filterRegExp: invertMatch ? ((mainStageApplicationsModel.count === 0
109− && sideStageApplicationModel.count === 0) ? RegExp("RunningApplicationsModel") : RegExp("")) :
110− RegExp("InstalledApplicationsModel")
111− invertMatch: pageHeader.searchQuery.length == 0
112− }
113−
114− onAtYEndChanged: if (atYEnd) endReached()
115− onMovingChanged: if (moving && atYEnd) endReached()
116
34
117− delegate: Base {
35+ ListElement { name: "running-apps" }
118− id: container
119− highlightWhenPressed: false
120−
121− Loader {
122− anchors {
123− top: parent.top
124− left: parent.left
125− right: parent.right
126− }
127− source: component
128− onLoaded: {
129− if (modelName == "RunningApplicationsModel") {
130− item.firstModel = mainStageApplicationsModel
131− item.secondModel = sideStageApplicationModel
132− item.canEnableTerminationMode =
133− Qt.binding(function() { return isCurrent; })
134− } else {
135− function activateApplication(index, data) {
136− shell.activateApplication(data);
137− }
138−
139− item.model = Qt.binding(function() { return categoryModels[modelName]; });
140− item.clicked.connect(activateApplication);
141−
142− }
143− }
144− }
145−
146− ListView.onRemove: SequentialAnimation {
147− PropertyAction {
148− target: container; property: "ListView.delayRemove"; value: true
149− }
150− NumberAnimation {
151− target: container; property: "height"; to: 0;
152− duration: 250; easing.type: Easing.InOutQuad
153− }
154− PropertyAction {
155− target: container; property: "ListView.delayRemove"; value: false
156− }
157− }
158− }
159−
160− sectionProperty: "category"
161− sectionDelegate: Header {
162− width: categoryView.width
163− text: categoryListModel.getCategory(section)
164− }
165− pageHeader: PageHeader {
166− id: pageHeader
167− width: categoryView.width
168− text: i18n.tr("Apps")
169− searchEntryEnabled: true
170− searchHistory: scopeView.searchHistory
171− }
172− }
173−
174− OpenEffect {
175− id: effect
176− anchors {
177− fill: parent
178− bottomMargin: -bottomOverflow
179− }
180− sourceItem: categoryView
181−
182− enabled: gap > 0.0
183−
184− topGapPx: (1 - gap) * positionPx
185− topOpacity: (1 - gap * 1.2)
186− bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
187− bottomOverflow: units.gu(20)
188− bottomOpacity: 1 - (gap * 0.8)
189−
190− property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
191− property real gap: previewLoader.open ? 1.0 : 0.0
192−
193− Behavior on gap {
194− NumberAnimation {
195− duration: 200
196− easing.type: Easing.InOutQuad
197− onRunningChanged: {
198− if (!previewLoader.open && !running) {
199− previewLoader.onScreen = false
200− }
201− }
202− }
203− }
204 }
36 }
205
37
206− Connections {
38+ SortFilterProxyModel {
207− target: scopeView.scope
39+ id: runningApplicationsModel
208− onPreviewReady: {
209− previewLoader.previewData = preview
210− previewLoader.open = true
211− }
212− }
213−
214− Connections {
215− ignoreUnknownSignals: true
216− target: previewLoader.valid ? previewLoader.item : null
217− onClose: {
218− previewLoader.open = false
219− }
220− }
221−
222− PreviewDelegateMapper {
223− id: previewDelegateMapper
224− }
225−
226− Loader {
227− id: previewLoader
228− property var previewData
229− height: effect.bottomGapPx - effect.topGapPx
230− anchors {
231− top: parent.top
232− topMargin: effect.topGapPx
233− left: parent.left
234− right: parent.right
235− }
236− source: onScreen ? previewDelegateMapper.map("preview-application") : ""
237−
238− property bool open: false
239− property bool onScreen: false
240− property bool valid: item !== null
241
40
242− onOpenChanged: {
41+ property var firstModel: mainStageApplicationsModel
243− if (open) {
42+ property var secondModel: sideStageApplicationModel
244− onScreen = true
43+ property bool canEnableTerminationMode: scopeView.isCurrent
245− }
246− }
247
44
248− onLoaded: {
45+ model: dummyVisibilityModifier
249− item.previewData = previewLoader.previewData
46+ filterRole: 0
250− }
47+ filterRegExp: invertMatch ? ((mainStageApplicationsModel.count === 0 &&
48+ sideStageApplicationModel.count === 0) ? RegExp("running-apps") : RegExp("")) : RegExp("disabled")
49+ invertMatch: scopeView.scope.searchQuery.length == 0
251 }
50 }
252
51
253− // TODO: Move as InverseMouseArea to DashPreview
52+ onScopeChanged: {
254− MouseArea {
53+ scopeView.scope.categories.overrideResults("recent", runningApplicationsModel);
255− enabled: previewLoader.onScreen
256− anchors {
257− fill: parent
258− topMargin: effect.bottomGapPx
259− }
260− onClicked: {
261− previewLoader.open = false;
262− }
263 }
54 }
264}
55}
Dash/DashHome.qml+8−123
@@ -19,136 +19,21 @@ import Ubuntu.Components 0.1
19import Utils 0.1
19import Utils 0.1
20import "../Components"
20import "../Components"
21import "../Components/ListItems"
21import "../Components/ListItems"
22−import "../Applications"
23import "Apps"
22import "Apps"
24−import "Video"
25−import "Music"
26
23
27−ScopeView {
24+GenericScopeView {
28 id: root
25 id: root
29 objectName: "DashHome"
26 objectName: "DashHome"
30
27
31− onMovementStarted: listView.showHeader()
28+ SearchableResultModel {
29+ id: appsModel
32
30
33− ListModel {
31+ model: FrequentlyUsedAppsModel {}
34− id: categoryListModel
32+ filterRole: 3
35− // specifies page's content categories, type of delegate and model used in each category
33+ searchQuery: root.scope.searchQuery
36− ListElement { category: "Frequent Apps"; component: "Apps/ApplicationsFilterGrid.qml"; modelName: "AppsModel" }
37− ListElement { category: "Recent Music"; component: "Music/MusicFilterGrid.qml"; modelName: "MusicModel" }
38− ListElement { category: "Videos Popular Online"; component: "Video/VideosFilterGrid.qml"; modelName: "VideosModel" }
39− function getCategory(category1) {
40− if (category1 === "Frequent Apps") {
41− return i18n.tr("Frequent Apps");
42− }
43− if (category1 === "Recent Music") {
44− return i18n.tr("Recent Music");
45− }
46− if (category1 === "Videos Popular Online") {
47− return i18n.tr("Videos Popular Online");
48− }
49− return ""
50− }
51 }
34 }
52
35
53− FrequentlyUsedAppsModel { id: appsModel }
36+ onScopeChanged: {
54−
37+ root.scope.categories.overrideResults("applications.scope", appsModel);
55− // FIXME this should be handled by the backends by populating the global search model
56− property var musicModel: null
57− property var videosModel: null
58−
59− Repeater {
60− id: musicRepeater
61−
62− delegate: Item {
63− Component.onCompleted: if (index == 1) musicModel = Qt.binding(function() { return model.results; });
64− }
65− }
66−
67− Repeater {
68− id: videosRepeater
69−
70− delegate: Item {
71− Component.onCompleted: if (index == 3) videosModel = Qt.binding(function() { return model.results; });
72− }
73− }
74−
75− Component.onCompleted: {
76− var scope = dashContent.scopes.get("mockmusicmaster.scope")
77− if (scope) {
78− musicRepeater.model = dashContent.scopes.get("mockmusicmaster.scope").categories
79− }
80− scope = dashContent.scopes.get("mockvideosmaster.scope")
81− if (scope) {
82− videosRepeater.model = dashContent.scopes.get("mockvideosmaster.scope").categories
83− }
84− }
85−
86− Connections {
87− target: dashContent
88− onScopeLoaded: switch (scopeId) {
89− case "mockmusicmaster.scope":
90− musicRepeater.model = dashContent.scopes.get("mockmusicmaster.scope").categories
91− break;
92− case "mockvideosmaster.scope":
93− videosRepeater.model = dashContent.scopes.get("mockvideosmaster.scope").categories
94− break;
95− }
96− }
97−
98− property var categoryModels: {"AppsModel": appsModel,
99− "MusicModel": musicModel,
100− "VideosModel": videosModel,
101− }
102−
103− ScopeListView {
104− id: listView
105− anchors.fill: parent
106− model: categoryListModel
107−
108− onAtYEndChanged: if (atYEnd) endReached()
109− onMovingChanged: if (moving && atYEnd) endReached()
110−
111− delegate: Base {
112− id: container
113− highlightWhenPressed: false
114− width: listView.width
115−
116− Loader {
117− anchors { top: parent.top; left: parent.left; right: parent.right }
118− source: component
119− onLoaded: {
120− item.model = Qt.binding(function() { return categoryModels[modelName]; });
121−
122− //FIXME: workaround for lack of previews for videos in Home scope.
123− //Need to connect to the clicked() signal here and act upon it here instead.
124− if (modelName === "VideosModel") {
125− function playVideo(index, data) {
126− if (data.fileUri) {
127− shell.activateApplication('/usr/share/applications/mediaplayer-app.desktop', "/usr/share/demo-assets/videos/" + data.fileUri);
128− }
129− }
130−
131− item.clicked.connect(playVideo);
132− } else if (modelName === "AppsModel") {
133− function activateApplication(index, data) {
134− shell.activateApplication(data);
135− }
136−
137− item.objectName = "dashHomeApplicationsGrid";
138− item.clicked.connect(activateApplication);
139− }
140− }
141− }
142− }
143−
144− sectionProperty: "category"
145− sectionDelegate: Header {
146− width: listView.width
147− text: listView.model.getCategory(section)
148− }
149− pageHeader: PageHeader {
150− width: listView.width
151− text: i18n.tr("Home")
152− }
153 }
38 }
154}
39}
Dash/Generic/GenericFilterGrid.qml−1
@@ -21,7 +21,6 @@ import "../../Components/IconUtil.js" as IconUtil
21FilterGrid {
21FilterGrid {
22 id: filtergrid
22 id: filtergrid
23
23
24− filter: false
25 minimumHorizontalSpacing: units.gu(0.5)
24 minimumHorizontalSpacing: units.gu(0.5)
26 delegateWidth: units.gu(11)
25 delegateWidth: units.gu(11)
27 delegateHeight: units.gu(9.5)
26 delegateHeight: units.gu(9.5)
Dash/Generic/GenericPreview.qml+10−2
@@ -74,25 +74,33 @@ DashPreview {
74 spacing: units.gu(2)
74 spacing: units.gu(2)
75
75
76 Label {
76 Label {
77+ anchors { left: parent.left; right: parent.right }
77 visible: text != ""
78 visible: text != ""
78 fontSize: "medium"
79 fontSize: "medium"
79 opacity: 0.6
80 opacity: 0.6
80 color: "white"
81 color: "white"
81− text: previewData.subtitle
82+ text: previewData.subtitle.replace(/[\r\n]+/g, "<br />")
82 style: Text.Raised
83 style: Text.Raised
83 styleColor: "black"
84 styleColor: "black"
84 wrapMode: Text.WordWrap
85 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 }
85 }
89 }
86
90
87 Label {
91 Label {
92+ anchors { left: parent.left; right: parent.right }
88 visible: text != ""
93 visible: text != ""
89 fontSize: "small"
94 fontSize: "small"
90 opacity: 0.6
95 opacity: 0.6
91 color: "white"
96 color: "white"
92− text: previewData.description
97+ text: previewData.description.replace(/[\r\n]+/g, "<br />")
93 style: Text.Raised
98 style: Text.Raised
94 styleColor: "black"
99 styleColor: "black"
95 wrapMode: Text.WordWrap
100 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 }
96 }
104 }
97 }
105 }
98}
106}
Dash/GenericScopeView.qml+59−1
@@ -53,9 +53,14 @@ ScopeView {
53 onAtYEndChanged: if (atYEnd) endReached()
53 onAtYEndChanged: if (atYEnd) endReached()
54 onMovingChanged: if (moving && atYEnd) endReached()
54 onMovingChanged: if (moving && atYEnd) endReached()
55
55
56+ property int expandedIndex: -1
57+
56 delegate: ListItems.Base {
58 delegate: ListItems.Base {
57 highlightWhenPressed: false
59 highlightWhenPressed: false
58
60
61+ readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
62+ readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true
63+
59 Loader {
64 Loader {
60 id: rendererLoader
65 id: rendererLoader
61 anchors {
66 anchors {
@@ -67,7 +72,20 @@ ScopeView {
67 source: getRenderer(model.renderer, model.contentType)
72 source: getRenderer(model.renderer, model.contentType)
68
73
69 onLoaded: {
74 onLoaded: {
75+ if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
76+ // TODO: the running apps grid doesn't support standard scope results model yet
77+ item.firstModel = results.firstModel
78+ item.secondModel = results.secondModel
79+ } else {
70− item.model = results
80+ item.model = results
81+ }
82+ item.objectName = categoryId
83+ if (item.expandable) {
84+ var shouldFilter = index != categoryView.expandedIndex;
85+ if (shouldFilter != item.filter) {
86+ item.filter = shouldFilter;
87+ }
88+ }
71 }
89 }
72
90
73 Connections {
91 Connections {
@@ -97,13 +115,47 @@ ScopeView {
97 delegateItem.model.metadata)
115 delegateItem.model.metadata)
98 }
116 }
99 }
117 }
118+ Connections {
119+ target: categoryView
120+ onExpandedIndexChanged: {
121+ var item = rendererLoader.item;
122+ if (item.expandable) {
123+ var shouldFilter = index != categoryView.expandedIndex;
124+ if (shouldFilter != item.filter) {
125+ // If the filter animation will be seen start it, otherwise, just flip the switch
126+ var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
127+ var growingVisible = !shouldFilter && y + height < categoryView.height;
128+ if (shrinkingVisible || growingVisible) {
129+ item.startFilterAnimation(shouldFilter)
130+ } else {
131+ item.filter = shouldFilter;
132+ }
133+ if (!shouldFilter) {
134+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
135+ }
136+ }
137+ }
138+ }
139+ }
100 }
140 }
101 }
141 }
102
142
103 sectionProperty: "name"
143 sectionProperty: "name"
104 sectionDelegate: ListItems.Header {
144 sectionDelegate: ListItems.Header {
145+ property var delegate: categoryView.item(delegateIndex)
105 width: categoryView.width
146 width: categoryView.width
106 text: section
147 text: section
148+ image: {
149+ if (delegate && delegate.expandable)
150+ return delegate.filtered ? "graphics/header_handlearrow.png" : "graphics/header_handlearrow2.png"
151+ return "";
152+ }
153+ onClicked: {
154+ if (categoryView.expandedIndex != delegateIndex)
155+ categoryView.expandedIndex = delegateIndex;
156+ else
157+ categoryView.expandedIndex = -1;
158+ }
107 }
159 }
108 pageHeader: PageHeader {
160 pageHeader: PageHeader {
109 id: pageHeader
161 id: pageHeader
@@ -132,6 +184,12 @@ ScopeView {
132 }
184 }
133 }
185 }
134 case "carousel": return "Generic/GenericCarousel.qml";
186 case "carousel": return "Generic/GenericCarousel.qml";
187+ case "special": {
188+ switch (contentType) {
189+ case "apps": return "Apps/RunningApplicationsGrid.qml";
190+ default: return "Generic/GenericFilterGrid.qml";
191+ }
192+ }
135 default: return "Generic/GenericFilterGrid.qml";
193 default: return "Generic/GenericFilterGrid.qml";
136 }
194 }
137 }
195 }
Dash/Music/MusicFilterGrid.qml−1
@@ -20,7 +20,6 @@ import "../../Components"
20FilterGrid {
20FilterGrid {
21 id: filterGrid
21 id: filterGrid
22
22
23− expandable: false
24 minimumHorizontalSpacing: units.gu(2)
23 minimumHorizontalSpacing: units.gu(2)
25 maximumNumberOfColumns: 5
24 maximumNumberOfColumns: 5
26 delegateWidth: units.gu(17)
25 delegateWidth: units.gu(17)
Dash/ScopeView.qml+8−1
@@ -30,7 +30,14 @@ FocusScope {
30
30
31 // FIXME delay the search so that daemons have time to settle, note that
31 // FIXME delay the search so that daemons have time to settle, note that
32 // removing this will break ScopeView::test_changeScope
32 // removing this will break ScopeView::test_changeScope
33− onScopeChanged: timer.restart()
33+ onScopeChanged: {
34+ timer.restart();
35+ scope.activateApplication.connect(activateApp);
36+ }
37+
38+ function activateApp(desktopFilePath) {
39+ shell.activateApplication(desktopFilePath);
40+ }
34
41
35 Timer {
42 Timer {
36 id: timer
43 id: timer
Dash/SearchableResultModel.qmlnew file+40
@@ -0,0 +1,40 @@
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 Utils 0.1
18+import Unity 0.1
19+
20+SortFilterProxyModel {
21+ id: root
22+
23+ property string searchQuery: ""
24+ property bool hideOnSearch: false
25+
26+ filterRole: CategoryResults.RoleTitle
27+
28+ onSearchQueryChanged: {
29+ if (searchQuery.length == 0) {
30+ filterRegExp = RegExp("");
31+ filterCaseSensitivity = Qt.CaseInsensitive;
32+ } else if (!hideOnSearch) {
33+ setFilterWildcard(searchQuery);
34+ filterCaseSensitivity = Qt.CaseInsensitive;
35+ } else {
36+ filterRegExp = /^$/;
37+ filterCaseSensitivity = Qt.CaseInsensitive;
38+ }
39+ }
40+}
Dash/Video/VideosFilterGrid.qml−1
@@ -20,7 +20,6 @@ import "../../Components"
20FilterGrid {
20FilterGrid {
21 id: filtergrid
21 id: filtergrid
22
22
23− expandable: false
24 minimumHorizontalSpacing: units.gu(0.5)
23 minimumHorizontalSpacing: units.gu(0.5)
25 maximumNumberOfColumns: 5
24 maximumNumberOfColumns: 5
26 delegateWidth: units.gu(11)
25 delegateWidth: units.gu(11)
Dash/graphics/header_handlearrow2@20.pngnew file

Binary file not shown.

Dash/graphics/header_handlearrow@20.pngnew file

Binary file not shown.

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

Binary file not shown.

Panel/Indicators.qml+3−6
@@ -177,15 +177,12 @@ Showable {
177 }
177 }
178 indicatorsModel: indicatorsModel
178 indicatorsModel: indicatorsModel
179 clip: !indicators.fullyOpened
179 clip: !indicators.fullyOpened
180+ activeHeader: indicators.state == "hint" || indicators.state == "reveal"
180
181
181 Connections {
182 Connections {
182 target: indicatorRow
183 target: indicatorRow
183− onCurrentItemIndexChanged: menuContent.updateCurrentMenu();
184+ onCurrentItemIndexChanged: {
184− }
185−
186− function updateCurrentMenu() {
187− if (currentMenuIndex != indicatorRow.currentItemIndex) {
188− currentMenuIndex = indicatorRow.currentItemIndex;
185+ menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex);
189 }
186 }
190 }
187 }
191
188
Panel/Indicators/DatetimeIndicatorWidget.qml+1−1
@@ -19,7 +19,7 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
23
23
24Indicators.IndicatorWidget {
24Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
Panel/Indicators/DefaultIndicatorPage.qml+1−1
@@ -18,7 +18,7 @@
18 */
18 */
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21−import Unity.Indicators 0.1 as Indicators
21+import Unity.IndicatorsLegacy 0.1 as Indicators
22
22
23Indicators.IndicatorPage {
23Indicators.IndicatorPage {
24 anchors.fill: parent
24 anchors.fill: parent
Panel/Indicators/DefaultIndicatorPage2.qmlnew file+25
@@ -0,0 +1,25 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Unity.Indicators 0.1 as Indicators
22+
23+Indicators.IndicatorPage {
24+ anchors.fill: parent
25+}
Panel/Indicators/DefaultIndicatorWidget.qml+1−1
@@ -19,7 +19,7 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
23
23
24Indicators.IndicatorWidget {
24Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
Panel/Indicators/DefaultIndicatorWidget2.qmlnew file+76
@@ -0,0 +1,76 @@
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+ * Nick Dedekind <nick.dedekind@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.IndicatorWidget {
25+ id: indicatorWidget
26+
27+ width: itemRow.width + units.gu(1)
28+
29+ property alias label: itemLabel.text
30+ property alias iconSource: itemImage.source
31+
32+ Row {
33+ id: itemRow
34+ objectName: "itemRow"
35+ anchors {
36+ top: parent.top
37+ bottom: parent.bottom
38+ horizontalCenter: parent.horizontalCenter
39+ }
40+ spacing: units.gu(0.5)
41+
42+ // FIXME : Should us Ubuntu.Icon . results in low res images
43+ Image {
44+ id: itemImage
45+ objectName: "itemImage"
46+ visible: source != ""
47+ height: indicatorWidget.iconSize
48+ width: indicatorWidget.iconSize
49+ anchors.verticalCenter: parent.verticalCenter
50+ }
51+
52+ Label {
53+ id: itemLabel
54+ objectName: "itemLabel"
55+ color: Theme.palette.selected.backgroundText
56+ opacity: 0.8
57+ font.family: "Ubuntu"
58+ fontSize: "medium"
59+ anchors.verticalCenter: parent.verticalCenter
60+ visible: text != ""
61+ }
62+ }
63+
64+ onActionStateChanged: {
65+ if (actionState == undefined) {
66+ label = "";
67+ iconSource = "";
68+ enabled = false;
69+ return;
70+ }
71+
72+ label = actionState.label;
73+ iconSource = actionState.icon;
74+ enabled = actionState.visible;
75+ }
76+}
Panel/Indicators/MessagingIndicatorPage.qml+1−1
@@ -19,7 +19,7 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
23
23
24Indicators.IndicatorPage {
24Indicators.IndicatorPage {
25 anchors.fill: parent
25 anchors.fill: parent
Panel/Indicators/NetworkIndicatorPage.qml+18−14
@@ -18,28 +18,32 @@
18 */
18 */
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21−import Unity.Indicators 0.1 as Indicators
21+import Unity.IndicatorsLegacy 0.1 as Indicators
22−import Unity.Indicators.Network 0.1 as ICNetwork
22+import Unity.IndicatorsLegacy.Network 0.1 as ICNetwork
23import Ubuntu.Components 0.1
23import Ubuntu.Components 0.1
24
24
25PageStack {
25PageStack {
26− id: _network
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
27
32
28 property alias title: _mainPage.title
33 property alias title: _mainPage.title
29− property alias emptyText: _pluginItem.emptyText
34+ property alias emptyText: pluginItem.emptyText
30− property alias busType: _pluginItem.busType
35+ property alias busType: pluginItem.busType
31− property alias busName: _pluginItem.busName
36+ property alias busName: pluginItem.busName
32− property alias actionsObjectPath : _pluginItem.actionsObjectPath
37+ property alias actionsObjectPath : pluginItem.actionsObjectPath
33− property alias menuObjectPaths : _pluginItem.menuObjectPaths
38+ property alias menuObjectPaths : pluginItem.menuObjectPaths
34
39
35− __showHeader: false
36 anchors.fill: parent
40 anchors.fill: parent
37
41
38 Page {
42 Page {
39 id: _mainPage
43 id: _mainPage
40
44
41 Indicators.IndicatorPage {
45 Indicators.IndicatorPage {
42− id: _pluginItem
46+ id: pluginItem
43 anchors.fill: parent
47 anchors.fill: parent
44 }
48 }
45 }
49 }
@@ -56,24 +60,24 @@ PageStack {
56 id: networkAgent
60 id: networkAgent
57
61
58 onSecretRequested: {
62 onSecretRequested: {
59− _network.push(passwordPageComponent, {"token": token});
63+ networkPage.push(passwordPageComponent, {"token": token});
60 }
64 }
61 }
65 }
62
66
63 function start()
67 function start()
64 {
68 {
65 push(_mainPage);
69 push(_mainPage);
66− _pluginItem.start();
70+ pluginItem.start();
67 }
71 }
68
72
69 function stop()
73 function stop()
70 {
74 {
71 clear();
75 clear();
72− _pluginItem.stop();
76+ pluginItem.stop();
73 }
77 }
74
78
75 function reset()
79 function reset()
76 {
80 {
77− _pluginItem.reset();
81+ pluginItem.reset();
78 }
82 }
79}
83}
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/Indicators/NetworkIndicatorWidget.qml+1−1
@@ -19,7 +19,7 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
23
23
24Indicators.IndicatorWidget {
24Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
Panel/Indicators/client/IndicatorsList.qml+1−1
@@ -20,7 +20,7 @@
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem
22import Ubuntu.Components.ListItems 0.1 as ListItem
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24import "../.."
24import "../.."
25
25
26Page {
26Page {
Panel/Indicators/client/IndicatorsPage.qml+6−9
@@ -43,6 +43,12 @@ Page {
43 Loader {
43 Loader {
44 id: pageLoader
44 id: pageLoader
45 objectName: "pageLoader"
45 objectName: "pageLoader"
46+ clip:true
47+
48+ Rectangle {
49+ anchors.fill: pageLoader
50+ color: "#221e1c" // FIXME not in palette yet
51+ }
46
52
47 anchors {
53 anchors {
48 top: visualCheckItem.bottom
54 top: visualCheckItem.bottom
@@ -81,14 +87,5 @@ Page {
81 text: "Back"
87 text: "Back"
82 onClicked: page.pageStack.reset()
88 onClicked: page.pageStack.reset()
83 }
89 }
84− Button {
85− id: refresh
86− visible: !visualCheck.checked
87− anchors {
88− right: parent.right
89− }
90− text: "Refresh"
91− onClicked: pageLoader.item.refresh()
92− }
93 }
90 }
94}
91}
Panel/Indicators/client/IndicatorsTree.qml+7−7
@@ -27,8 +27,7 @@ Page {
27 id: page
27 id: page
28 anchors.fill: parent
28 anchors.fill: parent
29
29
30− property alias busType: menuModel.busType
30+ property string busName: unityModel.busName
31− property alias busName: menuModel.busName
32 property string actionsObjectPath
31 property string actionsObjectPath
33 property var menuObjectPaths: undefined
32 property var menuObjectPaths: undefined
34 readonly property string device: "phone"
33 readonly property string device: "phone"
@@ -36,17 +35,18 @@ Page {
36 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
35 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
37
36
38 function start() {
37 function start() {
39− menuModel.start();
40 }
38 }
41
39
42− QDBusMenuModel {
40+ UnityMenuModel {
43− id: menuModel
41+ id: unityModel
42+ busName: page.busName
43+ actions: { "indicator": page.actionsObjectPath }
44− objectPath: page.deviceMenuObjectPath
44+ menuObjectPath: page.deviceMenuObjectPath
45 }
45 }
46
46
47 Indicators.ModelPrinter {
47 Indicators.ModelPrinter {
48 id: printer
48 id: printer
49− model: menuModel
49+ model: unityModel
50 }
50 }
51
51
52 Flickable {
52 Flickable {
Panel/IndicatorsDataModel.qml+29−11
@@ -27,7 +27,19 @@ Indicators.IndicatorsModel {
27 Component.onCompleted: load()
27 Component.onCompleted: load()
28
28
29 indicatorData : {
29 indicatorData : {
30− 'indicator-messaging' : {
30+ 'indicator-location' : {
31+ 'title': 'Location',
32+ 'description' : '',
33+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
34+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
35+ },
36+ 'indicator-bluetooth' : {
37+ 'title': 'Bluetooth',
38+ 'description' : '',
39+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
40+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
41+ },
42+ 'indicator-messages' : {
31 'title': 'Messaging',
43 'title': 'Messaging',
32 'description' : '',
44 'description' : '',
33 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/MessagingIndicatorWidget.qml',
45 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/MessagingIndicatorWidget.qml',
@@ -36,26 +48,32 @@ Indicators.IndicatorsModel {
36 'indicator-sound' : {
48 'indicator-sound' : {
37 'title': 'Sound',
49 'title': 'Sound',
38 'description' : '',
50 'description' : '',
39− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/SoundIndicatorWidget.qml',
51+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
40− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage.qml'
52+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
41 },
53 },
42 'indicator-network' : {
54 'indicator-network' : {
43 'title': 'Networks',
55 'title': 'Networks',
44 'description' : '',
56 'description' : '',
45− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorWidget.qml',
57+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
46− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage.qml'
58+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage2.qml'
47 },
59 },
48− 'indicator-battery' : {
60+ 'indicator-power' : {
49 'title': 'Battery',
61 'title': 'Battery',
50 'description' : '',
62 'description' : '',
51− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget.qml',
63+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
64+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
65+ },
66+ 'indicator-session' : {
67+ 'title': 'Session',
68+ 'description' : '',
69+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
52− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage.qml'
70+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
53 },
71 },
54− 'indicator-time' : {
72+ 'indicator-datetime' : {
55 'title': 'Date and Time',
73 'title': 'Date and Time',
56 'description' : '',
74 'description' : '',
57− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DatetimeIndicatorWidget.qml',
75+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
58− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DatetimeIndicatorPage.qml'
76+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
59 }
77 }
60 }
78 }
61}
79}
Panel/MenuContent.qml+78−97
@@ -19,20 +19,31 @@ import Ubuntu.Components 0.1
19import Unity.Indicators 0.1 as Indicators
19import Unity.Indicators 0.1 as Indicators
20import "../Components"
20import "../Components"
21
21
22−Rectangle {
22+// FIXME : We dont want to use MainView.
23+// Need a regular Item which can have tabs with local header.
24+// https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
25+MainView {
23 id: content
26 id: content
24
27
25 property QtObject indicatorsModel: null
28 property QtObject indicatorsModel: null
26 property bool __contentActive: false
29 property bool __contentActive: false
27− property alias currentMenuIndex : menus.currentIndex
30+ readonly property int currentMenuIndex : tabs.selectedTabIndex
28− property color backgroundColor: "#221e1c" // FIXME not in palette yet
31+ backgroundColor: "#221e1c" // FIXME not in palette yet
29 property int contentReleaseInterval: 20000
32 property int contentReleaseInterval: 20000
33+ property bool activeHeader: false
30
34
31 width: units.gu(40)
35 width: units.gu(40)
32 height: units.gu(42)
36 height: units.gu(42)
33− color: backgroundColor
34
37
35− signal menuSelected(int index)
38+ function setCurrentMenuIndex(index) {
39+ if (currentMenuIndex !== index) {
40+ if (currentMenuIndex == -1) {
41+ tabs.tabBar.animate = false;
42+ }
43+ tabs.selectedTabIndex = index;
44+ tabs.tabBar.animate = true;
45+ }
46+ }
36
47
37 function activateContent() {
48 function activateContent() {
38 contentReleaseTimer.stop();
49 contentReleaseTimer.stop();
@@ -40,95 +51,75 @@ Rectangle {
40 }
51 }
41
52
42 function releaseContent() {
53 function releaseContent() {
43− if (__contentActive)
54+ if (__contentActive) {
44 contentReleaseTimer.restart();
55 contentReleaseTimer.restart();
56+ }
45 }
57 }
46
58
47− ListView {
59+ onActiveHeaderChanged: {
48− id: menus
60+ tabs.tabBar.selectionMode = activeHeader;
49− objectName: "menus"
61+ tabs.tabBar.alwaysSelectionMode = activeHeader;
50−
51− anchors.bottom: parent.bottom
52− anchors.top: header.bottom
53− width: parent.width
54−
55− spacing: units.gu(0.5)
56− enabled: opacity != 0
57− orientation: ListView.Horizontal
58− model: indicatorsModel
59− interactive: false
60− //FIXME: This will make sure that all plugins still alive
61− //check bug https://bugs.launchpad.net/manhattan/+bug/1088945 for more details
62− cacheBuffer: 2147483647
63−
64− delegate: Loader {
65− clip: true
66− property bool contentActive: content.__contentActive && menuActivator.content[index].active
67−
68− onContentActiveChanged: {
69− if (contentActive && item) {
70− item.start();
71− } else if (!contentActive && item) {
72− item.stop();
73− }
74− }
62+ }
75
63
76− width: menus.width
64+ Tabs {
77− height: menus.height
65+ id: tabs
78− source: pageSource
66+ objectName: "tabs"
79− onVisibleChanged: {
67+ anchors.fill: parent
80− // Reset the indicator states
68+ selectedTabIndex: -1
81− if (!visible && item && item["reset"]) {
69+
82− item.reset();
70+ Repeater {
83− }
71+ id: repeater
72+ model: indicatorsModel
73+ objectName: "menus"
74+
75+ // FIXME: This is needed because tabs dont handle repeaters well.
76+ // Due to the child ordering happening after child insertion.
77+ // QTBUG-32438
78+ onItemAdded: {
79+ parent.childrenChanged();
84 }
80 }
85− asynchronous: true
86
81
82+ Tab {
83+ id: tab
84+ objectName: model.identifier
85+ title: model.title
86+
87+ page: Page {
88+ Loader {
89+ clip: true
90+ anchors.fill: parent
91+ source: pageSource
92+ asynchronous: true
93+
94+ property bool contentActive: content.__contentActive
95+
96+ onContentActiveChanged: {
97+ if (contentActive && item) {
98+ item.start()
99+ } else if (!contentActive && item) {
100+ item.stop()
101+ }
102+ }
103+
104+ onVisibleChanged: {
105+ // Reset the indicator states
106+ if (!visible && item && item["reset"]) {
107+ item.reset()
108+ }
109+ }
110+
87− onLoaded: {
111+ onLoaded: {
88− for(var pName in indicatorProperties) {
112+ for(var pName in indicatorProperties) {
89− if (item.hasOwnProperty(pName)) {
113+ if (item.hasOwnProperty(pName)) {
90− item[pName] = indicatorProperties[pName];
114+ item[pName] = indicatorProperties[pName]
115+ }
116+ }
117+ if (contentActive && menus.visible) {
118+ item.start()
119+ }
120+ }
91 }
121 }
92 }
122 }
93− if (contentActive && menus.visible) {
94− item.start();
95− }
96− }
97−
98− // Need to use a binding because the handle height changes.
99− // FIXME: Dont know why we're using handle height (introduces dep).. Check with design about bottom margin
100− Binding {
101− target: item
102− property: "anchors.bottomMargin"
103− value: handle.height
104− }
105− }
106− }
107−
108− Rectangle {
109− id: header
110− objectName: "header"
111−
112− property alias title: pageHeader.text
113−
114− color: backgroundColor
115− opacity: pageHeader.opacity
116− anchors {
117− left: parent.left
118− right: parent.right
119− }
120− height: childrenRect.height
121−
122− PageHeader {
123− id: pageHeader
124− anchors {
125− left: parent.left
126− right: parent.right
127− }
128− text: {
129− if (indicatorsModel && menus.currentIndex >= 0 && menus.currentIndex < indicatorsModel.count)
130− return indicatorsModel.data(menus.currentIndex, Indicators.IndicatorsModelRole.Title);
131− return "";
132 }
123 }
133 }
124 }
134 }
125 }
@@ -137,16 +128,6 @@ Rectangle {
137 id: contentReleaseTimer
128 id: contentReleaseTimer
138
129
139 interval: contentReleaseInterval
130 interval: contentReleaseInterval
140− onTriggered: {
141− __contentActive = false;
131+ onTriggered: content.__contentActive = false
142− menuActivator.clear();
143− }
144− }
145−
146− Indicators.MenuContentActivator {
147− id: menuActivator
148− running: content.__contentActive
149− baseIndex: content.currentMenuIndex
150− count: indicatorsModel.count
151 }
132 }
152}
133}
Shell.qml+50−18
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import GSettings 1.0
18import Ubuntu.Application 0.1
19import Ubuntu.Application 0.1
19import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
20import Ubuntu.Gestures 0.1
21import Ubuntu.Gestures 0.1
@@ -41,8 +42,8 @@ FocusScope {
41 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
42 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
42
43
43 property real edgeSize: units.gu(2)
44 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 defaultBackground: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
45− property url background: default_background
46+ property url background
46 readonly property real panelHeight: panel.panelHeight
47 readonly property real panelHeight: panel.panelHeight
47
48
48 property bool dashShown: dash.shown
49 property bool dashShown: dash.shown
@@ -115,6 +116,36 @@ FocusScope {
115 }
116 }
116 }
117 }
117
118
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+
118 VolumeControl {
149 VolumeControl {
119 id: volumeControl
150 id: volumeControl
120 }
151 }
@@ -130,21 +161,25 @@ FocusScope {
130 // Whether the underlay is fully covered by opaque UI elements.
161 // Whether the underlay is fully covered by opaque UI elements.
131 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
162 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
132
163
164+ readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
165+ || (sideStage.applications && sideStage.applications.count > 0))
166+
133 // Whether the user should see the topmost application surface (if there's one at all).
167 // Whether the user should see the topmost application surface (if there's one at all).
134− property bool applicationSurfaceShouldBeSeen:
168+ readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
135− (mainStage.applications && mainStage.applications.count > 0)
136− && (!stages.fullyHidden && !mainStage.usingScreenshots)
169+ && !mainStage.usingScreenshots // but want sideStage animating over app surface
170+
171+
137
172
138 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
173 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
139 // through the translucent parts of the shell surface.
174 // through the translucent parts of the shell surface.
140 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
175 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
141
176
142− Image {
177+ CrossFadeImage {
143 id: backgroundImage
178 id: backgroundImage
144− source: shell.background
179+ objectName: "backgroundImage"
145− sourceSize.width: parent.width
180+
146− sourceSize.height: parent.height
147 anchors.fill: parent
181 anchors.fill: parent
182+ source: shell.background
148 }
183 }
149
184
150 Rectangle {
185 Rectangle {
@@ -189,7 +224,6 @@ FocusScope {
189 }
224 }
190 }
225 }
191
226
192−
193 Item {
227 Item {
194 id: stagesOuterContainer
228 id: stagesOuterContainer
195
229
@@ -254,7 +288,6 @@ FocusScope {
254 ignoreUnknownSignals: true
288 ignoreUnknownSignals: true
255 }
289 }
256
290
257−
258 Stage {
291 Stage {
259 id: mainStage
292 id: mainStage
260
293
@@ -404,6 +437,7 @@ FocusScope {
404
437
405 Greeter {
438 Greeter {
406 id: greeter
439 id: greeter
440+ objectName: "greeter"
407
441
408 available: true
442 available: true
409 hides: [launcher, panel.indicators, hud]
443 hides: [launcher, panel.indicators, hud]
@@ -428,10 +462,6 @@ FocusScope {
428 }
462 }
429
463
430 onUnlocked: greeter.hide()
464 onUnlocked: greeter.hide()
431− onSelected: {
432− var bgPath = greeter.model.data(uid, LightDM.UserRoles.BackgroundPathRole)
433− shell.background = bgPath ? bgPath : default_background
434− }
435
465
436 onLeftTeaserPressedChanged: {
466 onLeftTeaserPressedChanged: {
437 if (leftTeaserPressed) {
467 if (leftTeaserPressed) {
@@ -471,11 +501,13 @@ FocusScope {
471 }
501 }
472 }
502 }
473
503
474− onPowerStateChange: {
504+ onDisplayPowerStateChange: {
475− if (state == 0) { // suspend
505+ // We ignore any display-off signals when the proximity sensor
506+ // is active. This usually indicates something like a phone call.
507+ if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
476 powerConnection.setFocused(false);
508 powerConnection.setFocused(false);
477 greeter.show();
509 greeter.show();
478− } else if (state == 1) { // active
510+ } else if (status == Powerd.On) {
479 powerConnection.setFocused(true);
511 powerConnection.setFocused(true);
480 }
512 }
481 }
513 }
build+7−3
@@ -40,11 +40,14 @@ install_dependencies() {
40 echo "Installing Unity 8 dependencies.."
40 echo "Installing Unity 8 dependencies.."
41 sudo apt-get install devscripts \
41 sudo apt-get install devscripts \
42 equivs \
42 equivs \
43+ gsettings-desktop-schemas \
43 qmenumodel-qml \
44 qmenumodel-qml \
44− qtdeclarative5-ubuntu-ui-toolkit-plugin \
45 qtdeclarative5-dee-plugin \
45 qtdeclarative5-dee-plugin \
46+ qtdeclarative5-gsettings1.0 \
47+ qtdeclarative5-ubuntu-ui-toolkit-plugin \
48+ unity-notifications-impl \
49+ qtdeclarative5-xmllistmodel-plugin \
46 ubuntu-mobile-icons \
50 ubuntu-mobile-icons \
47− unity-notifications-impl-1 \
48 unity-scope-home \
51 unity-scope-home \
49 unity-lens-applications \
52 unity-lens-applications \
50 || exit 5
53 || exit 5
@@ -56,7 +59,8 @@ mk_build_deps() {
56
59
57if [ -f "/usr/bin/ninja" ] ; then
60if [ -f "/usr/bin/ninja" ] ; then
58 GENERATOR="-G Ninja"
61 GENERATOR="-G Ninja"
62+ # Ninja does not need -j, it parallelizes automatically.
59− BUILD_COMMAND="ninja -j$NUM_JOBS"
63+ BUILD_COMMAND="ninja"
60else
64else
61 GENERATOR=
65 GENERATOR=
62 BUILD_COMMAND="make -j$NUM_JOBS"
66 BUILD_COMMAND="make -j$NUM_JOBS"
debian/changelog+309
@@ -1,3 +1,312 @@
1+unity8 (7.81.3+13.10.20130828.1-0ubuntu1) saucy; urgency=low
2+
3+ [ Andrea Cimitan ]
4+ * Hide the LauncherPanel when it's really hidden, by changing visible
5+ to false.
6+
7+ [ Gerry Boland ]
8+ * Fix sidestage applications - they were being ignored in the show-
9+ application-surface logic. (LP: #1217027, #1210079)
10+
11+ [ Michał Sawicz ]
12+ * Raise the exception if typing failed in autopilot.
13+
14+ [ Nick Dedekind ]
15+ * Added location indicator defaults.
16+
17+ [ Michael Zanetti ]
18+ * increase minimal dragging width for dismissing apps with left edge
19+ make it a configurable parameter and adjust animation to look like
20+ requested in the bug report . (LP: #1213153)
21+
22+ [ Ubuntu daily release ]
23+ * Automatic snapshot from revision 262
24+
25+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 28 Aug 2013 11:55:46 +0000
26+
27+unity8 (7.81.3+13.10.20130827.1-0ubuntu1) saucy; urgency=low
28+
29+ [ Michael Zanetti ]
30+ * unset model in quicklist before closing it.
31+
32+ [ Ubuntu daily release ]
33+ * Automatic snapshot from revision 256
34+
35+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 27 Aug 2013 10:49:17 +0000
36+
37+unity8 (7.81.3+13.10.20130827-0ubuntu1) saucy; urgency=low
38+
39+ [ Michael Zanetti ]
40+ * Theme the Quicklist Popover.
41+
42+ [ Albert Astals ]
43+ * Apply expandedIndex on delegate creation bug #1213033. (LP:
44+ #1213033)
45+
46+ [ Ubuntu daily release ]
47+ * Automatic snapshot from revision 254
48+
49+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 27 Aug 2013 02:10:43 +0000
50+
51+unity8 (7.81.3+13.10.20130826.5-0ubuntu1) saucy; urgency=low
52+
53+ [ Michael Zanetti ]
54+ * tweak launcher folding and visuals according to feedback from
55+ design: - increase foldingStartHeight - Introduces a
56+ foldingStopHeight. Folding only happens between the
57+ foldingStartHeight and the foldingStopHeight. - Only change
58+ brightness while folding - remove highlight of pressed icon -
59+ decrease launcher's width by half a grid unit.
60+
61+ [ Ubuntu daily release ]
62+ * Automatic snapshot from revision 251
63+
64+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 22:09:43 +0000
65+
66+unity8 (7.81.3+13.10.20130826.4-0ubuntu1) saucy; urgency=low
67+
68+ [ Michał Sawicz ]
69+ * Add module entry in HudClient's qmldir.
70+
71+ [ Jussi Pakkanen ]
72+ * Let Ninja parallelize itself.
73+
74+ [ Nick Dedekind ]
75+ * Abstraction of indicator menu item properties prior to move into
76+ common components library.
77+
78+ [ Christopher Lee ]
79+ * Added autopilot-tests for ephemeral, interactive and snap-decision
80+ notifications.
81+
82+ [ Mirco Müller ]
83+ * Added autopilot-tests for ephemeral, interactive and snap-decision
84+ notifications.
85+
86+ [ Michael Zanetti ]
87+ * only search visible children in findChild, add findInvisibleChild
88+ for others.
89+ * Added autopilot-tests for ephemeral, interactive and snap-decision
90+ notifications.
91+
92+ [ Thomi Richards ]
93+ * Added autopilot-tests for ephemeral, interactive and snap-decision
94+ notifications.
95+
96+ [ Ubuntu daily release ]
97+ * Automatic snapshot from revision 249
98+
99+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 18:34:44 +0000
100+
101+unity8 (7.81.3+13.10.20130826.2-0ubuntu1) saucy; urgency=low
102+
103+ [ Albert Astals ]
104+ * Don't include QtQML It includes LOTS of files we don't need.
105+
106+ [ Ubuntu daily release ]
107+ * Automatic snapshot from revision 243
108+
109+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 10:08:52 +0000
110+
111+unity8 (7.81.3+13.10.20130826.1-0ubuntu1) saucy; urgency=low
112+
113+ * Automatic snapshot from revision 241
114+
115+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 06:08:33 +0000
116+
117+unity8 (7.81.3+13.10.20130826-0ubuntu1) saucy; urgency=low
118+
119+ [ Michael Zanetti ]
120+ * delay move operations if the start dragging operation is running
121+ This prevents items to left in wrong places when transitions clash.
122+ * add empty setUser to allow compiling in jenkins again until the
123+ branch that matches unity-api is ready to land. .
124+ * add count and progress overlay information to real model too.
125+
126+ [ Pawel Stolowski ]
127+ * Changed the type of setActive argument from
128+ QVector<AbstractFilterOption *>::size_type to unsigned int.
129+
130+ [ Ubuntu daily release ]
131+ * Automatic snapshot from revision 240
132+
133+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 02:31:42 +0000
134+
135+unity8 (7.81.3+13.10.20130821.2-0ubuntu1) saucy; urgency=low
136+
137+ [ Michael Terry ]
138+ * Listen to display-power-changed rather than system-power-changed
139+ signals when showing the greeter; ignore such signals when the
140+ proximity sensor is active. (LP: #1214477)
141+
142+ [ Pawel Stolowski ]
143+ * Added a role for accessing progress-source property of categories.
144+
145+ [ Ubuntu daily release ]
146+ * Automatic snapshot from revision 233
147+
148+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 22:09:53 +0000
149+
150+unity8 (7.81.3+13.10.20130821.1-0ubuntu1) saucy; urgency=low
151+
152+ [ Michael Zanetti ]
153+ * allow testing at 11:13. Old code failed because the text actually
154+ says "11:13 AM".
155+ * add some checks if we actually clicked an item or in the spacing
156+ between them gets rid of some warnings printed by the launcher .
157+
158+ [ Michał Sawicz ]
159+ * Update runtime deps in the build script.
160+
161+ [ Nick Dedekind ]
162+ * Added/Updated legacy network indicator components to use with new
163+ indicator backend.
164+
165+ [ Albert Astals ]
166+ * Fix insertions/removals on the qlimitproxymodel . (LP: #1213959)
167+
168+ [ Ubuntu daily release ]
169+ * Automatic snapshot from revision 230
170+
171+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 15:04:59 +0000
172+
173+unity8 (7.81.3+13.10.20130821-0ubuntu1) saucy; urgency=low
174+
175+ [ Michael Zanetti ]
176+ * added support for count emblems and progress overlays on the
177+ launcher.
178+
179+ [ Pawel Stolowski ]
180+ * Add role for getting filter options model. Add method to activate
181+ option based on index or id.
182+
183+ [ Ted Gould ]
184+ * Mark indicators-client as providing an indicator-renderer.
185+
186+ [ Nick Dedekind ]
187+ * Added flatmenuproxymodel pass-through signals from qmenumodel.
188+ * Renamed indicator-messaging to indicator-messages.
189+
190+ [ Gerry Boland ]
191+ * Typo fix in FrequentlyUsedAppsModel.
192+
193+ [ Ubuntu daily release ]
194+ * Automatic snapshot from revision 224
195+
196+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 08:05:13 +0000
197+
198+unity8 (7.81.3+13.10.20130820.2-0ubuntu1) saucy; urgency=low
199+
200+ [ Michael Zanetti ]
201+ * initial support for quicklists For now they support pinning and
202+ removing of items.
203+
204+ [ Ubuntu daily release ]
205+ * Automatic snapshot from revision 217
206+
207+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 10:09:36 +0000
208+
209+unity8 (7.81.3+13.10.20130820-0ubuntu1) saucy; urgency=low
210+
211+ [ Andrea Cimitan ]
212+ * Implement background changing through gsettings. Make sure it does
213+ fallback to default background when needed.
214+
215+ [ Michael Zanetti ]
216+ * Implement background changing through gsettings. Make sure it does
217+ fallback to default background when needed.
218+ * Add Drag'n'drop support to Launcher As dragging an item pins it to
219+ the launcher this also contains initial quicklist and pinning
220+ support in the plugin part.
221+
222+ [ Michał Sawicz ]
223+ * Fix generic preview wrapping and force rich text parsing.
224+
225+ [ Michal Hruby ]
226+ * Remove the override for Apps available for download, click scope
227+ provides these now.
228+ * Hide rating widgets when scope provides rating set to < 0.0. Also
229+ fallback to regular preview image if there are no more_screenshots
230+ specified.
231+
232+ [ Albert Astals ]
233+ * Make sure minYExtent is updated before setting the new content
234+ height Otherwise bad things can happen in the positioning .
235+
236+ [ Ubuntu daily release ]
237+ * Automatic snapshot from revision 215
238+
239+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 02:08:42 +0000
240+
241+unity8 (7.81.3+13.10.20130816.3-0ubuntu1) saucy; urgency=low
242+
243+ [ Michal Hruby ]
244+ * Implement overrideResults() method, which allows us to seamlessly
245+ combine real scope data with mocked data.
246+
247+ [ Michał Sawicz ]
248+ * Wait for activeFocus before typing in autopilot tests. (LP:
249+ #1212580)
250+
251+ [ Nick Dedekind ]
252+ * Fixed network indicator password dialog not appearing. (LP:
253+ #1212730)
254+ * Remove time & battery indicator service info.
255+
256+ [ Albert Astals ]
257+ * Dash category expansion.
258+ * Fix crash in the shell test Give the item a parent, otherwise the
259+ qml engine decides to adopt it and when we do the deleteLater on
260+ them, they have been already deleted. Since we are the parents, we
261+ don't need to call the deleteLAter eiether they'll be properly
262+ deleted on our deletion.
263+
264+ [ Michael Zanetti ]
265+ * make entering text in lockscreen tests more robust. (LP: #1212580)
266+
267+ [ Michael Terry ]
268+ * Define the 'build' target as PHONY so make doesn't get confused by
269+ our 'build' script.
270+ * Add a test for the Powerd plugin shell support.
271+
272+ [ Pawel Stolowski ]
273+ * Bindings for filters.
274+
275+ [ Ubuntu daily release ]
276+ * Automatic snapshot from revision 208
277+
278+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 16 Aug 2013 14:11:35 +0000
279+
280+unity8 (7.81.3+13.10.20130814.3-0ubuntu1) saucy; urgency=low
281+
282+ [ Ted Gould ]
283+ * Upstart signals to control indicator services.
284+
285+ [ Nick Dedekind ]
286+ * Replaced indicator menu listView with tabs view.
287+ * Transition Unity.Indicators to UnityMenuModel.
288+
289+ [ Ubuntu daily release ]
290+ * Automatic snapshot from revision 197
291+
292+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 14 Aug 2013 14:33:39 +0000
293+
294+unity8 (7.81.3+13.10.20130814.1-0ubuntu1) saucy; urgency=low
295+
296+ [ Michał Sawicz ]
297+ * Add AppPreview.
298+
299+ [ Nick Dedekind ]
300+ * Use key from indicator service file to source indicator positions.
301+
302+ [ Diego Sarmentero ]
303+ * Add AppPreview.
304+
305+ [ Ubuntu daily release ]
306+ * Automatic snapshot from revision 193
307+
308+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 14 Aug 2013 06:33:14 +0000
309+
1unity8 (7.81.3+13.10.20130813.1-0ubuntu1) saucy; urgency=low
310unity8 (7.81.3+13.10.20130813.1-0ubuntu1) saucy; urgency=low
2
311
3 [ Michael Zanetti ]
312 [ Michael Zanetti ]
debian/control+11−4
@@ -10,15 +10,19 @@ Build-Depends: cmake,
10 libgles2-mesa-dev[armhf],
10 libgles2-mesa-dev[armhf],
11 libhud-client2-dev,
11 libhud-client2-dev,
12 libjson-perl,
12 libjson-perl,
13+ libnih-dbus-dev,
14+ libnih-dev,
13 libnm-glib-dev,
15 libnm-glib-dev,
14 libnm-util-dev,
16 libnm-util-dev,
15 libpulse-dev,
17 libpulse-dev,
16− libqmenumodel-dev,
18+ libqmenumodel-dev (>= 0.2.7),
17 libqt5v8-5-private-dev,
19 libqt5v8-5-private-dev,
20+ libsigc++-2.0-dev,
18 libunity-api-dev (>= 7.80.3),
21 libunity-api-dev (>= 7.80.3),
19 libunity-core-6.0-dev (<< 7.80),
22 libunity-core-6.0-dev (<< 7.80),
20− libunity-dev (>= 7.0.3),
23+ libunity-dev (>= 7.0.12),
21 libunity-scopes-json-def-phone,
24 libunity-scopes-json-def-phone,
25+ libupstart-dev,
22 libusermetricsoutput1-dev,
26 libusermetricsoutput1-dev,
23 libxcb1-dev,
27 libxcb1-dev,
24 pkg-config,
28 pkg-config,
@@ -46,7 +50,7 @@ Vcs-Browser: https://bazaar.launchpad.net/~unity-team/unity8/trunk
46
50
47Package: indicators-client
51Package: indicators-client
48Architecture: amd64 armhf i386
52Architecture: amd64 armhf i386
49−Depends: qmenumodel-qml,
53+Depends: qmenumodel-qml (>= 0.2.7),
50 qtdeclarative5-ubuntu-ui-toolkit-plugin | qt-components-ubuntu,
54 qtdeclarative5-ubuntu-ui-toolkit-plugin | qt-components-ubuntu,
51 unity8 (= ${binary:Version}),
55 unity8 (= ${binary:Version}),
52 ${misc:Depends},
56 ${misc:Depends},
@@ -56,8 +60,11 @@ Description: Indicators client test application
56
60
57Package: unity8
61Package: unity8
58Architecture: any
62Architecture: any
63+Provides: indicator-renderer,
64+Depends: gsettings-desktop-schemas,
59−Depends: qmenumodel-qml,
65+ qmenumodel-qml (>= 0.2.7),
60 qtdeclarative5-dee-plugin,
66 qtdeclarative5-dee-plugin,
67+ qtdeclarative5-gsettings1.0,
61 qtdeclarative5-ubuntu-ui-toolkit-plugin,
68 qtdeclarative5-ubuntu-ui-toolkit-plugin,
62 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
69 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
63 qtdeclarative5-xmllistmodel-plugin,
70 qtdeclarative5-xmllistmodel-plugin,
debian/rules+2−1
@@ -8,7 +8,8 @@ export NO_PNG_PKG_MANGLE=1
8
8
9SCOPES_RECOMMENDS := $(shell perl debian/scopes-recommends-generator /usr/share/unity/client-scopes.json)
9SCOPES_RECOMMENDS := $(shell perl debian/scopes-recommends-generator /usr/share/unity/client-scopes.json)
10
10
11+.PHONY: build
11−%:
12+build %:
12 dh $@ --parallel --fail-missing --with python2
13 dh $@ --parallel --fail-missing --with python2
13
14
14override_dh_auto_clean:
15override_dh_auto_clean:
main.cpp−1
@@ -18,7 +18,6 @@
18 */
18 */
19
19
20// Qt
20// Qt
21−#include <QtQml>
22#include <QtQuick/QQuickView>
21#include <QtQuick/QQuickView>
23#include <QtGui/QIcon>
22#include <QtGui/QIcon>
24#include <QtGui/QGuiApplication>
23#include <QtGui/QGuiApplication>
plugins/HudClient/qmldir+1
@@ -1,2 +1,3 @@
1+module HudClient
1plugin HudClientQml
2plugin HudClientQml
2typeinfo plugin.qmltypes
3typeinfo plugin.qmltypes
plugins/LightDM/plugin.cpp+1−1
@@ -25,7 +25,7 @@
25#include <QLightDM/UsersModel>
25#include <QLightDM/UsersModel>
26
26
27#include <QtCore/QAbstractItemModel>
27#include <QtCore/QAbstractItemModel>
28−#include <QtQml>
28+#include <QtQml/qqml.h>
29
29
30static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
30static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
31{
31{
plugins/ListViewWithPageHeader/listviewwithpageheader.cpp+47−11
@@ -280,7 +280,7 @@ void ListViewWithPageHeader::setSectionDelegate(QQmlComponent *delegate)
280
280
281 m_sectionDelegate = delegate;
281 m_sectionDelegate = delegate;
282
282
283− m_topSectionItem = getSectionItem(-1, QString());
283+ m_topSectionItem = getSectionItem(QString());
284 m_topSectionItem->setZ(3);
284 m_topSectionItem->setZ(3);
285 QQuickItemPrivate::get(m_topSectionItem)->setCulled(true);
285 QQuickItemPrivate::get(m_topSectionItem)->setCulled(true);
286
286
@@ -380,20 +380,51 @@ void ListViewWithPageHeader::showHeader()
380 }
380 }
381}
381}
382
382
383+QQuickItem *ListViewWithPageHeader::item(int modelIndex) const
384+{
385+ ListItem *item = itemAtIndex(modelIndex);
386+ if (item)
387+ return item->m_item;
388+ else
389+ return nullptr;
390+}
391+
383bool ListViewWithPageHeader::maximizeVisibleArea(int modelIndex)
392bool ListViewWithPageHeader::maximizeVisibleArea(int modelIndex)
384{
393{
385 ListItem *listItem = itemAtIndex(modelIndex);
394 ListItem *listItem = itemAtIndex(modelIndex);
386− if (listItem)
395+ if (listItem) {
387− {
396+ return maximizeVisibleArea(listItem, listItem->height());
397+ }
398+
399+ return false;
400+}
401+
402+bool ListViewWithPageHeader::maximizeVisibleArea(int modelIndex, int itemHeight)
403+{
404+ if (itemHeight < 0)
405+ return false;
406+
407+ ListItem *listItem = itemAtIndex(modelIndex);
408+ if (listItem) {
409+ return maximizeVisibleArea(listItem, itemHeight + (listItem->m_sectionItem ? listItem->m_sectionItem->height() : 0));
410+ }
411+
412+ return false;
413+}
414+
415+bool ListViewWithPageHeader::maximizeVisibleArea(ListItem *listItem, int listItemHeight)
416+{
417+ if (listItem) {
418+ layout();
388 const auto listItemY = m_clipItem->y() + listItem->y();
419 const auto listItemY = m_clipItem->y() + listItem->y();
389− if (listItemY > contentY() && listItemY + listItem->height() > contentY() + height()) {
420+ if (listItemY > contentY() && listItemY + listItemHeight > contentY() + height()) {
390 // we can scroll the list up to show more stuff
421 // we can scroll the list up to show more stuff
391− const auto to = qMin(listItemY, listItemY + listItem->height() - height());
422+ const auto to = qMin(listItemY, listItemY + listItemHeight - height());
392 m_contentYAnimation->setTo(to);
423 m_contentYAnimation->setTo(to);
393 contentYAnimationType = ContentYAnimationMaximizeVisibleArea;
424 contentYAnimationType = ContentYAnimationMaximizeVisibleArea;
394 m_contentYAnimation->start();
425 m_contentYAnimation->start();
395− } else if ((listItemY < contentY() && listItemY + listItem->height() < contentY() + height()) ||
426+ } else if ((listItemY < contentY() && listItemY + listItemHeight < contentY() + height()) ||
396− (m_topSectionItem && !listItem->m_sectionItem && listItemY - m_topSectionItem->height() < contentY() && listItemY + listItem->height() < contentY() + height()))
427+ (m_topSectionItem && !listItem->m_sectionItem && listItemY - m_topSectionItem->height() < contentY() && listItemY + listItemHeight < contentY() + height()))
397 {
428 {
398 // we can scroll the list down to show more stuff
429 // we can scroll the list down to show more stuff
399 auto realVisibleListItemY = listItemY;
430 auto realVisibleListItemY = listItemY;
@@ -406,7 +437,7 @@ bool ListViewWithPageHeader::maximizeVisibleArea(int modelIndex)
406 realVisibleListItemY -= m_topSectionItem->height();
437 realVisibleListItemY -= m_topSectionItem->height();
407 }
438 }
408 }
439 }
409− const auto to = qMax(realVisibleListItemY, listItemY + listItem->height() - height());
440+ const auto to = qMax(realVisibleListItemY, listItemY + listItemHeight - height());
410 m_contentYAnimation->setTo(to);
441 m_contentYAnimation->setTo(to);
411 contentYAnimationType = ContentYAnimationMaximizeVisibleArea;
442 contentYAnimationType = ContentYAnimationMaximizeVisibleArea;
412 m_contentYAnimation->start();
443 m_contentYAnimation->start();
@@ -641,17 +672,17 @@ QQuickItem *ListViewWithPageHeader::getSectionItem(int modelIndex, bool alreadyI
641 }
672 }
642 }
673 }
643
674
644− return getSectionItem(modelIndex, section);
675+ return getSectionItem(section);
645}
676}
646
677
647−QQuickItem *ListViewWithPageHeader::getSectionItem(int modelIndex, const QString &sectionText)
678+QQuickItem *ListViewWithPageHeader::getSectionItem(const QString &sectionText)
648{
679{
649 QQuickItem *sectionItem = nullptr;
680 QQuickItem *sectionItem = nullptr;
650
681
651 QQmlContext *creationContext = m_sectionDelegate->creationContext();
682 QQmlContext *creationContext = m_sectionDelegate->creationContext();
652 QQmlContext *context = new QQmlContext(creationContext ? creationContext : qmlContext(this));
683 QQmlContext *context = new QQmlContext(creationContext ? creationContext : qmlContext(this));
653 context->setContextProperty(QLatin1String("section"), sectionText);
684 context->setContextProperty(QLatin1String("section"), sectionText);
654− context->setContextProperty(QLatin1String("delegateIndex"), modelIndex);
685+ context->setContextProperty(QLatin1String("delegateIndex"), -1);
655 QObject *nobj = m_sectionDelegate->beginCreate(context);
686 QObject *nobj = m_sectionDelegate->beginCreate(context);
656 if (nobj) {
687 if (nobj) {
657 QQml_setParent_noEvent(context, nobj);
688 QQml_setParent_noEvent(context, nobj);
@@ -786,6 +817,10 @@ ListViewWithPageHeader::ListItem *ListViewWithPageHeader::createItem(int modelIn
786 m_firstVisibleIndex = modelIndex;
817 m_firstVisibleIndex = modelIndex;
787 polish();
818 polish();
788 }
819 }
820+ if (listItem->m_sectionItem) {
821+ QQmlContext *context = QQmlEngine::contextForObject(listItem->m_sectionItem)->parentContext();
822+ context->setContextProperty(QLatin1String("delegateIndex"), modelIndex);
823+ }
789 adjustMinYExtent();
824 adjustMinYExtent();
790 m_contentHeightDirty = true;
825 m_contentHeightDirty = true;
791 }
826 }
@@ -1209,6 +1244,7 @@ void ListViewWithPageHeader::updatePolish()
1209 }
1244 }
1210
1245
1211 m_contentHeightDirty = false;
1246 m_contentHeightDirty = false;
1247+ adjustMinYExtent();
1212 setContentHeight(contentHeight);
1248 setContentHeight(contentHeight);
1213 }
1249 }
1214}
1250}
plugins/ListViewWithPageHeader/listviewwithpageheader.h+5−1
@@ -88,11 +88,13 @@ public:
88
88
89 Q_INVOKABLE void positionAtBeginning();
89 Q_INVOKABLE void positionAtBeginning();
90 Q_INVOKABLE void showHeader();
90 Q_INVOKABLE void showHeader();
91+ Q_INVOKABLE QQuickItem *item(int modelIndex) const;
91
92
92 // The index has to be created for this to try to do something
93 // The index has to be created for this to try to do something
93 // Created items are those visible and the precached ones
94 // Created items are those visible and the precached ones
94 // Returns if the item existed or not
95 // Returns if the item existed or not
95 Q_INVOKABLE bool maximizeVisibleArea(int modelIndex);
96 Q_INVOKABLE bool maximizeVisibleArea(int modelIndex);
97+ Q_INVOKABLE bool maximizeVisibleArea(int modelIndex, int itemHeight);
96
98
97Q_SIGNALS:
99Q_SIGNALS:
98 void modelChanged();
100 void modelChanged();
@@ -141,6 +143,8 @@ private:
141 QQuickItem *m_sectionItem;
143 QQuickItem *m_sectionItem;
142 };
144 };
143
145
146+ bool maximizeVisibleArea(ListItem *listItem, int listItemHeight);
147+
144 void createDelegateModel();
148 void createDelegateModel();
145
149
146 void layout();
150 void layout();
@@ -157,7 +161,7 @@ private:
157 void reallyReleaseItem(ListItem *item);
161 void reallyReleaseItem(ListItem *item);
158 void updateWatchedRoles();
162 void updateWatchedRoles();
159 QQuickItem *getSectionItem(int modelIndex, bool alreadyInserted);
163 QQuickItem *getSectionItem(int modelIndex, bool alreadyInserted);
160− QQuickItem *getSectionItem(int modelIndex, const QString &sectionText);
164+ QQuickItem *getSectionItem(const QString &sectionText);
161
165
162#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
166#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
163 QQuickVisualDataModel *m_delegateModel;
167 QQuickVisualDataModel *m_delegateModel;
plugins/Powerd/Powerd.cpp+2−2
@@ -30,7 +30,7 @@ Powerd::Powerd(QObject* parent)
30 powerd->connection().connect("com.canonical.powerd",
30 powerd->connection().connect("com.canonical.powerd",
31 "/com/canonical/powerd",
31 "/com/canonical/powerd",
32 "com.canonical.powerd",
32 "com.canonical.powerd",
33− "SysPowerStateChange",
33+ "DisplayPowerStateChange",
34 this,
34 this,
35− SIGNAL(powerStateChange(int)));
35+ SIGNAL(displayPowerStateChange(int, unsigned int)));
36}
36}
plugins/Powerd/Powerd.h+15−1
@@ -26,12 +26,26 @@
26class Powerd: public QObject
26class Powerd: public QObject
27{
27{
28 Q_OBJECT
28 Q_OBJECT
29+ Q_ENUMS(Status)
30+ Q_FLAGS(DisplayFlag DisplayFlags)
29
31
30public:
32public:
33+ enum DisplayFlag {
34+ UseProximity = 1, // Use proximity sensor to override screen state
35+ DisableAutoBrightness = 2, // Force autobrightness to be disabled
36+ Bright = 4, // Request the screen to stay bright
37+ };
38+ Q_DECLARE_FLAGS(DisplayFlags, DisplayFlag)
39+
40+ enum Status {
41+ Off,
42+ On,
43+ };
44+
31 explicit Powerd(QObject *parent = 0);
45 explicit Powerd(QObject *parent = 0);
32
46
33Q_SIGNALS:
47Q_SIGNALS:
34− void powerStateChange(int state);
48+ void displayPowerStateChange(int status, unsigned int flags);
35
49
36private:
50private:
37 QDBusInterface *powerd;
51 QDBusInterface *powerd;
plugins/Powerd/plugin.cpp+1−1
@@ -20,7 +20,7 @@
20#include "plugin.h"
20#include "plugin.h"
21#include "Powerd.h"
21#include "Powerd.h"
22
22
23−#include <QtQml/QtQml>
23+#include <QtQml/qqml.h>
24
24
25static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
26{
26{
plugins/Ubuntu/DownloadDaemonListener/plugin.cpp+1−1
@@ -22,7 +22,7 @@
22#include "plugin.h"
22#include "plugin.h"
23#include "DownloadTracker.h"
23#include "DownloadTracker.h"
24
24
25−#include <QtQml>
25+#include <QtQml/qqml.h>
26#include <QtQml/QQmlContext>
26#include <QtQml/QQmlContext>
27
27
28
28
plugins/Unity/CMakeLists.txt+15−1
@@ -2,6 +2,7 @@
2include(Plugins)
2include(Plugins)
3
3
4add_subdirectory(Indicators)
4add_subdirectory(Indicators)
5+add_subdirectory(IndicatorsLegacy)
5
6
6# Dependencies
7# Dependencies
7include(FindPkgConfig)
8include(FindPkgConfig)
@@ -17,11 +18,23 @@ include_directories(
17)
18)
18
19
19set(QMLPLUGIN_SRC
20set(QMLPLUGIN_SRC
21+ abstractfilteroption.cpp
20 scope.cpp
22 scope.cpp
21 scopes.cpp
23 scopes.cpp
22 categories.cpp
24 categories.cpp
25+ checkoptionfilter.cpp
26+ filter.cpp
27+ filteroption.cpp
28+ filters.cpp
29+ genericoptionsmodel.cpp
30+ ratingoptionsmodel.cpp
31+ unityoptionsmodel.cpp
23 categoryresults.cpp
32 categoryresults.cpp
24 plugin.cpp
33 plugin.cpp
34+ multirangefilter.cpp
35+ radiooptionfilter.cpp
36+ ratingsfilter.cpp
37+ ratingfilteroption.cpp
25 bottombarvisibilitycommunicatorshell.cpp
38 bottombarvisibilitycommunicatorshell.cpp
26 applicationpreview.cpp
39 applicationpreview.cpp
27 genericpreview.cpp
40 genericpreview.cpp
@@ -30,10 +43,11 @@ set(QMLPLUGIN_SRC
30 preview.cpp
43 preview.cpp
31 previewaction.cpp
44 previewaction.cpp
32 previewinfohint.cpp
45 previewinfohint.cpp
46+ signalslist.cpp
33 socialpreview.cpp
47 socialpreview.cpp
34 socialpreviewcomment.cpp
48 socialpreviewcomment.cpp
35 variantutils.cpp
49 variantutils.cpp
36− )
50+)
37
51
38add_library(Unity-qml MODULE
52add_library(Unity-qml MODULE
39 ${QMLPLUGIN_SRC}
53 ${QMLPLUGIN_SRC}
plugins/Unity/Indicators/CMakeLists.txt+13−10
@@ -3,22 +3,27 @@ include(Plugins)
3
3
4project(IndicatorsQml)
4project(IndicatorsQml)
5
5
6+pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
7+pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
6pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
8pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
9+pkg_check_modules(LIBUPSTART REQUIRED libupstart libnih libnih-dbus dbus-1)
10+
7find_package(Qt5Core REQUIRED)
11find_package(Qt5Core REQUIRED)
8find_package(Qt5Quick REQUIRED)
12find_package(Qt5Quick REQUIRED)
9
13
10add_subdirectory(qml)
14add_subdirectory(qml)
11−add_subdirectory(Messaging)
12add_subdirectory(Network)
15add_subdirectory(Network)
13
16
14include_directories(
17include_directories(
15 ${CMAKE_CURRENT_SOURCE_DIR}
18 ${CMAKE_CURRENT_SOURCE_DIR}
16 ${CMAKE_CURRENT_BINARY_DIR}
19 ${CMAKE_CURRENT_BINARY_DIR}
20+ ${GLIB_INCLUDE_DIRS}
21+ ${GIO_INCLUDE_DIRS}
17 ${QMENUMODEL_INCLUDE_DIRS}
22 ${QMENUMODEL_INCLUDE_DIRS}
23+ ${LIBUPSTART_INCLUDE_DIRS}
18)
24)
19
25
20set(IndicatorsQML_SOURCES
26set(IndicatorsQML_SOURCES
21− flatmenuproxymodel.cpp
22 indicator.cpp
27 indicator.cpp
23 indicators.h
28 indicators.h
24 indicatorsmanager.cpp
29 indicatorsmanager.cpp
@@ -26,24 +31,22 @@ set(IndicatorsQML_SOURCES
26 menucontentactivator.cpp
31 menucontentactivator.cpp
27 modelprinter.cpp
32 modelprinter.cpp
28 plugin.cpp
33 plugin.cpp
34+ rootactionstate.cpp
35+ unitymenumodelcache.cpp
36+ unitymenumodelstack.cpp
29)
37)
30add_definitions(-DUNITYINDICATORS_LIBRARY)
38add_definitions(-DUNITYINDICATORS_LIBRARY)
31
39
32−set(IndicatorsQML_RESOURCES
33− resources/indicators.qrc
34−)
35−
36−qt5_add_resources(IndicatorsQML_RESOURCES_RCC
37− ${IndicatorsQML_RESOURCES}
38−)
39
40
40add_library(IndicatorsQml SHARED
41add_library(IndicatorsQml SHARED
41− ${IndicatorsQML_RESOURCES_RCC}
42 ${IndicatorsQML_SOURCES}
42 ${IndicatorsQML_SOURCES}
43)
43)
44
44
45target_link_libraries(IndicatorsQml
45target_link_libraries(IndicatorsQml
46+ ${GLIB_LIBRARIES}
47+ ${GIO_LIBRARIES}
46 ${QMENUMODEL_LDFLAGS}
48 ${QMENUMODEL_LDFLAGS}
49+ ${LIBUPSTART_LIBRARIES}
47)
50)
48
51
49qt5_use_modules(IndicatorsQml Core Qml Quick)
52qt5_use_modules(IndicatorsQml Core Qml Quick)
plugins/Unity/Indicators/Messaging/qml/CMakeLists.txtdeleted−5
@@ -1,5 +0,0 @@
1−# export_qmlfiles macro
2−include(Plugins)
3−
4−# export the qmldir, *.qml files.
5−export_qmlfiles(Unity.Indicators.Messaging Unity/Indicators/Messaging)
plugins/Unity/Indicators/Network/plugin.cpp+1−1
@@ -17,7 +17,7 @@
17 */
17 */
18
18
19// Qt
19// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
21
21
22// self
22// self
23#include "plugin.h"
23#include "plugin.h"
plugins/Unity/Indicators/Network/qml/AccessPoint.qml+19−15
@@ -22,16 +22,25 @@ import Ubuntu.Components 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem
22import Ubuntu.Components.ListItems 0.1 as ListItem
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24
24
25−Indicators.MenuItem {
25+Indicators.FramedMenuItem {
26 id: accessPoint
26 id: accessPoint
27− property variant actionWifiApStrength : menu && actionGroup ? actionGroup.action(menu.extra.canonical_wifi_ap_strength_action) : null
28− property variant wifiApStrength : actionWifiApStrength && actionWifiApStrength.valid ? actionWifiApStrength.state : "0"
29
27
30− function getNetworkIcon(data) {
28+ property bool checked: false
29+ property bool secure: false
30+ property bool adHoc: false
31+ property int signalStrength: 0
32+
33+ signal activate()
34+
35+ onCheckedChanged: {
36+ // Can't rely on binding. Checked is assigned on click.
37+ checkBoxActive.checked = checked;
38+ }
39+
40+ icon: {
31 var imageName = "nm-signal-100"
41 var imageName = "nm-signal-100"
32− var signalStrength = parseInt(wifiApStrength)
33
42
34− if (data.extra.canonical_wifi_ap_is_adhoc) {
43+ if (adHoc) {
35 imageName = "nm-adhoc";
44 imageName = "nm-adhoc";
36 } else if (signalStrength == 0) {
45 } else if (signalStrength == 0) {
37 imageName = "nm-signal-00";
46 imageName = "nm-signal-00";
@@ -43,26 +52,21 @@ Indicators.MenuItem {
43 imageName = "nm-signal-75";
52 imageName = "nm-signal-75";
44 }
53 }
45
54
46− if (data.extra.canonical_wifi_ap_is_secure) {
55+ if (secure) {
47 imageName += "-secure";
56 imageName += "-secure";
48 }
57 }
49−
50 return "image://gicon/" + imageName;
58 return "image://gicon/" + imageName;
51 }
59 }
52
60
53− icon: menu && wifiApStrength ? getNetworkIcon(menu) : "image://gicon/wifi-none"
54 iconFrame: false
61 iconFrame: false
55 control: CheckBox {
62 control: CheckBox {
56 id: checkBoxActive
63 id: checkBoxActive
57 height: units.gu(4)
64 height: units.gu(4)
58 width: units.gu(4)
65 width: units.gu(4)
59 anchors.centerIn: parent
66 anchors.centerIn: parent
60− }
61
67
62− Indicators.MenuActionBinding {
68+ onClicked: {
63− actionGroup: accessPoint.actionGroup
69+ accessPoint.activate();
64− action: menu ? menu.action : ""
70+ }
65− target: checkBoxActive
66− property: "checked"
67 }
71 }
68}
72}
plugins/Unity/Indicators/Network/qml/PasswordPage.qml+3−3
@@ -38,10 +38,10 @@ Page {
38 }
38 }
39
39
40 Indicators.SectionMenuItem {
40 Indicators.SectionMenuItem {
41− label: "Authentication"
41+ text: "Authentication"
42 }
42 }
43
43
44− Indicators.MenuItem {
44+ Indicators.FramedMenuItem {
45 implicitHeight: password.height + units.gu(1)
45 implicitHeight: password.height + units.gu(1)
46
46
47 PasswordTextField {
47 PasswordTextField {
@@ -55,7 +55,7 @@ Page {
55 }
55 }
56 }
56 }
57
57
58− Indicators.MenuItem {
58+ Indicators.FramedMenuItem {
59 Row {
59 Row {
60 anchors {
60 anchors {
61 right: parent.right
61 right: parent.right
plugins/Unity/Indicators/Network/qml/qmldir+1−2
@@ -5,5 +5,4 @@ typeinfo plugin.qmltypes
5NetworkPage 0.1 NetworkPage.qml
5NetworkPage 0.1 NetworkPage.qml
6AccessPoint 0.1 AccessPoint.qml
6AccessPoint 0.1 AccessPoint.qml
7PasswordPage 0.1 PasswordPage.qml
7PasswordPage 0.1 PasswordPage.qml
8−PasswordTextField 0.1 PasswordTextField.qml
8+PasswordTextField 0.1 PasswordTextField.qml
9−WifiSection 0.1 WifiSection.qml
plugins/Unity/Indicators/indicatorsmanager.cpp+40−1
@@ -21,6 +21,9 @@
21#include <QSettings>
21#include <QSettings>
22#include <QDebug>
22#include <QDebug>
23
23
24+#include <upstart.h>
25+#include <nih/alloc.h>
26+
24#include "paths.h"
27#include "paths.h"
25
28
26
29
@@ -31,7 +34,8 @@ public:
31 : m_name(name)
34 : m_name(name)
32 , m_fileInfo(fileInfo)
35 , m_fileInfo(fileInfo)
33 , m_verified (true)
36 , m_verified (true)
34− {}
37+ {
38+ }
35
39
36 QString m_name;
40 QString m_name;
37 QFileInfo m_fileInfo;
41 QFileInfo m_fileInfo;
@@ -43,12 +47,34 @@ public:
43IndicatorsManager::IndicatorsManager(QObject* parent)
47IndicatorsManager::IndicatorsManager(QObject* parent)
44 : QObject(parent)
48 : QObject(parent)
45 , m_loaded(false)
49 , m_loaded(false)
50+ , m_upstart(NULL)
46{
51{
52+ auto upstartsession = qgetenv("UPSTART_SESSION");
53+ if (!upstartsession.isNull()) {
54+ DBusConnection * conn = NULL;
55+ conn = dbus_connection_open(upstartsession.constData(), NULL);
56+ if (conn != NULL) {
57+ m_upstart = nih_dbus_proxy_new(NULL, conn,
58+ NULL,
59+ DBUS_PATH_UPSTART,
60+ NULL, NULL);
61+ dbus_connection_unref(conn);
62+ }
63+ }
64+
65+ if (m_upstart != NULL) {
66+ m_upstart->auto_start = FALSE;
67+ }
47}
68}
48
69
49IndicatorsManager::~IndicatorsManager()
70IndicatorsManager::~IndicatorsManager()
50{
71{
51 unload();
72 unload();
73+
74+ if (m_upstart != NULL) {
75+ nih_unref(m_upstart, NULL);
76+ m_upstart = NULL;
77+ }
52}
78}
53
79
54void IndicatorsManager::load()
80void IndicatorsManager::load()
@@ -208,6 +234,19 @@ void IndicatorsManager::setLoaded(bool loaded)
208 {
234 {
209 m_loaded = loaded;
235 m_loaded = loaded;
210 Q_EMIT loadedChanged(m_loaded);
236 Q_EMIT loadedChanged(m_loaded);
237+
238+ if (m_upstart != NULL) {
239+ int event_sent = 0;
240+ if (m_loaded) {
241+ event_sent = upstart_emit_event_sync(NULL, m_upstart, "indicator-services-start", NULL, 0);
242+ } else {
243+ event_sent = upstart_emit_event_sync(NULL, m_upstart, "indicator-services-end", NULL, 0);
244+ }
245+
246+ if (event_sent != 0) {
247+ qWarning() << "Unable to send indicator event to Upstart";
248+ }
249+ }
211 }
250 }
212}
251}
213
252
plugins/Unity/Indicators/indicatorsmanager.h+3
@@ -28,6 +28,8 @@
28#include <QHash>
28#include <QHash>
29#include <QSharedPointer>
29#include <QSharedPointer>
30
30
31+typedef struct nih_dbus_proxy NihDBusProxy;
32+
31class UNITYINDICATORS_EXPORT IndicatorsManager : public QObject
33class UNITYINDICATORS_EXPORT IndicatorsManager : public QObject
32{
34{
33 Q_OBJECT
35 Q_OBJECT
@@ -69,6 +71,7 @@ private:
69 QHash<QString, IndicatorData*> m_indicatorsData;
71 QHash<QString, IndicatorData*> m_indicatorsData;
70 QSharedPointer<QFileSystemWatcher> m_fsWatcher;
72 QSharedPointer<QFileSystemWatcher> m_fsWatcher;
71 bool m_loaded;
73 bool m_loaded;
74+ NihDBusProxy * m_upstart;
72};
75};
73
76
74#endif // INDICATORS_MANAGER_H
77#endif // INDICATORS_MANAGER_H
plugins/Unity/Indicators/indicatorsmodel.cpp+2−2
@@ -229,9 +229,9 @@ QVariant IndicatorsModel::defaultData(Indicator::Ptr indicator, int role)
229 case IndicatorsModelRole::Description:
229 case IndicatorsModelRole::Description:
230 return "";
230 return "";
231 case IndicatorsModelRole::WidgetSource:
231 case IndicatorsModelRole::WidgetSource:
232− return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorWidget.qml";
232+ return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorWidget2.qml";
233 case IndicatorsModelRole::PageSource:
233 case IndicatorsModelRole::PageSource:
234− return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorPage.qml";
234+ return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorPage2.qml";
235 }
235 }
236 return QVariant();
236 return QVariant();
237}
237}
plugins/Unity/Indicators/modelprinter.cpp+66−43
@@ -20,6 +20,8 @@
20// self
20// self
21#include "modelprinter.h"
21#include "modelprinter.h"
22
22
23+#include <unitymenumodel.h>
24+
23// Qt
25// Qt
24#include <QTextStream>
26#include <QTextStream>
25
27
@@ -29,7 +31,7 @@ ModelPrinter::ModelPrinter(QObject *parent)
29{
31{
30}
32}
31
33
32−void ModelPrinter::setSourceModel(QAbstractItemModel * sourceModel)
34+void ModelPrinter::setSourceModel(UnityMenuModel * sourceModel)
33{
35{
34 if (m_model != NULL) {
36 if (m_model != NULL) {
35 disconnect(m_model);
37 disconnect(m_model);
@@ -40,75 +42,96 @@ void ModelPrinter::setSourceModel(QAbstractItemModel * sourceModel)
40 Q_EMIT textChanged();
42 Q_EMIT textChanged();
41 }
43 }
42 if (m_model != NULL) {
44 if (m_model != NULL) {
43− connect(m_model, SIGNAL(modelReset()), this, SIGNAL(textChanged()));
44− connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(textChanged()));
45+ connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(textChanged()));
45− connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(textChanged()));
46+ connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(textChanged()));
47+ connect(m_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SIGNAL(textChanged()));
46 }
48 }
47}
49}
48
50
49−QAbstractItemModel* ModelPrinter::sourceModel() const
51+UnityMenuModel* ModelPrinter::sourceModel() const
50{
52{
51 return m_model;
53 return m_model;
52}
54}
53
55
54−QString ModelPrinter::text(const QModelIndex& index) const
56+QString ModelPrinter::text()
55{
57{
56− return recurseString(index, 0);
58+ return getModelDataString(m_model, 0);
57}
59}
58
60
59QString tabify(int level) { QString str;
61QString tabify(int level) { QString str;
60 for (int i = 0; i < level; i++) {
62 for (int i = 0; i < level; i++) {
61− str += " ";
63+ str += " ";
62 }
64 }
63 return str;
65 return str;
64}
66}
65
67
66−QString ModelPrinter::recurseString(const QModelIndex& index, int level) const
68+QString ModelPrinter::getModelDataString(UnityMenuModel* sourceModel, int level)
67{
69{
68− if (!m_model)
70+ if (!sourceModel)
69 return "";
71 return "";
70
72
71 QString str;
73 QString str;
72 QTextStream stream(&str);
74 QTextStream stream(&str);
73
75
76+ int rowCount = sourceModel->rowCount();
77+ for (int row = 0; row < rowCount; row++) {
78+
79+ stream << getRowSring(sourceModel, row, level) << endl;
80+
81+ UnityMenuModel* childMenuModel = qobject_cast<UnityMenuModel*>(sourceModel->submenu(row));
82+ if (childMenuModel) {
83+
84+ if (!m_children.contains(childMenuModel)) {
85+ m_children << childMenuModel;
86+ connect(childMenuModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(textChanged()));
87+ connect(childMenuModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(textChanged()));
88+ connect(childMenuModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SIGNAL(textChanged()));
89+ }
90+ stream << getModelDataString(childMenuModel, level+1);
91+ }
92+ }
93+ return str;
94+}
95+
96+QString ModelPrinter::getRowSring(UnityMenuModel* sourceModel, int row, int depth) const
97+{
98+ QString str;
99+ QTextStream stream(&str);
100+
101+ // Print out this row
74− QHash<int, QByteArray> roleNames = m_model->roleNames();
102+ QHash<int, QByteArray> roleNames = sourceModel->roleNames();
75 QList<int> roles = roleNames.keys();
103 QList<int> roles = roleNames.keys();
76 qSort(roles);
104 qSort(roles);
105+
77 Q_FOREACH(int role, roles) {
106 Q_FOREACH(int role, roles) {
78− QVariant vData = m_model->data(index, role);
107+ const QByteArray& roleName = roleNames[role];
79− if (vData.canConvert(QMetaType::QVariantMap)) {
108+ stream << tabify(depth) << getVariantString(roleName, sourceModel->get(row, roleName));
80− QMapIterator<QString, QVariant> iter(vData.toMap());
81− while (iter.hasNext()) {
82− iter.next();
83− stream << tabify(level)
84− << roleNames[role]
85− << "."
86− << iter.key()
87− << ": "
88− << iter.value().toString()
89− << endl;
90− }
91− }
92− else {
93− stream << tabify(level)
94− << roleNames[role]
95− << ": "
96− << vData.toString()
97− << endl;
98− }
99 }
109 }
110+ return str;
111+}
100
112
101− int rowCount = m_model->rowCount(index);
113+QString ModelPrinter::getVariantString(const QString& roleName, const QVariant vData ) const
102− stream << tabify(level)
114+{
103− << "child count"
115+ QString str;
104− << ": "
116+ QTextStream stream(&str);
105− << rowCount
117+
106− << endl
118+ if (vData.canConvert(QMetaType::QVariantMap)) {
107− << endl;
119+ QMapIterator<QString, QVariant> iter(vData.toMap());
108−
120+ while (iter.hasNext()) {
109− for (int i = 0; i < rowCount; i++) {
121+ iter.next();
110− QModelIndex child = m_model->index(i, 0, index);
122+ stream << roleName
111− str += recurseString(child, level+1);
123+ << "."
124+ << iter.key()
125+ << ": "
126+ << iter.value().toString()
127+ << endl;
128+ }
129+ }
130+ else {
131+ stream << roleName
132+ << ": "
133+ << vData.toString()
134+ << endl;
112 }
135 }
113 return str;
136 return str;
114}
137}
plugins/Unity/Indicators/modelprinter.h+12−7
@@ -21,6 +21,7 @@
21#define MODELPRINTER_H
21#define MODELPRINTER_H
22
22
23#include <QSortFilterProxyModel>
23#include <QSortFilterProxyModel>
24+class UnityMenuModel;
24
25
25// This class acts as a namespace only, with the addition that its enums
26// This class acts as a namespace only, with the addition that its enums
26// are registered to be exposed on the QML side.
27// are registered to be exposed on the QML side.
@@ -28,23 +29,27 @@ class ModelPrinter : public QObject
28{
29{
29 Q_OBJECT
30 Q_OBJECT
30
31
31− Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setSourceModel NOTIFY modelChanged)
32+ Q_PROPERTY(UnityMenuModel* model READ sourceModel WRITE setSourceModel NOTIFY modelChanged)
32 Q_PROPERTY(QString text READ text NOTIFY textChanged)
33 Q_PROPERTY(QString text READ text NOTIFY textChanged)
33public:
34public:
34 ModelPrinter(QObject* parent=0);
35 ModelPrinter(QObject* parent=0);
35
36
36− void setSourceModel(QAbstractItemModel* sourceModel);
37+ void setSourceModel(UnityMenuModel* sourceModel);
37− QAbstractItemModel* sourceModel() const;
38+ UnityMenuModel* sourceModel() const;
38
39
39− Q_INVOKABLE QString text(const QModelIndex& index = QModelIndex()) const;
40+ Q_INVOKABLE QString text();
40
41
41Q_SIGNALS:
42Q_SIGNALS:
42 void modelChanged();
43 void modelChanged();
43 void textChanged();
44 void textChanged();
44
45
45private:
46private:
46− QString recurseString(const QModelIndex& index, int level) const;
47+ QString getModelDataString(UnityMenuModel* sourceModel, int level);
47− QAbstractItemModel* m_model;
48+ QString getRowSring(UnityMenuModel* sourceModel, int index, int depth) const;
49+ QString getVariantString(const QString& roleName, const QVariant vData ) const;
50+
51+ UnityMenuModel* m_model;
52+ QList<UnityMenuModel*> m_children;
48};
53};
49
54
50−#endif // MODELPRINTER_H
55+#endif // MODELPRINTER_H
plugins/Unity/Indicators/plugin.cpp+23−13
@@ -1,44 +1,54 @@
1/*
1/*
2− * Copyright (C) 2012 Canonical, Ltd.
2+ * Copyright 2013 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5+ * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
6 * the Free Software Foundation; version 3.
7 *
7 *
8 * This program is distributed in the hope that it will be useful,
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
11+ * GNU Lesser General Public License for more details.
12 *
12 *
13− * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
15 *
16+ * Authors:
16− * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ * Nick Dedekind <nick.dedekind@canonical.com>
17 */
18 */
18
19
19// Qt
20// Qt
20−#include <QtQml>
21+#include <QtQml/qqml.h>
21
22
22// self
23// self
23#include "plugin.h"
24#include "plugin.h"
24
25
25// local
26// local
26−#include "flatmenuproxymodel.h"
27+#include "indicators.h"
27#include "indicatorsmanager.h"
28#include "indicatorsmanager.h"
28#include "indicatorsmodel.h"
29#include "indicatorsmodel.h"
29−#include "indicators.h"
30#include "menucontentactivator.h"
30#include "menucontentactivator.h"
31#include "modelprinter.h"
31#include "modelprinter.h"
32+#include "rootactionstate.h"
33+#include "unitymenumodelcache.h"
34+#include "unitymenumodelstack.h"
32
35
33−void IndicatorsPlugin::registerTypes(const char *uri)
36+static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
34−{
37+ Q_UNUSED(engine);
35− Q_INIT_RESOURCE(indicators);
38+ Q_UNUSED(scriptEngine);
39+ return new UnityMenuModelCache;
40+}
36
41
42+void Indicators2Plugin::registerTypes(const char *uri)
43+{
37 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
44 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
38− qmlRegisterType<FlatMenuProxyModel>(uri, 0, 1, "FlatMenuProxyModel");
39 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
45 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
40− qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
41 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
46 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
47+ qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
48+ qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
49+ qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
50+
51+ qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
42
52
43 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
53 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
44 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
54 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
plugins/Unity/Indicators/plugin.h+10−9
@@ -1,27 +1,28 @@
1/*
1/*
2− * Copyright (C) 2012 Canonical, Ltd.
2+ * Copyright 2013 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5+ * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
6 * the Free Software Foundation; version 3.
7 *
7 *
8 * This program is distributed in the hope that it will be useful,
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
11+ * GNU Lesser General Public License for more details.
12 *
12 *
13− * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
15 *
16+ * Authors:
16− * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ * Nick Dedekind <nick.dedekind@canonical.com>
17 */
18 */
18
19
19−#ifndef INDICATORS_PLUGIN_H
20+#ifndef INDICATORS2_PLUGIN_H
20−#define INDICATORS_PLUGIN_H
21+#define INDICATORS2_PLUGIN_H
21
22
22#include <QtQml/QQmlExtensionPlugin>
23#include <QtQml/QQmlExtensionPlugin>
23
24
24−class IndicatorsPlugin : public QQmlExtensionPlugin
25+class Indicators2Plugin : public QQmlExtensionPlugin
25{
26{
26 Q_OBJECT
27 Q_OBJECT
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
@@ -29,4 +30,4 @@ public:
29 void registerTypes(const char *uri);
30 void registerTypes(const char *uri);
30};
31};
31
32
32−#endif // INDICATORS_PLUGIN_H
33+#endif // INDICATORS2_PLUGIN_H
plugins/Unity/Indicators/qml/BaseMenuItem.qmlnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
19+ */
20+
21+import QtQuick 2.0
22+import Ubuntu.Components.ListItems 0.1 as ListItem
23+
24+ListItem.Standard {
25+ id: baseMenu
26+
27+ property bool menuActivated: false
28+
29+ signal selectMenu()
30+ signal deselectMenu()
31+
32+ showDivider: false
33+ __foregroundColor: "#e8e1d0" // FIXME not in palette yet
34+
35+ backgroundIndicator: RemoveBackground {
36+ state: baseMenu.swipingState
37+ }
38+}
plugins/Unity/Indicators/qml/ButtonMenuItem.qml+9−12
@@ -15,33 +15,30 @@
15 *
15 *
16 * Authors:
16 * Authors:
17 * Renato Araujo Oliveira Filho <renato@canonical.com>
17 * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
18 */
19 */
19
20
20import QtQuick 2.0
21import QtQuick 2.0
21import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23+import Unity.Indicators 0.1 as Indicators
22
24
23−BasicMenuItem {
25+BaseMenuItem {
24− id: buttonMenu
26+ id: menuItem
25− color: "#221e1b"
26 implicitHeight: units.gu(7)
27 implicitHeight: units.gu(7)
27
28
29+ property alias text: button.text
30+
31+ signal activate()
32+
28 Button {
33 Button {
29 id: button
34 id: button
30− enabled: menuAction.valid
31− text: menu ? menu.label : ""
32 anchors.centerIn: parent
35 anchors.centerIn: parent
33 height: units.gu(4)
36 height: units.gu(4)
34 width: units.gu(16)
37 width: units.gu(16)
35 color: "#1b1817"
38 color: "#1b1817"
36
39
37 onClicked: {
40 onClicked: {
38− menuAction.activate()
41+ menuItem.activate();
39 }
42 }
40 }
43 }
41−
42− MenuAction {
43− id: menuAction
44− actionGroup: buttonMenu.actionGroup
45− action: menu ? menu.action : ""
46− }
47}
44}
plugins/Unity/Indicators/qml/CachedUnityMenuModel.qmlnew file+60
@@ -0,0 +1,60 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import QMenuModel 0.1 as QMenuModel
22+import Unity.Indicators 0.1 as Indicators
23+
24+// Make sure we don't duplicate models.
25+Item {
26+ id: cachedModel
27+ property string busName: ""
28+ property string actionsObjectPath: ""
29+ property string menuObjectPath: ""
30+
31+ readonly property bool ready: busName!=="" && actionsObjectPath!=="" && menuObjectPath!==""
32+
33+ property QtObject model: {
34+ if (!ready) {
35+ return null;
36+ }
37+
38+ var component = Indicators.UnityMenuModelCache.model(menuObjectPath);
39+ if (!component) {
40+ component = modelComponent.createObject(cachedModel);
41+ return component;
42+ }
43+ return component;
44+ }
45+
46+ Component {
47+ id: modelComponent
48+
49+ QMenuModel.UnityMenuModel {
50+ id: unityModel
51+ busName: cachedModel.busName
52+ actions: { "indicator": cachedModel.actionsObjectPath }
53+ menuObjectPath: cachedModel.menuObjectPath
54+
55+ Component.onCompleted: {
56+ Indicators.UnityMenuModelCache.registerModel(cachedModel.menuObjectPath, unityModel);
57+ }
58+ }
59+ }
60+}
plugins/Unity/Indicators/qml/DivMenuItem.qml+1−1
@@ -21,6 +21,6 @@ import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22
22
23Rectangle {
23Rectangle {
24− color: "#181514"
24+ color: "#181514" // FIXME not in palette yet
25 implicitHeight: units.gu(1)
25 implicitHeight: units.gu(1)
26}
26}
plugins/Unity/Indicators/qml/FramedMenuItem.qmlnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
19+ */
20+
21+import QtQuick 2.0
22+import Ubuntu.Components 0.1
23+import Ubuntu.Components.ListItems 0.1 as ListItem
24+
25+BaseMenuItem {
26+ implicitHeight: units.gu(7)
27+
28+ iconFrame: false
29+
30+ HLine {
31+ anchors.top: parent.top
32+ color: "#403b37" // FIXME not in palette yet
33+ }
34+
35+ HLine {
36+ anchors.bottom: parent.bottom
37+ color: "#060606" // FIXME not in palette yet
38+ }
39+}
plugins/Unity/Indicators/qml/IndicatorBase.qml+13−19
@@ -14,46 +14,40 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
15 *
16 * Authors:
16 * Authors:
17− * Renato Araujo Oliveira Filho <renato@canonical.com>
18 * Nick Dedekind <nick.dedekind@canonical.com>
17 * Nick Dedekind <nick.dedekind@canonical.com>
19 */
18 */
20
19
21import QtQuick 2.0
20import QtQuick 2.0
22−import QMenuModel 0.1
21+import QMenuModel 0.1 as QMenuModel
23import Unity.Indicators 0.1 as Indicators
22import Unity.Indicators 0.1 as Indicators
24
23
25Item {
24Item {
26 id: indicatorItem
25 id: indicatorItem
27
26
27+ // FIXME : should be disabled until bus available when we have desktop indicators
28+ // for now, disable when we dont habe the correct profile.
29+ enabled: menuObjectPaths.hasOwnProperty(device)
30+
28 //const
31 //const
29 property string title
32 property string title
30− property int busType
31 property string busName
33 property string busName
32 property string actionsObjectPath
34 property string actionsObjectPath
33 property var menuObjectPaths: undefined
35 property var menuObjectPaths: undefined
34 readonly property string device: "phone"
36 readonly property string device: "phone"
37+ property string rootMenuType: "com.canonical.indicator.root"
38+ property bool active: false
35
39
36 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
40 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
37
41
38 signal actionGroupUpdated()
42 signal actionGroupUpdated()
39 signal modelUpdated()
43 signal modelUpdated()
40
44
41− property var actionGroup: QDBusActionGroup {
45+ property alias menuModel: cachedModel.model
42− busType: indicatorItem.busType
43− busName: indicatorItem.busName
44− objectPath: indicatorItem.actionsObjectPath
45−
46− onActionAppear: indicatorItem.actionGroupUpdated();
47− }
48−
49− property var proxyModel: Indicators.FlatMenuProxyModel {
50− busType: indicatorItem.busType
51− busName: indicatorItem.busName
52− objectPath: indicatorItem.deviceMenuObjectPath
53
46
54− onStatusChanged: indicatorItem.modelUpdated();
47+ CachedUnityMenuModel {
55− onRowsInserted: indicatorItem.modelUpdated();
48+ id: cachedModel
56− onRowsRemoved: indicatorItem.modelUpdated();
49+ busName: active ? indicatorItem.busName : ""
57− onDataChanged: indicatorItem.modelUpdated();
50+ actionsObjectPath: active ? indicatorItem.actionsObjectPath : ""
51+ menuObjectPath: active ? indicatorItem.deviceMenuObjectPath : ""
58 }
52 }
59}
53}
plugins/Unity/Indicators/qml/IndicatorPage.qml+43−79
@@ -20,6 +20,7 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1 as Components
22import Ubuntu.Components 0.1 as Components
23+import Unity.Indicators 0.1 as Indicators
23
24
24IndicatorBase {
25IndicatorBase {
25 id: main
26 id: main
@@ -27,14 +28,16 @@ IndicatorBase {
27 //const
28 //const
28 property alias emptyText: emptyLabel.text
29 property alias emptyText: emptyLabel.text
29 property alias highlightFollowsCurrentItem : mainMenu.highlightFollowsCurrentItem
30 property alias highlightFollowsCurrentItem : mainMenu.highlightFollowsCurrentItem
30− property bool __active: false
31+
32+ Indicators.UnityMenuModelStack {
33+ id: menuStack
34+ head: main.menuModel
35+ }
31
36
32 ListView {
37 ListView {
33 id: mainMenu
38 id: mainMenu
39+ model: menuStack.tail ? menuStack.tail : null
34
40
35− property int visibleItems: 0
36−
37− model: proxyModel
38 anchors {
41 anchors {
39 fill: parent
42 fill: parent
40 bottomMargin: Qt.inputMethod.visible ? (Qt.inputMethod.keyboardRectangle.height - main.anchors.bottomMargin) : 0
43 bottomMargin: Qt.inputMethod.visible ? (Qt.inputMethod.keyboardRectangle.height - main.anchors.bottomMargin) : 0
@@ -59,95 +62,60 @@ IndicatorBase {
59
62
60 currentIndex: -1
63 currentIndex: -1
61 delegate: Item {
64 delegate: Item {
62− id: item
65+ id: menuDelegate
63− property bool ready: false
64− property alias empty: factory.empty
65
66
66 anchors {
67 anchors {
67 left: parent.left
68 left: parent.left
68 right: parent.right
69 right: parent.right
69 }
70 }
70− height: div.height + factory.implicitHeight
71+ height: loader.height
71 visible: height > 0
72 visible: height > 0
72
73
73− Component.onCompleted: {
74+ Loader {
74− if (!item.empty) {
75+ id: loader
75− mainMenu.visibleItems += 1;
76+ asynchronous: true
76− }
77− ready = true;
78− }
79− Component.onDestruction: {
80− if (!item.empty) {
81− mainMenu.visibleItems -= 1;
82− }
83− ready = false;
84− }
85
77
86− onEmptyChanged: {
78+ property int modelIndex: index
87− if (!ready) {
88− return;
89− }
90− if (empty) {
91− mainMenu.visibleItems -= 1;
92− } else {
93− mainMenu.visibleItems += 1;
94− }
95− }
96
79
97− ListView.onRemove: {
80+ anchors {
98− if (!highlightFollowsCurrentItem) {
81+ left: parent.left
99− mainMenu.currentIndex = -1;
82+ right: parent.right
100 }
83 }
101− }
102
84
103− Column {
85+ sourceComponent: factory.load(model)
104− id: contents
105−
106− anchors.fill: parent
107−
108− DivMenuItem {
109− id: div
110−
111− anchors.left: parent.left
112− anchors.right: parent.right
113− // only visible if is a root or a section header element and the factory is visible
114− height: {
115− // skipe first item
116− if (item.y == 0) {
117− return 0;
118− }
119− if (hasSection || ((depth == 0) && (!factory.empty))) {
120− return implicitHeight;
121− } else {
122− return 0;
123− }
124− }
125− }
126
86
127− MenuItemFactory {
87+ onLoaded: {
128− id: factory
88+ if (model.type === rootMenuType) {
129−
89+ menuStack.push(mainMenu.model.submenu(index));
130− actionGroup: main.actionGroup
131− isCurrentItem: item.ListView.isCurrentItem
132− anchors {
133− left: parent.left
134− right: parent.right
135 }
90 }
136− menu: model
137
91
138− Connections {
92+ if (item.hasOwnProperty("menuActivated")) {
139− target : factory
93+ item.menuActivated = Qt.binding(function() { return ListView.isCurrentItem; });
140− onActivated: item.ListView.view.currentIndex = index
94+ item.selectMenu.connect(function() { ListView.view.currentIndex = index });
141− onDeactivated: item.ListView.view.currentIndex = -1
95+ item.deselectMenu.connect(function() { ListView.view.currentIndex = -1 });
96+ }
97+ if (item.hasOwnProperty("menu")) {
98+ item.menu = Qt.binding(function() { return model; });
142 }
99 }
143 }
100 }
101+
102+ Binding {
103+ target: item ? item : null
104+ property: "objectName"
105+ value: model.action
106+ }
144 }
107 }
145 }
108 }
146 }
109 }
147
110
111+ MenuItemFactory {
112+ id: factory
113+ model: mainMenu.model
114+ }
115+
148 Components.Label {
116 Components.Label {
149 id: emptyLabel
117 id: emptyLabel
150− visible: (mainMenu.visibleItems === 0)
118+ visible: mainMenu.count == 0
151 anchors {
119 anchors {
152 top: parent.top
120 top: parent.top
153 left: parent.left
121 left: parent.left
@@ -167,19 +135,15 @@ IndicatorBase {
167 function start()
135 function start()
168 {
136 {
169 reset()
137 reset()
170− if (!__active) {
138+ if (!active) {
171− proxyModel.start();
139+ active = true;
172− actionGroup.start();
173− __active = true;
174 }
140 }
175 }
141 }
176
142
177 function stop()
143 function stop()
178 {
144 {
179− if (__active) {
145+ if (active) {
180− __active = false;
146+ active = false;
181− proxyModel.stop();
182− actionGroup.stop();
183 }
147 }
184 }
148 }
185
149
plugins/Unity/Indicators/qml/IndicatorWidget.qml+4−39
@@ -23,46 +23,11 @@ import Unity.Indicators 0.1 as Indicators
23IndicatorBase {
23IndicatorBase {
24 id: indicatorWidget
24 id: indicatorWidget
25
25
26−// enabled: false // FIXME : should be disabled until bus available when we have desktop indicators
26+ active: true
27− property var action: undefined
28− property bool started: (busType != 0) && (busName != "") && (deviceMenuObjectPath != "")
29 property int iconSize: height - units.gu(0.5)
27 property int iconSize: height - units.gu(0.5)
30− property string rootMenuType: "com.canonical.indicator.root"
31
28
32− signal actionStateChanged()
29+ property var actionState: Indicators.RootActionState {
33−
30+ menu: menuModel
34− onStartedChanged: {
31+ onUpdated: indicatorWidget.actionStateChanged()
35− if (started) {
36− proxyModel.start();
37− actionGroup.start();
38− } else {
39− proxyModel.stop();
40− actionGroup.stop();
41− }
42− }
43− onActionChanged: actionStateChanged()
44− onActionGroupUpdated: update_state()
45− onModelUpdated: update_state()
46−
47−
48− function update_state() {
49− var extra = proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Extra);
50− if (extra == undefined) {
51− return;
52− }
53−
54− if (!extra.hasOwnProperty("canonical_type")) {
55− return;
56− }
57−
58− if (extra.canonical_type === rootMenuType) {
59− action = actionGroup.action(proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Action));
60− }
61− }
62−
63− Connections {
64− target: action == undefined ? null : action
65− onStateChanged: actionStateChanged()
66− onValidChanged: actionStateChanged()
67 }
32 }
68}
33}
plugins/Unity/Indicators/qml/MenuItemFactory.qml+158−56
@@ -19,22 +19,13 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Unity.Indicators 0.1 as Indicators
21import Unity.Indicators 0.1 as Indicators
22−import Unity.Indicators.Messaging 0.1 as ICMessaging
23import Unity.Indicators.Network 0.1 as ICNetwork
22import Unity.Indicators.Network 0.1 as ICNetwork
23+import QMenuModel 0.1 as QMenuModel
24
24
25Item {
25Item {
26 id: menuFactory
26 id: menuFactory
27
27
28− property QtObject menu
28+ property var model: null
29− property QtObject listView
30− property QtObject actionGroup
31− property bool isCurrentItem
32− readonly property bool empty: (loader.item !== undefined && loader.status == Loader.Ready) ? loader.item.state === "EMPTY" : true
33−
34− implicitHeight: loader.height
35−
36− signal activated()
37− signal deactivated()
38
29
39 property var _map: {
30 property var _map: {
40 "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
31 "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
@@ -47,64 +38,175 @@ Item {
47 "com.canonical.indicator.slider" : sliderMenu,
38 "com.canonical.indicator.slider" : sliderMenu,
48 "com.canonical.indicator.switch" : switchMenu,
39 "com.canonical.indicator.switch" : switchMenu,
49
40
50− "com.canonical.indicator.messages.messageitem" : messageItem,
41+ "com.canonical.unity.slider" : sliderMenu,
51− "com.canonical.indicator.messages.snapdecision" : snapDecision,
42+ "com.canonical.unity.switch" : switchMenu,
52− "com.canonical.indicator.messages.sourceitem" : groupedMessage,
53
43
54− "unity.widget.systemsettings.tablet.sectiontitle" : wifiSection,
55 "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
44 "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
56 "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
45 "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
57 }
46 }
58
47
59− Component { id: sliderMenu; Indicators.SliderMenuItem {} }
48+ Component {
60− Component { id: switchMenu; Indicators.SwitchMenuItem {} }
49+ id: sliderMenu;
61− Component { id: buttonMenu; Indicators.ButtonMenuItem {} }
50+ Indicators.SliderMenuItem {
62− Component { id: divMenu; Indicators.DivMenuItem {} }
51+ property QtObject menu: null
63− Component { id: sectionMenu; Indicators.SectionMenuItem {} }
52+
64− Component { id: progressMenu; Indicators.ProgressMenuItem {} }
53+ text: menu && menu.label ? menu.label : ""
65− Component { id: messageItem; ICMessaging.MessageMenuItem {} }
54+ icon: menu ? menu.icon : ""
66− Component { id: snapDecision; ICMessaging.SnapDecision {} }
55+ minIcon: menu.ext.minIcon
67− Component { id: groupedMessage; ICMessaging.GroupedMessage {} }
56+ maxIcon: menu.ext.maxIcon
68− Component { id: wifiSection; ICNetwork.WifiSection {} }
57+
69− Component { id: accessPoint; ICNetwork.AccessPoint {} }
58+ minimumValue: menu.ext.minValue ? menu.ext.minValue : 0.0
70− Component { id: indicatorMenu; Indicators.MenuItem {} }
59+ maximumValue: {
71−
60+ var maximum = menu.ext.maxValue ? menu.ext.maxValue : 1.0
72− Loader {
61+ if (maximum <= minimumValue) {
73− id: loader
62+ return minimumValue + 1;
74− asynchronous: true
63+ }
75−
64+ return maximum;
76− anchors {
65+ }
77− left: parent.left
66+ value: menu ? menu.actionState : 0.0
78− right: parent.right
67+ enabled: menu ? menu.sensitive : false
79− }
68+
80−
69+ Component.onCompleted: {
81− sourceComponent: {
70+ model.loadExtendedAttributes(modelIndex, {'min-value': 'double',
82− if (!menuFactory.menu || !menuFactory.menu.extra) {
71+ 'max-value': 'double',
83− return undefined;
72+ 'min-icon': 'icon',
73+ 'max-icon': 'icon'});
84 }
74 }
85
75
86− var widgetType = menuFactory.menu.extra.canonical_type;
76+ // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
87− if (widgetType) {
77+ // reaching the boundaries (0.0 or 1.0), the value is converted
88− return _map[widgetType];
78+ // to an integer when automatically wrapped in a variant when
89− } else {
79+ // passed to QStateAction::updateState(…). The server chokes on
90− if (widgetType === "com.canonical.indicator.root") {
80+ // those values, complaining that they’re not of the right type…
91− return undefined;
81+ onChangeState: {
92− // Try discovery the item based on the basic properties
82+ if (value == Math.round(value)) {
93− } else if (menu.hasSection) {
83+ if (value >= maximumValue) {
94− return sectionMenu;
84+ model.changeState(modelIndex, maximumValue - 0.000001);
85+ } else if (value <= minimumValue) {
86+ model.changeState(modelIndex, minimumValue + 0.000001);
87+ } else {
88+ model.changeState(modelIndex, value * 1.000001);
89+ }
95 } else {
90 } else {
96− return indicatorMenu;
91+ model.changeState(modelIndex, value);
97 }
92 }
98 }
93 }
99 }
94 }
95+ }
96+
97+ Component { id: divMenu; Indicators.DivMenuItem {} }
98+
99+ Component {
100+ id: buttonMenu;
101+ Indicators.ButtonMenuItem {
102+ property QtObject menu: null
103+
104+ text: menu && menu.label ? menu.label : ""
105+ enabled: menu ? menu.sensitive : false
106+
107+ onActivate: model.activate(modelIndex);
108+ }
109+ }
110+ Component {
111+ id: sectionMenu;
112+ Indicators.SectionMenuItem {
113+ property QtObject menu: null
114+
115+ text: menu && menu.label ? menu.label : ""
116+ }
117+ }
118+
119+ Component {
120+ id: progressMenu;
121+ Indicators.ProgressMenuItem {
122+ property QtObject menu: null
123+
124+ text: menu && menu.label ? menu.label : ""
125+ icon: menu ? menu.icon : ""
126+ value : menu ? menu.actionState : 0.0
127+ }
128+ }
100
129
101− onLoaded: {
130+ Component {
102− item.menuActivated = Qt.binding(function() { return isCurrentItem; });
131+ id: standardMenu;
103− item.actionGroup = Qt.binding(function() { return menuFactory.actionGroup; });
132+ Indicators.StandardMenuItem {
104− item.menu = Qt.binding(function() { return menuFactory.menu; });
133+ property QtObject menu: null
105
134
106− item.activateMenu.connect(function() { menuFactory.activated(); });
135+ text: menu && menu.label ? menu.label : ""
107− item.deactivateMenu.connect(function() { menuFactory.deactivated(); });
136+ icon: menu ? menu.icon : ""
137+ checkable: menu ? (menu.isCheck || menu.isRadio) : false
138+ checked: checkable ? menu.isToggled : false
139+ enabled: menu ? menu.sensitive : false
140+
141+ onActivate: model.activate(modelIndex);
142+ }
143+ }
144+
145+ Component {
146+ id: switchMenu;
147+ Indicators.SwitchMenuItem {
148+ property QtObject menu: null
149+
150+ text: menu && menu.label ? menu.label : ""
151+ icon: menu ? menu.icon : ""
152+ checked: menu ? menu.isToggled : false
153+ enabled: menu ? menu.sensitive : false
154+
155+ onActivate: model.activate(modelIndex);
156+ }
157+ }
158+
159+ Component {
160+ id: wifiSection;
161+ Indicators.SectionMenuItem {
162+ property QtObject menu: null
163+
164+ text: menu && menu.label ? menu.label : ""
165+ busy: menu ? menu.ext.xCanonicalBusyAction : false
166+
167+ Component.onCompleted: {
168+ model.loadExtendedAttributes(modelIndex, {'x-canonical-busy-action': 'bool'});
169+ }
170+ }
171+ }
172+
173+ Component {
174+ id: accessPoint;
175+ ICNetwork.AccessPoint {
176+ property QtObject menu: null
177+// property var strenthAction: QMenuModel.UnityMenuAction {
178+// model: menuFactory.model ? menuFactory.model : null
179+// name: menu ? menu.ext.xCanonicalWifiApStrengthAction : ""
180+// }
181+
182+ text: menu && menu.label ? menu.label : ""
183+ icon: menu ? menu.icon : ""
184+ secure: menu ? menu.ext.xCanonicalWifiApIsSecure : false
185+ adHoc: menu ? menu.ext.xCanonicalWifiApIsAdhoc : false
186+ checked: menu ? menu.isToggled : false
187+// signalStrength: strenthAction.valid ? strenthAction.state : 0
188+ enabled: menu ? menu.sensitive : false
189+
190+ Component.onCompleted: {
191+ model.loadExtendedAttributes(modelIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
192+ 'x-canonical-wifi-ap-is-secure': 'bool',
193+ 'x-canonical-wifi-ap-strength-action': 'string'});
194+ }
195+ onActivate: model.activate(modelIndex);
196+ }
197+ }
198+
199+ function load(modelData) {
200+ if (modelData.type !== undefined) {
201+ var component = _map[modelData.type];
202+ if (component !== undefined) {
203+ return component;
204+ }
205+ } else {
206+ if (modelData.isSeparator) {
207+ return divMenu;
208+ }
108 }
209 }
210+ return standardMenu;
109 }
211 }
110}
212}
plugins/Unity/Indicators/qml/ProgressMenuItem.qml+11−38
@@ -19,47 +19,20 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22+import Unity.Indicators 0.1 as Indicators
22
23
23−MenuItem {
24+FramedMenuItem {
24− id: progressMenu
25+ id: menuItem
25− property alias value : progressBar.value
26
26
27− // TODO: Replace this with the official ProgressBar component as soon as
27+ property int value : 0.0
28− // it is available. For now, this rebuilds the Mockup as close as possible.
29− UbuntuShape {
30− id: progressBar
31− anchors {
32− right: parent.right
33− rightMargin: units.gu(2)
34− left: parent.left
35− leftMargin: units.gu(2)
36− }
37− height: units.gu(4)
38− anchors.verticalCenter: parent.verticalCenter
39− color: "transparent"
40−
41− property int minimumValue: 0
42− property int maximumValue: 100
43− property int value: 50
44−
45− UbuntuShape {
46− anchors.fill: parent
47− anchors.rightMargin: parent.width - (parent.width * parent.value / parent.maximumValue)
48− color: "#c94212"
49− }
50
28
51− Label {
29+ control: Label {
52− anchors.centerIn: parent
30+ id: progress
53− text: parent.value + " %"
31+ anchors.verticalCenter: parent.verticalCenter
54− fontSize: "medium"
32+ anchors.right: parent.right
55− color: "#e8e1d0"
56− }
57− }
58
33
59− MenuActionBinding {
34+ text: menuItem.value + " %"
60− actionGroup: progressMenu.actionGroup
35+ fontSize: "medium"
61− action: menu ? menu.action : ""
36+ color: "#e8e1d0"
62− target: progressBar
63− property: "value"
64 }
37 }
65}
38}
plugins/Unity/Indicators/qml/SectionMenuItem.qml+5−8
@@ -21,19 +21,16 @@ import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem
22import Ubuntu.Components.ListItems 0.1 as ListItem
23
23
24−BasicMenuItem {
24+BaseMenuItem {
25− property string label: menu && menu.label ? menu.label : ""
25+ id: menuItem
26+ property alias text: header.text
26 property bool busy: false
27 property bool busy: false
27− property int count: menu && menu.linkSection ? menu.linkSection.count : 0
28
28
29− color: "#221e1b"
29+ implicitHeight: text !== "" ? header.height : 0
30− implicitHeight: label !== "" ? __header.height : 0
31− state: count === 0 ? "EMPTY" : ""
32
30
33 ListItem.Header {
31 ListItem.Header {
34− id: __header
32+ id: header
35
33
36− text: label
37 height: units.gu(4)
34 height: units.gu(4)
38 anchors {
35 anchors {
39 left: parent.left
36 left: parent.left
plugins/Unity/Indicators/qml/SliderMenuItem.qml+75−34
@@ -15,50 +15,91 @@
15 *
15 *
16 * Authors:
16 * Authors:
17 * Renato Araujo Oliveira Filho <renato@canonical.com>
17 * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
18 */
19 */
19
20
20import QtQuick 2.0
21import QtQuick 2.0
21−import Ubuntu.Components 0.1
22+import Ubuntu.Components 0.1 as Components
23+import Unity.Indicators 0.1 as Indicators
22
24
23−MenuItem {
25+FramedMenuItem {
24− id: _sliderMenu
26+ id: menuItem
25
27
26 property alias minimumValue: slider.minimumValue
28 property alias minimumValue: slider.minimumValue
27 property alias maximumValue: slider.maximumValue
29 property alias maximumValue: slider.maximumValue
28− property alias value: slider.value
30+ property double value: 0.0
29−
31+
30− control: Slider {
32+ property alias minIcon: leftImage.source
31− id: slider
33+ property alias maxIcon: rightImage.source
32− enabled: menuAction.valid
34+
33− width: _sliderMenu.text ? units.gu(20) : _sliderMenu.width - units.gu(4)
35+ property QtObject d: QtObject {
34− anchors.verticalCenter: parent.verticalCenter
36+ property bool enableValueConnection: true
35− live: false
37+ }
36− // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
38+
37− // reaching the boundaries (0.0 or 1.0), the value is converted
39+ signal changeState(real value)
38− // to an integer when automatically wrapped in a variant when
40+
39− // passed to QStateAction::updateState(…). The server chokes on
41+ onValueChanged: {
40− // those values, complaining that they’re not of the right type…
42+ // TODO: look into adding a component to manage bi-directional bindings.
41− minimumValue: menu.extra.canonical_min ? menu.extra.canonical_min * 1.000001 : 0.0000001
43+ var oldEnable = d.enableValueConnection
42− maximumValue: {
44+ d.enableValueConnection = false;
43− var maximum = menu.extra.canonical_max ? menu.extra.canonical_max * 1.000001 : 0.9999999
45+
44− if (maximum <= minimumValue) {
46+ // Can't rely on binding. Slider value is assigned by user slide.
45− return minimumValue + 1;
47+ slider.value = value;
46− }
48+
47− return maximum;
49+ d.enableValueConnection = oldEnable;
50+ }
51+
52+ control: Item {
53+ id: row
54+ width: sliderMenu.text ? units.gu(20) : menuItem.width - (2 * menuItem.__contentsMargins)
55+ height: slider.height
56+
57+ Image {
58+ id: leftImage
59+ visible: source != ""
60+ anchors.left: row.left
61+ anchors.verticalCenter: row.verticalCenter
62+ height: units.gu(4)
63+ width: height
48 }
64 }
49
65
50− // FIXME - to be deprecated in Ubuntu.Components.
66+ Components.Slider {
51− // Use this to disable the label, since there is not way to do it on the component.
67+ id: slider
52− function formatValue(v) {
68+ anchors {
53− return "";
69+ left: leftImage.visible ? leftImage.right : row.left
70+ right: rightImage.visible ? rightImage.left : row.right
71+ leftMargin: leftImage.visible ? units.gu(0.5) : 0
72+ rightMargin: rightImage.visible ? units.gu(0.5) : 0
73+ }
74+ live: false
75+
76+ Component.onCompleted: {
77+ value = menuItem.value
78+ }
79+
80+ minimumValue: 0.0
81+ maximumValue: 0.1
82+
83+ // FIXME - to be deprecated in Ubuntu.Components.
84+ // Use this to disable the label, since there is not way to do it on the component.
85+ function formatValue(v) {
86+ return "";
87+ }
88+
89+ Connections {
90+ target: d.enableValueConnection ? slider : null
91+ onValueChanged: {
92+ menuItem.changeState(slider.value);
93+ }
94+ }
54 }
95 }
55− }
56
96
57− MenuActionBinding {
97+ Image {
58− id: menuAction
98+ id: rightImage
59− actionGroup: _sliderMenu.actionGroup
99+ anchors.right: row.right
60− action: menu ? menu.action : ""
100+ anchors.verticalCenter: row.verticalCenter
61− target: slider
101+ height: units.gu(4)
62− property: "value"
102+ width: height
103+ }
63 }
104 }
64}
105}
plugins/Unity/Indicators/qml/StandardMenuItem.qmlnew file+71
@@ -0,0 +1,71 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1 as Components
22+import Unity.Indicators 0.1 as Indicators
23+
24+FramedMenuItem {
25+ id: menuItem
26+
27+ property bool checkable: false
28+ property bool checked: false
29+
30+ signal activate()
31+
32+ // FIXME : need a radio button from sdk
33+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211866
34+ onCheckableChanged: {
35+ if (checkable) {
36+ icon = checkComponent.createObject(menuItem);
37+ }
38+ }
39+
40+ onCheckedChanged: {
41+ // Can't rely on binding. Checked is assigned on click.
42+ if (icon) {
43+ icon.checked = checked;
44+ }
45+ }
46+
47+ onClicked: {
48+ if (checkable && icon) {
49+ icon.clicked();
50+ } else {
51+ menuItem.activate();
52+ }
53+ }
54+
55+ property var checkComponent: Component {
56+ Components.CheckBox {
57+ anchors.verticalCenter: parent.verticalCenter
58+ anchors.left: parent.left
59+
60+ Component.onCompleted: {
61+ checked = menuItem.checked;
62+ }
63+
64+ // FIXME : should use Checkbox.toggled signal
65+ // lp:~nick-dedekind/ubuntu-ui-toolkit/checkbox.toggled
66+ onClicked: {
67+ menuItem.activate();
68+ }
69+ }
70+ }
71+}
plugins/Unity/Indicators/qml/SwitchMenuItem.qml+21−11
@@ -15,29 +15,39 @@
15 *
15 *
16 * Authors:
16 * Authors:
17 * Renato Araujo Oliveira Filho <renato@canonical.com>
17 * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
18 */
19 */
19
20
20import QtQuick 2.0
21import QtQuick 2.0
21import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23+import Unity.Indicators 0.1 as Indicators
22
24
23−MenuItem {
25+FramedMenuItem {
24− id: switchMenu
26+ id: menuItem
25
27
26− property alias checked: switcher.checked
28+ property bool checked: false
29+
30+ signal activate()
31+
32+ onCheckedChanged: {
33+ // Can't rely on binding. Checked is assigned on click.
34+ switcher.checked = checked;
35+ }
27
36
28 control: Switch {
37 control: Switch {
29 id: switcher
38 id: switcher
30− enabled: menuAction.valid
39+
40+ Component.onCompleted: {
41+ checked = menuItem.checked;
42+ }
31
43
32 anchors.verticalCenter: parent.verticalCenter
44 anchors.verticalCenter: parent.verticalCenter
33 anchors.right: parent.right
45 anchors.right: parent.right
34− }
35
46
36− MenuActionBinding {
47+ // FIXME : should use Checkbox.toggled signal
37− id: menuAction
48+ // lp:~nick-dedekind/ubuntu-ui-toolkit/checkbox.toggled
38− actionGroup: switchMenu.actionGroup
49+ onClicked: {
39− action: menu ? menu.action : ""
50+ menuItem.activate();
40− target: switcher
51+ }
41− property: "checked"
42 }
52 }
43}
53}
plugins/Unity/Indicators/qml/qmldir+5−3
@@ -2,20 +2,22 @@ module Unity.Indicators
2plugin IndicatorsQml
2plugin IndicatorsQml
3typeinfo plugin.qmltypes
3typeinfo plugin.qmltypes
4
4
5−BasicMenuItem 0.1 BasicMenuItem.qml
5+BaseMenuItem 0.1 BaseMenuItem.qml
6ButtonMenuItem 0.1 ButtonMenuItem.qml
6ButtonMenuItem 0.1 ButtonMenuItem.qml
7+CachedUnityMenuModel 0.1 CachedUnityMenuModel.qml
7DivMenuItem 0.1 DivMenuItem.qml
8DivMenuItem 0.1 DivMenuItem.qml
9+FramedMenuItem 0.1 FramedMenuItem.qml
8HLine 0.1 HLine.qml
10HLine 0.1 HLine.qml
9IndicatorBase 0.1 IndicatorBase.qml
11IndicatorBase 0.1 IndicatorBase.qml
10IndicatorPage 0.1 IndicatorPage.qml
12IndicatorPage 0.1 IndicatorPage.qml
11IndicatorWidget 0.1 IndicatorWidget.qml
13IndicatorWidget 0.1 IndicatorWidget.qml
12−MenuItem 0.1 MenuItem.qml
13−MenuAction 0.1 MenuAction.qml
14MenuActionBinding 0.1 MenuActionBinding.qml
14MenuActionBinding 0.1 MenuActionBinding.qml
15MenuItemFactory 0.1 MenuItemFactory.qml
15MenuItemFactory 0.1 MenuItemFactory.qml
16ProgressMenuItem 0.1 ProgressMenuItem.qml
16ProgressMenuItem 0.1 ProgressMenuItem.qml
17SectionMenuItem 0.1 SectionMenuItem.qml
17SectionMenuItem 0.1 SectionMenuItem.qml
18SliderMenuItem 0.1 SliderMenuItem.qml
18SliderMenuItem 0.1 SliderMenuItem.qml
19+StandardMenuItem 0.1 StandardMenuItem.qml
19SwitchMenuItem 0.1 SwitchMenuItem.qml
20SwitchMenuItem 0.1 SwitchMenuItem.qml
20
21
22+
21internal RemoveBackground RemoveBackground.qml
23internal RemoveBackground RemoveBackground.qml
plugins/Unity/Indicators/rootactionstate.cppnew file+249
@@ -0,0 +1,249 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "rootactionstate.h"
21+#include "indicators.h"
22+
23+#include <unitymenumodel.h>
24+#include <QVariant>
25+#include <QIcon>
26+
27+extern "C" {
28+#include <glib.h>
29+#include <gio/gio.h>
30+}
31+
32+RootActionState::RootActionState(QObject *parent)
33+ : ActionStateParser(parent),
34+ m_menu(NULL)
35+{
36+}
37+
38+RootActionState::~RootActionState()
39+{
40+}
41+
42+UnityMenuModel* RootActionState::menu() const
43+{
44+ return m_menu;
45+}
46+
47+void RootActionState::setMenu(UnityMenuModel* menu)
48+{
49+ if (m_menu != menu) {
50+ if (m_menu) {
51+ m_menu->disconnect(this);
52+ }
53+ m_menu = menu;
54+
55+ if (m_menu) {
56+ connect(m_menu, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(onModelRowsAdded(const QModelIndex&, int, int)));
57+ connect(m_menu, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(onModelRowsRemoved(const QModelIndex&, int, int)));
58+ connect(m_menu, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SLOT(onModelDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
59+
60+ m_menu->setActionStateParser(this);
61+ }
62+ updateActionState();
63+ Q_EMIT menuChanged();
64+ }
65+}
66+
67+void RootActionState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
68+{
69+ Q_UNUSED(parent);
70+ if (start == 0 && end >= 0) {
71+ updateActionState();
72+ }
73+}
74+
75+void RootActionState::onModelRowsRemoved(const QModelIndex& parent, int start, int end)
76+{
77+ Q_UNUSED(parent);
78+ if (start == 0 && end >= 0) {
79+ updateActionState();
80+ }
81+}
82+
83+void RootActionState::onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
84+{
85+ Q_UNUSED(roles);
86+ if (!topLeft.isValid() || !bottomRight.isValid()) {
87+ return;
88+ }
89+
90+ if (topLeft.row() <= 0 && bottomRight.row() >= 0) {
91+ updateActionState();
92+ }
93+}
94+
95+void RootActionState::updateActionState()
96+{
97+ if (m_menu && m_menu->rowCount() > 0) {
98+ m_cachedState = m_menu->get(0, "actionState").toMap();
99+ } else {
100+ m_cachedState.clear();
101+ }
102+ Q_EMIT updated();
103+}
104+
105+
106+bool RootActionState::isValid() const
107+{
108+ return m_menu && m_menu->rowCount() > 0;
109+}
110+
111+QString RootActionState::label() const
112+{
113+ if (!isValid()) return QString();
114+
115+ return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
116+}
117+
118+QString RootActionState::icon() const
119+{
120+ if (!isValid()) return QString();
121+
122+ return m_cachedState.value("icon", QVariant::fromValue(QString())).toString();
123+}
124+
125+QString RootActionState::accessibleName() const
126+{
127+ if (!isValid()) return QString();
128+
129+ return m_cachedState.value("accessible-name", QVariant::fromValue(QString())).toString();
130+}
131+
132+bool RootActionState::isVisible() const
133+{
134+ if (!isValid()) return false;
135+
136+ return m_cachedState.value("visible", QVariant::fromValue(true)).toBool();
137+}
138+
139+static QString iconUri(GIcon *icon)
140+{
141+ QString uri;
142+
143+ if (G_IS_THEMED_ICON (icon)) {
144+
145+ const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon));
146+ guint index = 0;
147+ while(iconNames[index] != NULL) {
148+ if (QIcon::hasThemeIcon(iconNames[index])) {
149+ uri = QString("image://theme/%1").arg(iconNames[index]);
150+ break;
151+ }
152+ index++;
153+ }
154+ }
155+ else if (G_IS_FILE_ICON (icon)) {
156+ GFile *file;
157+
158+ file = g_file_icon_get_file (G_FILE_ICON (icon));
159+ if (g_file_is_native (file)) {
160+ gchar *fileuri;
161+
162+ fileuri = g_file_get_path (file);
163+ uri = QString(fileuri);
164+
165+ g_free (fileuri);
166+ }
167+ }
168+ else if (G_IS_BYTES_ICON (icon)) {
169+ gsize size;
170+ gconstpointer data;
171+ gchar *base64;
172+
173+ data = g_bytes_get_data (g_bytes_icon_get_bytes (G_BYTES_ICON (icon)), &size);
174+ base64 = g_base64_encode ((const guchar *) data, size);
175+
176+ uri = QString("data://");
177+ uri.append (base64);
178+
179+ g_free (base64);
180+ }
181+
182+ return uri;
183+}
184+
185+QVariant RootActionState::toQVariant(GVariant* state) const
186+{
187+ if (!state) {
188+ return QVariant();
189+ }
190+
191+ if (g_variant_is_of_type(state, G_VARIANT_TYPE_VARDICT)) {
192+ GVariantIter iter;
193+ GVariant *vvalue;
194+ gchar *key;
195+ QVariantMap qmap;
196+
197+ g_variant_iter_init (&iter, state);
198+ while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
199+ {
200+ QString str = QString::fromUtf8(key);
201+ if (str == "icon") {
202+ // FIXME - should be sending a url.
203+ GIcon *icon = g_icon_deserialize (vvalue);
204+ if (icon) {
205+ qmap.insert(str, iconUri(icon));
206+ g_object_unref (icon);
207+ }
208+ else {
209+ qmap.insert(str, QVariant(""));
210+ }
211+ } else {
212+ qmap.insert(str, ActionStateParser::toQVariant(vvalue));
213+ }
214+ }
215+
216+ return QVariant::fromValue(qmap);
217+
218+ } else if (g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)"))) {
219+ QVariantMap qmap;
220+
221+ char* label;
222+ char* icon;
223+ char* accessible_name;
224+ gboolean visible;
225+ GIcon *gicon;
226+
227+ g_variant_get(state, "(sssb)", &label,
228+ &icon,
229+ &accessible_name,
230+ &visible);
231+
232+ qmap["label"] = label ? QString::fromUtf8(label) : "";
233+ qmap["accessible-name"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
234+ qmap["visible"] = visible;
235+
236+ gicon = g_icon_new_for_string (icon, NULL);
237+ if (gicon) {
238+ qmap["icon"] = iconUri(gicon);
239+ g_object_unref (gicon);
240+ }
241+
242+ if (label) g_free(label);
243+ if (icon) g_free(icon);
244+ if (accessible_name) g_free(accessible_name);
245+
246+ return QVariant::fromValue(qmap);
247+ }
248+ return ActionStateParser::toQVariant(state);
249+}
plugins/Unity/Indicators/rootactionstate.hnew 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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef ROOTACTIONSTATE_H
21+#define ROOTACTIONSTATE_H
22+
23+#include "unityindicatorsglobal.h"
24+
25+#include <actionstateparser.h>
26+
27+class UnityMenuModel;
28+
29+class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
30+{
31+ Q_OBJECT
32+ Q_PROPERTY(UnityMenuModel* menu READ menu WRITE setMenu NOTIFY menuChanged)
33+
34+ Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
35+ Q_PROPERTY(QString label READ label NOTIFY labelChanged)
36+ Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
37+ Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
38+ Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
39+public:
40+ RootActionState(QObject *parent = 0);
41+ virtual ~RootActionState();
42+
43+ UnityMenuModel* menu() const;
44+ void setMenu(UnityMenuModel* menu);
45+
46+ int index() const;
47+ void setIndex(int index);
48+
49+ bool isValid() const;
50+ QString label() const;
51+ QString icon() const;
52+ QString accessibleName() const;
53+ bool isVisible() const;
54+
55+ // from ActionStateParser
56+ virtual QVariant toQVariant(GVariant* state) const;
57+
58+Q_SIGNALS:
59+ void updated();
60+
61+ void menuChanged();
62+ void indexChanged();
63+
64+ void validChanged();
65+ void labelChanged();
66+ void iconChanged();
67+ void accessibleNameChanged();
68+ void visibleChanged();
69+
70+private Q_SLOTS:
71+ void onModelRowsAdded(const QModelIndex& parent, int start, int end);
72+ void onModelRowsRemoved(const QModelIndex& parent, int start, int end);
73+ void onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>&);
74+
75+private:
76+ void updateActionState();
77+
78+ UnityMenuModel* m_menu;
79+ QVariantMap m_cachedState;
80+};
81+
82+#endif // ROOTACTIONSTATE_H
plugins/Unity/Indicators/unitymenumodelcache.cppnew file+42
@@ -0,0 +1,42 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "unitymenumodelcache.h"
21+#include <unitymenumodel.h>
22+
23+UnityMenuModelCache::UnityMenuModelCache(QObject* parent)
24+ : QObject(parent)
25+{
26+}
27+
28+UnityMenuModelCache::~UnityMenuModelCache()
29+{
30+}
31+
32+
33+UnityMenuModel* UnityMenuModelCache::model(const QString& path) const
34+{
35+ return m_menuModels.value(path, NULL);
36+}
37+
38+void UnityMenuModelCache::registerModel(const QString& path, UnityMenuModel* menuModel)
39+{
40+ menuModel->setParent(this);
41+ m_menuModels[path] = menuModel;
42+}
plugins/Unity/Indicators/unitymenumodelcache.hnew file+44
@@ -0,0 +1,44 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef UNITYMENUMODELCACHE_H
21+#define UNITYMENUMODELCACHE_H
22+
23+#include "unityindicatorsglobal.h"
24+
25+#include <QObject>
26+#include <QHash>
27+
28+class UnityMenuModel;
29+
30+class UNITYINDICATORS_EXPORT UnityMenuModelCache : public QObject
31+{
32+ Q_OBJECT
33+public:
34+ UnityMenuModelCache(QObject*parent=NULL);
35+ ~UnityMenuModelCache();
36+
37+ Q_INVOKABLE UnityMenuModel* model(const QString& path) const;
38+ Q_INVOKABLE void registerModel(const QString& path, UnityMenuModel* menuModel);
39+
40+private:
41+ QHash<QString, UnityMenuModel*> m_menuModels;
42+};
43+
44+#endif // UNITYMENUMODELCACHE_H
plugins/Unity/Indicators/unitymenumodelstack.cppnew file+69
@@ -0,0 +1,69 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "unitymenumodelstack.h"
21+
22+UnityMenuModelStack::UnityMenuModelStack(QObject* parent)
23+ : QObject(parent)
24+{
25+}
26+
27+UnityMenuModelStack::~UnityMenuModelStack()
28+{
29+}
30+
31+UnityMenuModel* UnityMenuModelStack::head() const
32+{
33+ return !m_menuModels.isEmpty() ? m_menuModels.first() : NULL;
34+}
35+
36+void UnityMenuModelStack::setHead(UnityMenuModel* model)
37+{
38+ if (head() != model) {
39+ m_menuModels.clear();
40+ push(model);
41+ Q_EMIT headChanged(model);
42+ }
43+}
44+
45+UnityMenuModel* UnityMenuModelStack::tail() const
46+{
47+ return !m_menuModels.isEmpty() ? m_menuModels.last() : NULL;
48+}
49+
50+void UnityMenuModelStack::push(UnityMenuModel* model)
51+{
52+ m_menuModels << model;
53+ Q_EMIT tailChanged(model);
54+}
55+
56+UnityMenuModel* UnityMenuModelStack::pop()
57+{
58+ if (m_menuModels.isEmpty()) {
59+ return NULL;
60+ }
61+ UnityMenuModel* model = m_menuModels.takeLast();
62+
63+ Q_EMIT tailChanged(tail());
64+ if (m_menuModels.isEmpty()) {
65+ Q_EMIT headChanged(NULL);
66+ }
67+
68+ return model;
69+}
plugins/Unity/Indicators/unitymenumodelstack.hnew file+60
@@ -0,0 +1,60 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef UNITYMENUMODELSTACK_H
21+#define UNITYMENUMODELSTACK_H
22+
23+#include "unityindicatorsglobal.h"
24+
25+#include <QObject>
26+#include <QList>
27+
28+class UnityMenuModel;
29+
30+// A LIFO queue for storing the current submenu of a UnityMenuModel.
31+// The root menu model is set as the head, and each subsiquent submenu that is
32+// opened can be pushed onto the queue.
33+// The tail is set to the last item on the queue
34+// Popping the queue will remove the last entry, and the tail be updated to the last item.
35+class UNITYINDICATORS_EXPORT UnityMenuModelStack : public QObject
36+{
37+ Q_OBJECT
38+ Q_PROPERTY(UnityMenuModel* head READ head WRITE setHead NOTIFY headChanged)
39+ Q_PROPERTY(UnityMenuModel* tail READ tail NOTIFY tailChanged)
40+public:
41+ UnityMenuModelStack(QObject*parent=NULL);
42+ ~UnityMenuModelStack();
43+
44+ UnityMenuModel* head() const;
45+ void setHead(UnityMenuModel* model);
46+
47+ UnityMenuModel* tail() const;
48+
49+ Q_INVOKABLE void push(UnityMenuModel* model);
50+ Q_INVOKABLE UnityMenuModel* pop();
51+
52+Q_SIGNALS:
53+ void headChanged(UnityMenuModel* head);
54+ void tailChanged(UnityMenuModel* tail);
55+
56+private:
57+ QList<UnityMenuModel*> m_menuModels;
58+};
59+
60+#endif // UNITYMENUMODELSTACK_H
plugins/Unity/IndicatorsLegacy/CMakeLists.txtnew file+49
@@ -0,0 +1,49 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+project(IndicatorsLegacyQml)
5+
6+pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
7+find_package(Qt5Core REQUIRED)
8+find_package(Qt5Quick REQUIRED)
9+
10+add_subdirectory(qml)
11+add_subdirectory(Messaging)
12+add_subdirectory(Network)
13+
14+include_directories(
15+ ${CMAKE_CURRENT_SOURCE_DIR}
16+ ${CMAKE_CURRENT_BINARY_DIR}
17+ ${QMENUMODEL_INCLUDE_DIRS}
18+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators
19+)
20+
21+set(IndicatorsLegacyQML_SOURCES
22+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
23+ flatmenuproxymodel.cpp
24+ modelprinter.cpp
25+ plugin.cpp
26+)
27+add_definitions(-DUNITYINDICATORS_LIBRARY)
28+
29+set(IndicatorsLegacyQML_RESOURCES
30+ resources/indicators.qrc
31+)
32+
33+qt5_add_resources(IndicatorsLegacyQML_RESOURCES_RCC
34+ ${IndicatorsLegacyQML_RESOURCES}
35+)
36+
37+add_library(IndicatorsLegacyQml SHARED
38+ ${IndicatorsLegacyQML_RESOURCES_RCC}
39+ ${IndicatorsLegacyQML_SOURCES}
40+)
41+
42+target_link_libraries(IndicatorsLegacyQml
43+ ${QMENUMODEL_LDFLAGS}
44+)
45+
46+qt5_use_modules(IndicatorsLegacyQml Core Qml Quick)
47+
48+# export the qmltypes and plugin files
49+export_qmlplugin(Unity.IndicatorsLegacy 0.1 Unity/IndicatorsLegacy TARGETS IndicatorsLegacyQml)
plugins/Unity/Indicators/Messaging/CMakeLists.txt → plugins/Unity/IndicatorsLegacy/Messaging/CMakeLists.txtrenamed

The content is the same.

plugins/Unity/Indicators/Messaging/qml/ActionButton.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/ActionButton.qmlrenamed+1−1
@@ -20,7 +20,7 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24
24
25Button {
25Button {
26 id: button
26 id: button
plugins/Unity/Indicators/Messaging/qml/ActionTextField.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/ActionTextField.qmlrenamed

The content is the same.

plugins/Unity/IndicatorsLegacy/Messaging/qml/CMakeLists.txtnew file+5
@@ -0,0 +1,5 @@
1+# export_qmlfiles macro
2+include(Plugins)
3+
4+# export the qmldir, *.qml files.
5+export_qmlfiles(Unity.IndicatorsLegacy.Messaging Unity/IndicatorsLegacy/Messaging)
plugins/Unity/Indicators/Messaging/qml/GroupedMessage.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/GroupedMessage.qmlrenamed+1−1
@@ -20,7 +20,7 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24
24
25Indicators.BasicMenuItem {
25Indicators.BasicMenuItem {
26 id: groupedMessage
26 id: groupedMessage
plugins/Unity/Indicators/Messaging/qml/HeroMessage.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/HeroMessage.qmlrenamed+1−1
@@ -20,7 +20,7 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24
24
25Indicators.BasicMenuItem {
25Indicators.BasicMenuItem {
26 id: heroMessage
26 id: heroMessage
plugins/Unity/Indicators/Messaging/qml/HeroMessageHeader.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/HeroMessageHeader.qmlrenamed

The content is the same.

plugins/Unity/Indicators/Messaging/qml/MessageMenuItem.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/MessageMenuItem.qmlrenamed+1−1
@@ -20,7 +20,7 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24
24
25Indicators.BasicMenuItem {
25Indicators.BasicMenuItem {
26 property variant actionsDescription: menu ? menu.extra.canonical_message_actions : undefined
26 property variant actionsDescription: menu ? menu.extra.canonical_message_actions : undefined
plugins/Unity/Indicators/Messaging/qml/QuickReply.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/QuickReply.qmlrenamed+1−1
@@ -20,7 +20,7 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24
24
25Item {
25Item {
26 property alias actionGroup: actionTextField.actionGroup
26 property alias actionGroup: actionTextField.actionGroup
plugins/Unity/Indicators/Messaging/qml/SimpleTextMessage.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/SimpleTextMessage.qmlrenamed

The content is the same.

plugins/Unity/Indicators/Messaging/qml/SnapDecision.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/SnapDecision.qmlrenamed+1−1
@@ -20,7 +20,7 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24import "utils.js" as Utils
24import "utils.js" as Utils
25
25
26HeroMessage {
26HeroMessage {
plugins/Unity/Indicators/Messaging/qml/TextMessage.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/TextMessage.qmlrenamed+1−1
@@ -20,7 +20,7 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24
24
25SimpleTextMessage {
25SimpleTextMessage {
26 id: simpleMessage
26 id: simpleMessage
plugins/Unity/Indicators/Messaging/qml/qmldir → plugins/Unity/IndicatorsLegacy/Messaging/qml/qmldirrenamed+1−1
@@ -1,4 +1,4 @@
1−module Unity.Indicators.Messaging
1+module Unity.IndicatorsLegacy.Messaging
2
2
3ActionButton 0.1 ActionButton.qml
3ActionButton 0.1 ActionButton.qml
4ActionTextField 0.1 ActionTextField.qml
4ActionTextField 0.1 ActionTextField.qml
plugins/Unity/Indicators/Messaging/qml/utils.js → plugins/Unity/IndicatorsLegacy/Messaging/qml/utils.jsrenamed

The content is the same.

plugins/Unity/IndicatorsLegacy/Network/CMakeLists.txtnew file+45
@@ -0,0 +1,45 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+project(IndicatorsLegacyNetworkQml)
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_LEGACY_NETWORK_SRC
12+ networkagent.cpp
13+ plugin.cpp
14+ secret-agent.c
15+ )
16+
17+ add_library(IndicatorsLegacyNetworkQml MODULE
18+ ${INDICATORS_LEGACY_NETWORK_SRC}
19+ )
20+
21+ qt5_use_modules(IndicatorsLegacyNetworkQml Core Qml Widgets)
22+
23+ set_target_properties(IndicatorsLegacyNetworkQml 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(IndicatorsLegacyNetworkQml
33+ ${GLIB_LIBRARIES}
34+ ${LIBNM_GLIB_LIBRARIES}
35+ ${LIBNM_UTIL_LIBRARIES}
36+ )
37+
38+ # export the qmltypes and plugin files
39+ export_qmlplugin(Unity.IndicatorsLegacy.Network 0.1 Unity/IndicatorsLegacy/Network TARGETS IndicatorsLegacyNetworkQml)
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/IndicatorsLegacy/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/IndicatorsLegacy/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/IndicatorsLegacy/Network/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2012 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+// Qt
20+#include <QtQml/qqml.h>
21+
22+// self
23+#include "plugin.h"
24+
25+// local
26+#include "networkagent.h"
27+
28+void IndicatorsNetworkPlugin::registerTypes(const char *uri)
29+{
30+ qmlRegisterType<NetworkAgent>(uri, 0, 1, "NetworkAgent");
31+}
plugins/Unity/IndicatorsLegacy/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/IndicatorsLegacy/Network/qml/AccessPoint.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+import Ubuntu.Components.ListItems 0.1 as ListItem
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24+
25+Indicators.MenuItem {
26+ id: accessPoint
27+ property variant actionWifiApStrength : menu && actionGroup ? actionGroup.action(menu.extra.canonical_wifi_ap_strength_action) : null
28+ property variant wifiApStrength : actionWifiApStrength && actionWifiApStrength.valid ? actionWifiApStrength.state : "0"
29+
30+ function getNetworkIcon(data) {
31+ var imageName = "nm-signal-100"
32+ var signalStrength = parseInt(wifiApStrength)
33+
34+ if (data.extra.canonical_wifi_ap_is_adhoc) {
35+ imageName = "nm-adhoc";
36+ } else if (signalStrength == 0) {
37+ imageName = "nm-signal-00";
38+ } else if (signalStrength <= 25) {
39+ imageName = "nm-signal-25";
40+ } else if (signalStrength <= 50) {
41+ imageName = "nm-signal-50";
42+ } else if (signalStrength <= 75) {
43+ imageName = "nm-signal-75";
44+ }
45+
46+ if (data.extra.canonical_wifi_ap_is_secure) {
47+ imageName += "-secure";
48+ }
49+
50+ return "image://gicon/" + imageName;
51+ }
52+
53+ icon: menu && wifiApStrength ? getNetworkIcon(menu) : "image://gicon/wifi-none"
54+ iconFrame: false
55+ control: CheckBox {
56+ id: checkBoxActive
57+ height: units.gu(4)
58+ width: units.gu(4)
59+ anchors.centerIn: parent
60+ }
61+
62+ Indicators.MenuActionBinding {
63+ actionGroup: accessPoint.actionGroup
64+ action: menu ? menu.action : ""
65+ target: checkBoxActive
66+ property: "checked"
67+ }
68+}
plugins/Unity/IndicatorsLegacy/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.IndicatorsLegacy.Network Unity/IndicatorsLegacy/Network)
plugins/Unity/IndicatorsLegacy/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.IndicatorsLegacy 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.MenuItem {
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.MenuItem {
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/IndicatorsLegacy/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.qml → plugins/Unity/IndicatorsLegacy/Network/qml/WifiSection.qmlrenamed+1−1
@@ -19,7 +19,7 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
23
23
24Indicators.SectionMenuItem {
24Indicators.SectionMenuItem {
25 id: wifiSectionMenu
25 id: wifiSectionMenu
plugins/Unity/IndicatorsLegacy/Network/qml/qmldirnew file+9
@@ -0,0 +1,9 @@
1+module Unity.IndicatorsLegacy.Network
2+plugin IndicatorsLegacyNetworkQml
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/IndicatorsLegacy/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/IndicatorsLegacy/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/flatmenuproxymodel.cpp → plugins/Unity/IndicatorsLegacy/flatmenuproxymodel.cpprenamed+13
@@ -80,6 +80,19 @@ void FlatMenuProxyModel::setSourceModel(QAbstractItemModel *source)
80 SIGNAL(modelReset()),
80 SIGNAL(modelReset()),
81 SLOT(onModelReset()), Qt::QueuedConnection);
81 SLOT(onModelReset()), Qt::QueuedConnection);
82
82
83+ //Connection args
84+ connect(source,
85+ SIGNAL(busTypeChanged(DBusEnums::BusType)),
86+ SIGNAL(busTypeChanged()));
87+
88+ connect(source,
89+ SIGNAL(busNameChanged(const QString&)),
90+ SIGNAL(busNameChanged()));
91+
92+ connect(source,
93+ SIGNAL(objectPathChanged(const QString&)),
94+ SIGNAL(objectPathChanged()));
95+
83 connect(source,
96 connect(source,
84 SIGNAL(statusChanged(DBusEnums::ConnectionStatus)),
97 SIGNAL(statusChanged(DBusEnums::ConnectionStatus)),
85 SIGNAL(statusChanged()), Qt::QueuedConnection);
98 SIGNAL(statusChanged()), Qt::QueuedConnection);
plugins/Unity/Indicators/flatmenuproxymodel.h → plugins/Unity/IndicatorsLegacy/flatmenuproxymodel.hrenamed

The content is the same.

plugins/Unity/IndicatorsLegacy/modelprinter.cppnew file+114
@@ -0,0 +1,114 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+// self
21+#include "modelprinter.h"
22+
23+// Qt
24+#include <QTextStream>
25+
26+ModelPrinter::ModelPrinter(QObject *parent)
27+ : QObject(parent)
28+ , m_model(NULL)
29+{
30+}
31+
32+void ModelPrinter::setSourceModel(QAbstractItemModel * sourceModel)
33+{
34+ if (m_model != NULL) {
35+ disconnect(m_model);
36+ }
37+ if (m_model != sourceModel) {
38+ m_model = sourceModel;
39+ Q_EMIT modelChanged();
40+ Q_EMIT textChanged();
41+ }
42+ if (m_model != NULL) {
43+ connect(m_model, SIGNAL(modelReset()), this, SIGNAL(textChanged()));
44+ connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(textChanged()));
45+ connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(textChanged()));
46+ }
47+}
48+
49+QAbstractItemModel* ModelPrinter::sourceModel() const
50+{
51+ return m_model;
52+}
53+
54+QString ModelPrinter::text(const QModelIndex& index) const
55+{
56+ return recurseString(index, 0);
57+}
58+
59+QString tabify(int level) { QString str;
60+ for (int i = 0; i < level; i++) {
61+ str += " ";
62+ }
63+ return str;
64+}
65+
66+QString ModelPrinter::recurseString(const QModelIndex& index, int level) const
67+{
68+ if (!m_model)
69+ return "";
70+
71+ QString str;
72+ QTextStream stream(&str);
73+
74+ QHash<int, QByteArray> roleNames = m_model->roleNames();
75+ QList<int> roles = roleNames.keys();
76+ qSort(roles);
77+ Q_FOREACH(int role, roles) {
78+ QVariant vData = m_model->data(index, role);
79+ if (vData.canConvert(QMetaType::QVariantMap)) {
80+ QMapIterator<QString, QVariant> iter(vData.toMap());
81+ while (iter.hasNext()) {
82+ iter.next();
83+ stream << tabify(level)
84+ << roleNames[role]
85+ << "."
86+ << iter.key()
87+ << ": "
88+ << iter.value().toString()
89+ << endl;
90+ }
91+ }
92+ else {
93+ stream << tabify(level)
94+ << roleNames[role]
95+ << ": "
96+ << vData.toString()
97+ << endl;
98+ }
99+ }
100+
101+ int rowCount = m_model->rowCount(index);
102+ stream << tabify(level)
103+ << "child count"
104+ << ": "
105+ << rowCount
106+ << endl
107+ << endl;
108+
109+ for (int i = 0; i < rowCount; i++) {
110+ QModelIndex child = m_model->index(i, 0, index);
111+ str += recurseString(child, level+1);
112+ }
113+ return str;
114+}
plugins/Unity/IndicatorsLegacy/modelprinter.hnew file+50
@@ -0,0 +1,50 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef MODELPRINTER_H
21+#define MODELPRINTER_H
22+
23+#include <QSortFilterProxyModel>
24+
25+// This class acts as a namespace only, with the addition that its enums
26+// are registered to be exposed on the QML side.
27+class ModelPrinter : public QObject
28+{
29+ Q_OBJECT
30+
31+ Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setSourceModel NOTIFY modelChanged)
32+ Q_PROPERTY(QString text READ text NOTIFY textChanged)
33+public:
34+ ModelPrinter(QObject* parent=0);
35+
36+ void setSourceModel(QAbstractItemModel* sourceModel);
37+ QAbstractItemModel* sourceModel() const;
38+
39+ Q_INVOKABLE QString text(const QModelIndex& index = QModelIndex()) const;
40+
41+Q_SIGNALS:
42+ void modelChanged();
43+ void textChanged();
44+
45+private:
46+ QString recurseString(const QModelIndex& index, int level) const;
47+ QAbstractItemModel* m_model;
48+};
49+
50+#endif // MODELPRINTER_H
plugins/Unity/IndicatorsLegacy/plugin.cppnew file+42
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright (C) 2012 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+// Qt
20+#include <QtQml/qqml.h>
21+
22+// self
23+#include "plugin.h"
24+
25+// local
26+#include "flatmenuproxymodel.h"
27+#include "indicators.h"
28+#include "modelprinter.h"
29+
30+void IndicatorsPlugin::registerTypes(const char *uri)
31+{
32+ Q_INIT_RESOURCE(indicators);
33+
34+ qmlRegisterType<FlatMenuProxyModel>(uri, 0, 1, "FlatMenuProxyModel");
35+ qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
36+
37+ qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
38+ qmlRegisterUncreatableType<NetworkActionState>(uri, 0, 1, "NetworkActionState", "Can't create NetworkActionState class");
39+ qmlRegisterUncreatableType<NetworkConnection>(uri, 0, 1, "NetworkConnection", "Can't create NetworkConnection class");
40+ qmlRegisterUncreatableType<IndicatorsModelRole>(uri, 0, 1, "IndicatorsModelRole", "Can't create IndicatorsModelRole class");
41+ qmlRegisterUncreatableType<FlatMenuProxyModelRole>(uri, 0, 1, "FlatMenuProxyModelRole", "Can't create FlatMenuProxyModelRole class");
42+}
plugins/Unity/IndicatorsLegacy/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_PLUGIN_H
20+#define INDICATORS_PLUGIN_H
21+
22+#include <QtQml/QQmlExtensionPlugin>
23+
24+class IndicatorsPlugin : 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_PLUGIN_H
plugins/Unity/Indicators/qml/BasicMenuItem.qml → plugins/Unity/IndicatorsLegacy/qml/BasicMenuItem.qmlrenamed

The content is the same.

plugins/Unity/IndicatorsLegacy/qml/ButtonMenuItem.qmlnew file+47
@@ -0,0 +1,47 @@
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+BasicMenuItem {
24+ id: buttonMenu
25+ color: "#221e1b"
26+ implicitHeight: units.gu(7)
27+
28+ Button {
29+ id: button
30+ enabled: menuAction.valid
31+ text: menu ? menu.label : ""
32+ anchors.centerIn: parent
33+ height: units.gu(4)
34+ width: units.gu(16)
35+ color: "#1b1817"
36+
37+ onClicked: {
38+ menuAction.activate()
39+ }
40+ }
41+
42+ MenuAction {
43+ id: menuAction
44+ actionGroup: buttonMenu.actionGroup
45+ action: menu ? menu.action : ""
46+ }
47+}
plugins/Unity/IndicatorsLegacy/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.IndicatorsLegacy Unity/IndicatorsLegacy)
plugins/Unity/IndicatorsLegacy/qml/DivMenuItem.qmlnew file+26
@@ -0,0 +1,26 @@
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+ * Olivier Tilloy <olivier.tilloy@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+
23+Rectangle {
24+ color: "#181514"
25+ implicitHeight: units.gu(1)
26+}
plugins/Unity/IndicatorsLegacy/qml/HLine.qmlnew file+29
@@ -0,0 +1,29 @@
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+Rectangle {
24+ height: units.dp(1)
25+ anchors {
26+ left: parent.left
27+ right: parent.right
28+ }
29+}
plugins/Unity/IndicatorsLegacy/qml/IndicatorBase.qmlnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
19+ */
20+
21+import QtQuick 2.0
22+import QMenuModel 0.1
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24+
25+Item {
26+ id: indicatorItem
27+
28+ //const
29+ property string title
30+ property int busType
31+ property string busName
32+ property string actionsObjectPath
33+ property var menuObjectPaths: undefined
34+ readonly property string device: "phone"
35+
36+ property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
37+
38+ signal actionGroupUpdated()
39+ signal modelUpdated()
40+
41+ property var actionGroup: QDBusActionGroup {
42+ busType: indicatorItem.busType
43+ busName: indicatorItem.busName
44+ objectPath: indicatorItem.actionsObjectPath
45+
46+ onActionAppear: indicatorItem.actionGroupUpdated();
47+ }
48+
49+ property var proxyModel: Indicators.FlatMenuProxyModel {
50+ busType: indicatorItem.busType
51+ busName: indicatorItem.busName
52+ objectPath: indicatorItem.deviceMenuObjectPath
53+
54+ onStatusChanged: indicatorItem.modelUpdated();
55+ onRowsInserted: indicatorItem.modelUpdated();
56+ onRowsRemoved: indicatorItem.modelUpdated();
57+ onDataChanged: indicatorItem.modelUpdated();
58+ }
59+}
plugins/Unity/IndicatorsLegacy/qml/IndicatorPage.qmlnew file+191
@@ -0,0 +1,191 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
19+ */
20+
21+import QtQuick 2.0
22+import Ubuntu.Components 0.1 as Components
23+
24+IndicatorBase {
25+ id: main
26+
27+ //const
28+ property alias emptyText: emptyLabel.text
29+ property alias highlightFollowsCurrentItem : mainMenu.highlightFollowsCurrentItem
30+ property bool __active: false
31+
32+ ListView {
33+ id: mainMenu
34+
35+ property int visibleItems: 0
36+
37+ model: proxyModel
38+ anchors {
39+ fill: parent
40+ bottomMargin: Qt.inputMethod.visible ? (Qt.inputMethod.keyboardRectangle.height - main.anchors.bottomMargin) : 0
41+
42+ Behavior on bottomMargin {
43+ NumberAnimation {
44+ duration: 175
45+ easing.type: Easing.OutQuad
46+ }
47+ }
48+ // TODO - does ever frame.
49+ onBottomMarginChanged: {
50+ mainMenu.positionViewAtIndex(mainMenu.currentIndex, ListView.End)
51+ }
52+ }
53+
54+ // Ensure all delegates are cached in order to improve smoothness of scrolling
55+ cacheBuffer: 10000
56+
57+ // Only allow flicking if the content doesn't fit on the page
58+ interactive: contentHeight > height
59+
60+ currentIndex: -1
61+ delegate: Item {
62+ id: item
63+ property bool ready: false
64+ property alias empty: factory.empty
65+
66+ anchors {
67+ left: parent.left
68+ right: parent.right
69+ }
70+ height: div.height + factory.implicitHeight
71+ visible: height > 0
72+
73+ Component.onCompleted: {
74+ if (!item.empty) {
75+ mainMenu.visibleItems += 1;
76+ }
77+ ready = true;
78+ }
79+ Component.onDestruction: {
80+ if (!item.empty) {
81+ mainMenu.visibleItems -= 1;
82+ }
83+ ready = false;
84+ }
85+
86+ onEmptyChanged: {
87+ if (!ready) {
88+ return;
89+ }
90+ if (empty) {
91+ mainMenu.visibleItems -= 1;
92+ } else {
93+ mainMenu.visibleItems += 1;
94+ }
95+ }
96+
97+ ListView.onRemove: {
98+ if (!highlightFollowsCurrentItem) {
99+ mainMenu.currentIndex = -1;
100+ }
101+ }
102+
103+ Column {
104+ id: contents
105+
106+ anchors.fill: parent
107+
108+ DivMenuItem {
109+ id: div
110+
111+ anchors.left: parent.left
112+ anchors.right: parent.right
113+ // only visible if is a root or a section header element and the factory is visible
114+ height: {
115+ // skipe first item
116+ if (item.y == 0) {
117+ return 0;
118+ }
119+ if (hasSection || ((depth == 0) && (!factory.empty))) {
120+ return implicitHeight;
121+ } else {
122+ return 0;
123+ }
124+ }
125+ }
126+
127+ MenuItemFactory {
128+ id: factory
129+
130+ actionGroup: main.actionGroup
131+ isCurrentItem: item.ListView.isCurrentItem
132+ anchors {
133+ left: parent.left
134+ right: parent.right
135+ }
136+ menu: model
137+
138+ Connections {
139+ target : factory
140+ onActivated: item.ListView.view.currentIndex = index
141+ onDeactivated: item.ListView.view.currentIndex = -1
142+ }
143+ }
144+ }
145+ }
146+ }
147+
148+ Components.Label {
149+ id: emptyLabel
150+ visible: (mainMenu.visibleItems === 0)
151+ anchors {
152+ top: parent.top
153+ left: parent.left
154+ right: parent.right
155+ topMargin: units.gu(2)
156+ }
157+ wrapMode: Text.WordWrap
158+ horizontalAlignment: Text.AlignHCenter
159+
160+ //style
161+ color: "#e8e1d0"
162+ fontSize: "medium"
163+
164+ text: "Empty!"
165+ }
166+
167+ function start()
168+ {
169+ reset()
170+ if (!__active) {
171+ proxyModel.start();
172+ actionGroup.start();
173+ __active = true;
174+ }
175+ }
176+
177+ function stop()
178+ {
179+ if (__active) {
180+ __active = false;
181+ proxyModel.stop();
182+ actionGroup.stop();
183+ }
184+ }
185+
186+ function reset()
187+ {
188+ mainMenu.currentIndex = -1;
189+ mainMenu.positionViewAtBeginning();
190+ }
191+}
plugins/Unity/IndicatorsLegacy/qml/IndicatorWidget.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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Unity.IndicatorsLegacy 0.1 as Indicators
22+
23+IndicatorBase {
24+ id: indicatorWidget
25+
26+// enabled: false // FIXME : should be disabled until bus available when we have desktop indicators
27+ property var action: undefined
28+ property bool started: (busType != 0) && (busName != "") && (deviceMenuObjectPath != "")
29+ property int iconSize: height - units.gu(0.5)
30+ property string rootMenuType: "com.canonical.indicator.root"
31+
32+ signal actionStateChanged()
33+
34+ onStartedChanged: {
35+ if (started) {
36+ proxyModel.start();
37+ actionGroup.start();
38+ } else {
39+ proxyModel.stop();
40+ actionGroup.stop();
41+ }
42+ }
43+ onActionChanged: actionStateChanged()
44+ onActionGroupUpdated: update_state()
45+ onModelUpdated: update_state()
46+
47+
48+ function update_state() {
49+ var extra = proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Extra);
50+ if (extra == undefined) {
51+ return;
52+ }
53+
54+ if (!extra.hasOwnProperty("canonical_type")) {
55+ return;
56+ }
57+
58+ if (extra.canonical_type === rootMenuType) {
59+ action = actionGroup.action(proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Action));
60+ }
61+ }
62+
63+ Connections {
64+ target: action == undefined ? null : action
65+ onStateChanged: actionStateChanged()
66+ onValidChanged: actionStateChanged()
67+ }
68+}
plugins/Unity/Indicators/qml/MenuAction.qml → plugins/Unity/IndicatorsLegacy/qml/MenuAction.qmlrenamed

The content is the same.

plugins/Unity/Indicators/qml/MenuActionBinding.qml → plugins/Unity/IndicatorsLegacy/qml/MenuActionBinding.qmlrenamed

The content is the same.

plugins/Unity/Indicators/qml/MenuItem.qml → plugins/Unity/IndicatorsLegacy/qml/MenuItem.qmlrenamed

The content is the same.

plugins/Unity/IndicatorsLegacy/qml/MenuItemFactory.qmlnew file+110
@@ -0,0 +1,110 @@
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+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Unity.IndicatorsLegacy 0.1 as Indicators
22+import Unity.IndicatorsLegacy.Messaging 0.1 as ICMessaging
23+import Unity.IndicatorsLegacy.Network 0.1 as ICNetwork
24+
25+Item {
26+ id: menuFactory
27+
28+ property QtObject menu
29+ property QtObject listView
30+ property QtObject actionGroup
31+ property bool isCurrentItem
32+ readonly property bool empty: (loader.item !== undefined && loader.status == Loader.Ready) ? loader.item.state === "EMPTY" : true
33+
34+ implicitHeight: loader.height
35+
36+ signal activated()
37+ signal deactivated()
38+
39+ property var _map: {
40+ "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
41+ "unity.widgets.systemsettings.tablet.switch" : switchMenu,
42+
43+ "com.canonical.indicator.button" : buttonMenu,
44+ "com.canonical.indicator.div" : divMenu,
45+ "com.canonical.indicator.section" : sectionMenu,
46+ "com.canonical.indicator.progress" : progressMenu,
47+ "com.canonical.indicator.slider" : sliderMenu,
48+ "com.canonical.indicator.switch" : switchMenu,
49+
50+ "com.canonical.indicator.messages.messageitem" : messageItem,
51+ "com.canonical.indicator.messages.snapdecision" : snapDecision,
52+ "com.canonical.indicator.messages.sourceitem" : groupedMessage,
53+
54+ "unity.widget.systemsettings.tablet.sectiontitle" : wifiSection,
55+ "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
56+ "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
57+ }
58+
59+ Component { id: sliderMenu; Indicators.SliderMenuItem {} }
60+ Component { id: switchMenu; Indicators.SwitchMenuItem {} }
61+ Component { id: buttonMenu; Indicators.ButtonMenuItem {} }
62+ Component { id: divMenu; Indicators.DivMenuItem {} }
63+ Component { id: sectionMenu; Indicators.SectionMenuItem {} }
64+ Component { id: progressMenu; Indicators.ProgressMenuItem {} }
65+ Component { id: messageItem; ICMessaging.MessageMenuItem {} }
66+ Component { id: snapDecision; ICMessaging.SnapDecision {} }
67+ Component { id: groupedMessage; ICMessaging.GroupedMessage {} }
68+ Component { id: wifiSection; ICNetwork.WifiSection {} }
69+ Component { id: accessPoint; ICNetwork.AccessPoint {} }
70+ Component { id: indicatorMenu; Indicators.MenuItem {} }
71+
72+ Loader {
73+ id: loader
74+ asynchronous: true
75+
76+ anchors {
77+ left: parent.left
78+ right: parent.right
79+ }
80+
81+ sourceComponent: {
82+ if (!menuFactory.menu || !menuFactory.menu.extra) {
83+ return undefined;
84+ }
85+
86+ var widgetType = menuFactory.menu.extra.canonical_type;
87+ if (widgetType) {
88+ return _map[widgetType];
89+ } else {
90+ if (widgetType === "com.canonical.indicator.root") {
91+ return undefined;
92+ // Try discovery the item based on the basic properties
93+ } else if (menu.hasSection) {
94+ return sectionMenu;
95+ } else {
96+ return indicatorMenu;
97+ }
98+ }
99+ }
100+
101+ onLoaded: {
102+ item.menuActivated = Qt.binding(function() { return isCurrentItem; });
103+ item.actionGroup = Qt.binding(function() { return menuFactory.actionGroup; });
104+ item.menu = Qt.binding(function() { return menuFactory.menu; });
105+
106+ item.activateMenu.connect(function() { menuFactory.activated(); });
107+ item.deactivateMenu.connect(function() { menuFactory.deactivated(); });
108+ }
109+ }
110+}
plugins/Unity/IndicatorsLegacy/qml/ProgressMenuItem.qmlnew 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+ * Michael Zanetti <michael.zanetti@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+
23+MenuItem {
24+ id: progressMenu
25+ property alias value : progressBar.value
26+
27+ // TODO: Replace this with the official ProgressBar component as soon as
28+ // it is available. For now, this rebuilds the Mockup as close as possible.
29+ UbuntuShape {
30+ id: progressBar
31+ anchors {
32+ right: parent.right
33+ rightMargin: units.gu(2)
34+ left: parent.left
35+ leftMargin: units.gu(2)
36+ }
37+ height: units.gu(4)
38+ anchors.verticalCenter: parent.verticalCenter
39+ color: "transparent"
40+
41+ property int minimumValue: 0
42+ property int maximumValue: 100
43+ property int value: 50
44+
45+ UbuntuShape {
46+ anchors.fill: parent
47+ anchors.rightMargin: parent.width - (parent.width * parent.value / parent.maximumValue)
48+ color: "#c94212"
49+ }
50+
51+ Label {
52+ anchors.centerIn: parent
53+ text: parent.value + " %"
54+ fontSize: "medium"
55+ color: "#e8e1d0"
56+ }
57+ }
58+
59+ MenuActionBinding {
60+ actionGroup: progressMenu.actionGroup
61+ action: menu ? menu.action : ""
62+ target: progressBar
63+ property: "value"
64+ }
65+}
plugins/Unity/IndicatorsLegacy/qml/RemoveBackground.qmlnew file+55
@@ -0,0 +1,55 @@
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+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+
20+Rectangle {
21+ anchors.fill: parent
22+ color: "#333130"
23+ Label {
24+ id: backgroundText
25+
26+ anchors.fill: parent
27+ verticalAlignment: Text.AlignVCenter
28+ text: "Clear"
29+ fontSize: "medium"
30+ color: "#e8e1d0"
31+ font.bold: true
32+ }
33+
34+ states: [
35+ State {
36+ name: "SwipingRight"
37+ PropertyChanges {
38+ target: backgroundText
39+ anchors.rightMargin: units.gu(3)
40+ anchors.leftMargin: 0
41+ horizontalAlignment: Text.AlignRight
42+
43+ }
44+ },
45+ State {
46+ name: "SwipingLeft"
47+ PropertyChanges {
48+ target: backgroundText
49+ anchors.rightMargin: 0
50+ anchors.leftMargin: units.gu(3)
51+ horizontalAlignment: Text.AlignLeft
52+ }
53+ }
54+ ]
55+}
plugins/Unity/IndicatorsLegacy/qml/SectionMenuItem.qmlnew file+57
@@ -0,0 +1,57 @@
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+
24+BasicMenuItem {
25+ property string label: menu && menu.label ? menu.label : ""
26+ property bool busy: false
27+ property int count: menu && menu.linkSection ? menu.linkSection.count : 0
28+
29+ color: "#221e1b"
30+ implicitHeight: label !== "" ? __header.height : 0
31+ state: count === 0 ? "EMPTY" : ""
32+
33+ ListItem.Header {
34+ id: __header
35+
36+ text: label
37+ height: units.gu(4)
38+ anchors {
39+ left: parent.left
40+ right: parent.right
41+ top: parent.top
42+ }
43+ visible: text != ""
44+
45+ ActivityIndicator {
46+ id: indicator
47+ running: busy
48+ anchors {
49+ margins: units.gu(0.5)
50+ right: parent.right
51+ }
52+ height: parent.height - (anchors.margins * 2)
53+ width: height
54+ anchors.verticalCenter: parent.verticalCenter
55+ }
56+ }
57+}
plugins/Unity/IndicatorsLegacy/qml/SliderMenuItem.qmlnew file+64
@@ -0,0 +1,64 @@
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+MenuItem {
24+ id: _sliderMenu
25+
26+ property alias minimumValue: slider.minimumValue
27+ property alias maximumValue: slider.maximumValue
28+ property alias value: slider.value
29+
30+ control: Slider {
31+ id: slider
32+ enabled: menuAction.valid
33+ width: _sliderMenu.text ? units.gu(20) : _sliderMenu.width - units.gu(4)
34+ anchors.verticalCenter: parent.verticalCenter
35+ live: false
36+ // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
37+ // reaching the boundaries (0.0 or 1.0), the value is converted
38+ // to an integer when automatically wrapped in a variant when
39+ // passed to QStateAction::updateState(…). The server chokes on
40+ // those values, complaining that they’re not of the right type…
41+ minimumValue: menu.extra.canonical_min ? menu.extra.canonical_min * 1.000001 : 0.0000001
42+ maximumValue: {
43+ var maximum = menu.extra.canonical_max ? menu.extra.canonical_max * 1.000001 : 0.9999999
44+ if (maximum <= minimumValue) {
45+ return minimumValue + 1;
46+ }
47+ return maximum;
48+ }
49+
50+ // FIXME - to be deprecated in Ubuntu.Components.
51+ // Use this to disable the label, since there is not way to do it on the component.
52+ function formatValue(v) {
53+ return "";
54+ }
55+ }
56+
57+ MenuActionBinding {
58+ id: menuAction
59+ actionGroup: _sliderMenu.actionGroup
60+ action: menu ? menu.action : ""
61+ target: slider
62+ property: "value"
63+ }
64+}
plugins/Unity/IndicatorsLegacy/qml/SwitchMenuItem.qmlnew file+43
@@ -0,0 +1,43 @@
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+MenuItem {
24+ id: switchMenu
25+
26+ property alias checked: switcher.checked
27+
28+ control: Switch {
29+ id: switcher
30+ enabled: menuAction.valid
31+
32+ anchors.verticalCenter: parent.verticalCenter
33+ anchors.right: parent.right
34+ }
35+
36+ MenuActionBinding {
37+ id: menuAction
38+ actionGroup: switchMenu.actionGroup
39+ action: menu ? menu.action : ""
40+ target: switcher
41+ property: "checked"
42+ }
43+}
plugins/Unity/IndicatorsLegacy/qml/qmldirnew file+21
@@ -0,0 +1,21 @@
1+module Unity.IndicatorsLegacy
2+plugin IndicatorsLegacyQml
3+typeinfo plugin.qmltypes
4+
5+BasicMenuItem 0.1 BasicMenuItem.qml
6+ButtonMenuItem 0.1 ButtonMenuItem.qml
7+DivMenuItem 0.1 DivMenuItem.qml
8+HLine 0.1 HLine.qml
9+IndicatorBase 0.1 IndicatorBase.qml
10+IndicatorPage 0.1 IndicatorPage.qml
11+IndicatorWidget 0.1 IndicatorWidget.qml
12+MenuItem 0.1 MenuItem.qml
13+MenuAction 0.1 MenuAction.qml
14+MenuActionBinding 0.1 MenuActionBinding.qml
15+MenuItemFactory 0.1 MenuItemFactory.qml
16+ProgressMenuItem 0.1 ProgressMenuItem.qml
17+SectionMenuItem 0.1 SectionMenuItem.qml
18+SliderMenuItem 0.1 SliderMenuItem.qml
19+SwitchMenuItem 0.1 SwitchMenuItem.qml
20+
21+internal RemoveBackground RemoveBackground.qml
plugins/Unity/Indicators/resources/artwork/messaging/default_app.svg → plugins/Unity/IndicatorsLegacy/resources/artwork/messaging/default_app.svgrenamed

The content is the same.

plugins/Unity/Indicators/resources/artwork/messaging/default_contact.png → plugins/Unity/IndicatorsLegacy/resources/artwork/messaging/default_contact.pngrenamed

Binary file not shown.

plugins/Unity/Indicators/resources/artwork/messaging/message_sms01_54px.png → plugins/Unity/IndicatorsLegacy/resources/artwork/messaging/message_sms01_54px.pngrenamed

Binary file not shown.

plugins/Unity/Indicators/resources/indicators.qrc → plugins/Unity/IndicatorsLegacy/resources/indicators.qrcrenamed

The content is the same.

plugins/Unity/Launcher/backend/launcherbackend.cpp+1−1
@@ -101,7 +101,7 @@ bool LauncherBackend::isPinned(const QString &appId) const
101{
101{
102 // TODO: return app's pinned state from settings
102 // TODO: return app's pinned state from settings
103 Q_UNUSED(appId)
103 Q_UNUSED(appId)
104− return true;
104+ return false;
105}
105}
106
106
107void LauncherBackend::setPinned(const QString &appId, bool pinned)
107void LauncherBackend::setPinned(const QString &appId, bool pinned)
plugins/Unity/Launcher/common/quicklistentry.cpp+5
@@ -52,3 +52,8 @@ void QuickListEntry::setIcon(const QString &icon)
52{
52{
53 m_icon = icon;
53 m_icon = icon;
54}
54}
55+
56+bool QuickListEntry::clickable() const
57+{
58+ return !m_actionId.isEmpty();
59+}
plugins/Unity/Launcher/common/quicklistentry.h+2
@@ -35,6 +35,8 @@ public:
35 QString icon() const;
35 QString icon() const;
36 void setIcon(const QString &icon);
36 void setIcon(const QString &icon);
37
37
38+ bool clickable() const;
39+
38private:
40private:
39 QString m_actionId;
41 QString m_actionId;
40 QString m_text;
42 QString m_text;
plugins/Unity/Launcher/launcheritem.cpp+11−1
@@ -33,7 +33,13 @@ LauncherItem::LauncherItem(const QString &appId, const QString &desktopFile, con
33 m_count(0),
33 m_count(0),
34 m_quickList(new QuickListModel(this))
34 m_quickList(new QuickListModel(this))
35{
35{
36−
36+ QuickListEntry nameAction;
37+ nameAction.setText(m_name);
38+ m_quickList->appendAction(nameAction);
39+ QuickListEntry pinningAction;
40+ pinningAction.setActionId("pin_item");
41+ pinningAction.setText("Pin to Launcher");
42+ m_quickList->appendAction(pinningAction);
37}
43}
38
44
39QString LauncherItem::appId() const
45QString LauncherItem::appId() const
@@ -65,6 +71,10 @@ void LauncherItem::setPinned(bool pinned)
65{
71{
66 if (m_pinned != pinned) {
72 if (m_pinned != pinned) {
67 m_pinned = pinned;
73 m_pinned = pinned;
74+ QuickListEntry entry;
75+ entry.setActionId("pin_item");
76+ entry.setText(pinned ? "Remove from Launcher": "Pin to Launcher");
77+ m_quickList->updateAction(entry);
68 Q_EMIT pinnedChanged(pinned);
78 Q_EMIT pinnedChanged(pinned);
69 }
79 }
70}
80}
plugins/Unity/Launcher/launchermodel.cpp+68−4
@@ -25,6 +25,9 @@ LauncherModel::LauncherModel(QObject *parent):
25 LauncherModelInterface(parent),
25 LauncherModelInterface(parent),
26 m_backend(new LauncherBackend(this))
26 m_backend(new LauncherBackend(this))
27{
27{
28+ connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
29+ connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
30+
28 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
31 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
29 LauncherItem *item = new LauncherItem(entry,
32 LauncherItem *item = new LauncherItem(entry,
30 m_backend->desktopFile(entry),
33 m_backend->desktopFile(entry),
@@ -67,6 +70,10 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
67 return item->icon();
70 return item->icon();
68 case RolePinned:
71 case RolePinned:
69 return item->pinned();
72 return item->pinned();
73+ case RoleCount:
74+ return item->count();
75+ case RoleProgress:
76+ return item->progress();
70 }
77 }
71
78
72 return QVariant();
79 return QVariant();
@@ -82,8 +89,25 @@ unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) con
82
89
83void LauncherModel::move(int oldIndex, int newIndex)
90void LauncherModel::move(int oldIndex, int newIndex)
84{
91{
85− // Perform the move in our list
92+ // Make sure its not moved outside the lists
86− beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
93+ if (newIndex < 0) {
94+ newIndex = 0;
95+ }
96+ if (newIndex >= m_list.count()) {
97+ newIndex = m_list.count()-1;
98+ }
99+
100+ // Nothing to do?
101+ if (oldIndex == newIndex) {
102+ return;
103+ }
104+
105+ // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
106+ // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
107+ // adjust the model's index by +1 in case we're moving upwards
108+ int newModelIndex = newIndex > oldIndex ? newIndex+1 : newIndex;
109+
110+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
87 m_list.move(oldIndex, newIndex);
111 m_list.move(oldIndex, newIndex);
88 endMoveRows();
112 endMoveRows();
89
113
@@ -140,13 +164,32 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
140 return;
164 return;
141 }
165 }
142
166
143− QuickListModel *model = qobject_cast<QuickListModel*>(m_list.at(index)->quickList());
167+ LauncherItem *item = m_list.at(index);
168+ QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
144 if (model) {
169 if (model) {
145 QString actionId = model->get(actionIndex).actionId();
170 QString actionId = model->get(actionIndex).actionId();
146− m_backend->triggerQuickListAction(appId, actionId);
171+
172+ // Check if this is one of the launcher actions we handle ourselves
173+ if (actionId == "pin_item") {
174+ if (item->pinned()) {
175+ requestRemove(appId);
176+ } else {
177+ pin(appId);
178+ }
179+
180+ // Nope, we don't know this action, let the backend forward it to the application
181+ } else {
182+ m_backend->triggerQuickListAction(appId, actionId);
183+ }
147 }
184 }
148}
185}
149
186
187+void LauncherModel::setUser(const QString &username)
188+{
189+ Q_UNUSED(username)
190+ // TODO: Implement this...
191+}
192+
150void LauncherModel::storeAppList()
193void LauncherModel::storeAppList()
151{
194{
152 QStringList appIds;
195 QStringList appIds;
@@ -168,3 +211,24 @@ int LauncherModel::findApplication(const QString &appId)
168 }
211 }
169 return -1;
212 return -1;
170}
213}
214+
215+void LauncherModel::progressChanged(const QString &appId, int progress)
216+{
217+ int idx = findApplication(appId);
218+ if (idx >= 0) {
219+ LauncherItem *item = m_list.at(idx);
220+ item->setProgress(progress);
221+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
222+ }
223+}
224+
225+
226+void LauncherModel::countChanged(const QString &appId, int count)
227+{
228+ int idx = findApplication(appId);
229+ if (idx >= 0) {
230+ LauncherItem *item = m_list.at(idx);
231+ item->setCount(count);
232+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
233+ }
234+}
plugins/Unity/Launcher/launchermodel.h+5
@@ -48,11 +48,16 @@ public:
48 Q_INVOKABLE void pin(const QString &appId, int index = -1);
48 Q_INVOKABLE void pin(const QString &appId, int index = -1);
49 Q_INVOKABLE void requestRemove(const QString &appId);
49 Q_INVOKABLE void requestRemove(const QString &appId);
50 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
50 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
51+ Q_INVOKABLE void setUser(const QString &username);
51
52
52private:
53private:
53 void storeAppList();
54 void storeAppList();
54 int findApplication(const QString &appId);
55 int findApplication(const QString &appId);
55
56
57+private Q_SLOTS:
58+ void progressChanged(const QString &appId, int progress);
59+ void countChanged(const QString &appId, int count);
60+
56private:
61private:
57 QList<LauncherItem*> m_list;
62 QList<LauncherItem*> m_list;
58 LauncherBackend *m_backend;
63 LauncherBackend *m_backend;
plugins/Unity/Launcher/plugin.cpp+3−1
@@ -18,7 +18,7 @@
18 */
18 */
19
19
20// Qt
20// Qt
21−#include <QtQml>
21+#include <QtQml/qqml.h>
22
22
23// self
23// self
24#include "plugin.h"
24#include "plugin.h"
@@ -41,7 +41,9 @@ void UnityLauncherPlugin::registerTypes(const char *uri)
41
41
42 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
42 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
43 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
43 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
44+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListInterface", "Abstract Interface. Cannot be instantiated.");
44
45
45 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
46 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
46 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", "Can't create new Launcher Items in QML. Get them from the LauncherModel.");
47 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", "Can't create new Launcher Items in QML. Get them from the LauncherModel.");
48+ qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", "Can't create a QuickListModel in QML. Get them from the LauncherItems.");
47}
49}
plugins/Unity/Launcher/quicklistmodel.cpp+13
@@ -37,6 +37,17 @@ void QuickListModel::appendAction(const QuickListEntry &entry)
37 endInsertRows();
37 endInsertRows();
38}
38}
39
39
40+void QuickListModel::updateAction(const QuickListEntry &entry)
41+{
42+ for (int i = 0; i < m_list.count(); ++i) {
43+ if (m_list.at(i).actionId() == entry.actionId()) {
44+ m_list.replace(i, entry);
45+ Q_EMIT dataChanged(index(i), index(i));
46+ return;
47+ }
48+ }
49+}
50+
40QuickListEntry QuickListModel::get(int index) const
51QuickListEntry QuickListModel::get(int index) const
41{
52{
42 return m_list.at(index);
53 return m_list.at(index);
@@ -55,6 +66,8 @@ QVariant QuickListModel::data(const QModelIndex &index, int role) const
55 return m_list.at(index.row()).text();
66 return m_list.at(index.row()).text();
56 case RoleIcon:
67 case RoleIcon:
57 return m_list.at(index.row()).icon();
68 return m_list.at(index.row()).icon();
69+ case RoleClickable:
70+ return m_list.at(index.row()).clickable();
58 }
71 }
59 return QVariant();
72 return QVariant();
60}
73}
plugins/Unity/Launcher/quicklistmodel.h+10
@@ -36,6 +36,16 @@ public:
36 ~QuickListModel();
36 ~QuickListModel();
37
37
38 void appendAction(const QuickListEntry &entry);
38 void appendAction(const QuickListEntry &entry);
39+
40+ /**
41+ * @brief Update an existing action
42+ * @param entry The new, updated entry
43+ *
44+ * This will only do something if entry.actionId is found in the model.
45+ * To add a new entry, use appendAction().
46+ */
47+ void updateAction(const QuickListEntry &entry);
48+
39 QuickListEntry get(int index) const;
49 QuickListEntry get(int index) const;
40
50
41 int rowCount(const QModelIndex &parent = QModelIndex()) const;
51 int rowCount(const QModelIndex &parent = QModelIndex()) const;
plugins/Unity/abstractfilteroption.cppnew file+25
@@ -0,0 +1,25 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#include "abstractfilteroption.h"
21+
22+AbstractFilterOption::AbstractFilterOption(QObject *parent)
23+ : QObject(parent)
24+{
25+}
plugins/Unity/abstractfilteroption.hnew file+58
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef ABSTRACTFILTEROPTION_H
21+#define ABSTRACTFILTEROPTION_H
22+
23+// Qt
24+#include <QObject>
25+#include <QMetaType>
26+
27+class Q_DECL_EXPORT AbstractFilterOption : public QObject
28+{
29+ Q_OBJECT
30+
31+ Q_PROPERTY(QString id READ id NOTIFY idChanged)
32+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
33+ Q_PROPERTY(QString iconHint READ iconHint NOTIFY iconHintChanged)
34+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
35+
36+public:
37+ /* getters */
38+ virtual QString id() const = 0;
39+ virtual QString name() const = 0;
40+ virtual QString iconHint() const = 0;
41+ virtual bool active() const = 0;
42+
43+ /* setters */
44+ virtual void setActive(bool active) = 0;
45+
46+protected:
47+ AbstractFilterOption(QObject *parent);
48+
49+Q_SIGNALS:
50+ void idChanged(const QString &);
51+ void nameChanged(const QString &);
52+ void iconHintChanged(const QString &);
53+ void activeChanged(bool);
54+};
55+
56+Q_DECLARE_METATYPE(AbstractFilterOption*)
57+
58+#endif // ABSTRACTFILTEROPTION_H
plugins/Unity/categories.cpp+72
@@ -39,6 +39,7 @@ Categories::Categories(QObject* parent)
39 m_roles[Categories::RoleIcon] = "icon";
39 m_roles[Categories::RoleIcon] = "icon";
40 m_roles[Categories::RoleRenderer] = "renderer";
40 m_roles[Categories::RoleRenderer] = "renderer";
41 m_roles[Categories::RoleContentType] = "contentType";
41 m_roles[Categories::RoleContentType] = "contentType";
42+ m_roles[Categories::RoleProgressSource] = "progressSource";
42 m_roles[Categories::RoleHints] = "hints";
43 m_roles[Categories::RoleHints] = "hints";
43 m_roles[Categories::RoleResults] = "results";
44 m_roles[Categories::RoleResults] = "results";
44 m_roles[Categories::RoleCount] = "count";
45 m_roles[Categories::RoleCount] = "count";
@@ -104,6 +105,25 @@ Categories::onCountChanged()
104 }
105 }
105}
106}
106
107
108+void Categories::onRowCountChanged()
109+{
110+ QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>(sender());
111+ // find the corresponding category index
112+ for (auto iter = m_overriddenCategories.begin(); iter != m_overriddenCategories.end(); ++iter) {
113+ if (iter.value() == model) {
114+ for (int i = 0; i < rowCount(); i++) {
115+ auto id = data(index(i), RoleCategoryId).toString();
116+ if (id != iter.key()) continue;
117+ QVector<int> roles;
118+ roles.append(RoleCount);
119+ QModelIndex changedIndex = index(i);
120+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
121+ break;
122+ }
123+ }
124+ }
125+}
126+
107void
127void
108Categories::onEmitCountChanged()
128Categories::onEmitCountChanged()
109{
129{
@@ -123,6 +143,41 @@ Categories::roleNames() const
123 return m_roles;
143 return m_roles;
124}
144}
125
145
146+void Categories::onOverrideModelDestroyed()
147+{
148+ QObject* model = sender();
149+ auto iter = m_overriddenCategories.begin();
150+ while (iter != m_overriddenCategories.end()) {
151+ if (iter.value() == model) {
152+ iter = m_overriddenCategories.erase(iter);
153+ continue;
154+ }
155+ ++iter;
156+ }
157+}
158+
159+void Categories::overrideResults(const QString& categoryId, QAbstractItemModel* model)
160+{
161+ m_overriddenCategories[categoryId] = model;
162+ // watch the model
163+ connect(model, &QObject::destroyed, this, &Categories::onOverrideModelDestroyed);
164+ connect(model, &QAbstractItemModel::rowsInserted, this, &Categories::onRowCountChanged);
165+ connect(model, &QAbstractItemModel::rowsRemoved, this, &Categories::onRowCountChanged);
166+ connect(model, &QAbstractItemModel::modelReset, this, &Categories::onRowCountChanged);
167+
168+ // emit the dataChanged signal if the category is already in the model
169+ for (int i = 0; i < rowCount(); i++) {
170+ auto id = data(index(i), RoleCategoryId).toString();
171+ if (id != categoryId) continue;
172+ QVector<int> roles;
173+ roles.append(RoleCount);
174+ roles.append(RoleResults);
175+ QModelIndex changedIndex = index(i);
176+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
177+ break;
178+ }
179+}
180+
126QVariant
181QVariant
127Categories::data(const QModelIndex& index, int role) const
182Categories::data(const QModelIndex& index, int role) const
128{
183{
@@ -144,11 +199,28 @@ Categories::data(const QModelIndex& index, int role) const
144 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
199 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
145 return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
200 return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
146 }
201 }
202+ case RoleProgressSource:
203+ {
204+ auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
205+ return hints.contains("progress-source") ? hints["progress-source"] : QVariant();
206+ }
147 case RoleHints:
207 case RoleHints:
148 return DeeListModel::data(index, CategoryColumn::HINTS);
208 return DeeListModel::data(index, CategoryColumn::HINTS);
149 case RoleResults:
209 case RoleResults:
210+ if (m_overriddenCategories.size() > 0)
211+ {
212+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
213+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
214+ return QVariant::fromValue(m_overriddenCategories[id]);
215+ }
150 return QVariant::fromValue(getResults(index.row()));
216 return QVariant::fromValue(getResults(index.row()));
151 case RoleCount:
217 case RoleCount:
218+ if (m_overriddenCategories.size() > 0)
219+ {
220+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
221+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
222+ return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
223+ }
152 return QVariant::fromValue(getResults(index.row())->rowCount());
224 return QVariant::fromValue(getResults(index.row())->rowCount());
153 case RoleCategoryIndex:
225 case RoleCategoryIndex:
154 return QVariant::fromValue(index.row());
226 return QVariant::fromValue(index.row());
plugins/Unity/categories.h+6
@@ -47,12 +47,15 @@ public:
47 RoleIcon,
47 RoleIcon,
48 RoleRenderer,
48 RoleRenderer,
49 RoleContentType,
49 RoleContentType,
50+ RoleProgressSource,
50 RoleHints,
51 RoleHints,
51 RoleResults,
52 RoleResults,
52 RoleCount,
53 RoleCount,
53 RoleCategoryIndex
54 RoleCategoryIndex
54 };
55 };
55
56
57+ Q_INVOKABLE void overrideResults(const QString& categoryId, QAbstractItemModel* model);
58+
56 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
59 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
57
60
58 QHash<int, QByteArray> roleNames() const;
61 QHash<int, QByteArray> roleNames() const;
@@ -62,7 +65,9 @@ public:
62
65
63private Q_SLOTS:
66private Q_SLOTS:
64 void onCountChanged();
67 void onCountChanged();
68+ void onRowCountChanged();
65 void onEmitCountChanged();
69 void onEmitCountChanged();
70+ void onOverrideModelDestroyed();
66
71
67private:
72private:
68 void onCategoriesModelChanged(unity::glib::Object<DeeModel> model);
73 void onCategoriesModelChanged(unity::glib::Object<DeeModel> model);
@@ -73,6 +78,7 @@ private:
73 QTimer m_timer;
78 QTimer m_timer;
74 QSet<int> m_updatedCategories;
79 QSet<int> m_updatedCategories;
75 QHash<int, QByteArray> m_roles;
80 QHash<int, QByteArray> m_roles;
81+ QMap<QString, QAbstractItemModel*> m_overriddenCategories;
76 mutable QMap<int, DeeListModel*> m_results;
82 mutable QMap<int, DeeListModel*> m_results;
77 sigc::connection m_categoriesChangedConnection;
83 sigc::connection m_categoriesChangedConnection;
78};
84};
plugins/Unity/checkoptionfilter.cppnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 2011 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// Self
21+#include "checkoptionfilter.h"
22+
23+// local
24+#include "unityoptionsmodel.h"
25+
26+CheckOptionFilter::CheckOptionFilter(QObject *parent) :
27+ Filter(parent),
28+ m_unityCheckOptionFilter(nullptr),
29+ m_options(nullptr)
30+{
31+}
32+
33+GenericOptionsModel* CheckOptionFilter::options() const
34+{
35+ return m_options;
36+}
37+
38+void CheckOptionFilter::setUnityFilter(unity::dash::Filter::Ptr filter)
39+{
40+ Filter::setUnityFilter(filter);
41+ m_unityCheckOptionFilter = std::dynamic_pointer_cast<unity::dash::CheckOptionFilter>(m_unityFilter);
42+ onOptionsChanged(m_unityCheckOptionFilter->options);
43+}
44+
45+void CheckOptionFilter::onOptionsChanged(unity::dash::CheckOptionFilter::CheckOptions options)
46+{
47+ if (m_options != nullptr) {
48+ m_signals.disconnectAll();
49+ delete m_options;
50+ m_options = nullptr;
51+ }
52+
53+ m_options = new UnityOptionsModel(this, options,
54+ m_unityCheckOptionFilter->option_added,
55+ m_unityCheckOptionFilter->option_removed);
56+
57+ connect(m_options, SIGNAL(activeChanged(AbstractFilterOption *)), m_options, SLOT(ensureTheOnlyActive(AbstractFilterOption *)));
58+
59+ /* Property change signals */
60+ m_signals << m_unityCheckOptionFilter->options.changed.connect(sigc::mem_fun(this, &CheckOptionFilter::onOptionsChanged));
61+
62+ Q_EMIT optionsChanged();
63+}
plugins/Unity/checkoptionfilter.hnew file+57
@@ -0,0 +1,57 @@
1+/*
2+ * Copyright (C) 2011 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef CHECKOPTIONFILTER_H
21+#define CHECKOPTIONFILTER_H
22+
23+// Qt
24+#include <QObject>
25+#include <QMetaType>
26+
27+// libunity-core
28+#include <UnityCore/CheckOptionFilter.h>
29+
30+// Local
31+#include "filter.h"
32+#include "signalslist.h"
33+
34+class Q_DECL_EXPORT CheckOptionFilter : public Filter
35+{
36+ Q_OBJECT
37+
38+public:
39+ explicit CheckOptionFilter(QObject *parent = nullptr);
40+
41+ /* getters */
42+ GenericOptionsModel* options() const override;
43+
44+protected:
45+ void setUnityFilter(unity::dash::Filter::Ptr filter) override;
46+
47+private:
48+ void onOptionsChanged(unity::dash::CheckOptionFilter::CheckOptions filter);
49+
50+ unity::dash::CheckOptionFilter::Ptr m_unityCheckOptionFilter;
51+ SignalsList m_signals;
52+ GenericOptionsModel* m_options;
53+};
54+
55+Q_DECLARE_METATYPE(CheckOptionFilter*)
56+
57+#endif // CHECKOPTIONFILTER_H
plugins/Unity/filter.cppnew file+129
@@ -0,0 +1,129 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "filter.h"
23+
24+// local
25+#include "ratingsfilter.h"
26+#include "radiooptionfilter.h"
27+#include "checkoptionfilter.h"
28+#include "multirangefilter.h"
29+
30+// libunity-core
31+#include <UnityCore/Filter.h>
32+#include <UnityCore/RatingsFilter.h>
33+#include <UnityCore/RadioOptionFilter.h>
34+#include <UnityCore/CheckOptionFilter.h>
35+#include <UnityCore/MultiRangeFilter.h>
36+
37+// Qt
38+#include <QDebug>
39+
40+Filter::Filter(QObject *parent) :
41+ QObject(parent)
42+{
43+}
44+
45+QString Filter::id() const
46+{
47+ if (!m_unityFilter)
48+ return QString::Null();
49+ return QString::fromStdString(m_unityFilter->id());
50+}
51+
52+QString Filter::name() const
53+{
54+ if (!m_unityFilter)
55+ return QString::Null();
56+ return QString::fromStdString(m_unityFilter->name());
57+}
58+
59+QString Filter::iconHint() const
60+{
61+ if (!m_unityFilter)
62+ return QString::Null();
63+ return QString::fromStdString(m_unityFilter->icon_hint());
64+}
65+
66+QString Filter::rendererName() const
67+{
68+ if (!m_unityFilter)
69+ return QString::Null();
70+ return QString::fromStdString(m_unityFilter->renderer_name());
71+}
72+
73+bool Filter::visible() const
74+{
75+ if (!m_unityFilter)
76+ return false;
77+ return m_unityFilter->visible();
78+}
79+
80+bool Filter::collapsed() const
81+{
82+ if (!m_unityFilter)
83+ return false;
84+ return m_unityFilter->collapsed();
85+}
86+
87+bool Filter::filtering() const
88+{
89+ if (!m_unityFilter)
90+ return false;
91+ return m_unityFilter->filtering();
92+}
93+
94+void Filter::clear()
95+{
96+ if (m_unityFilter)
97+ m_unityFilter->Clear();
98+}
99+
100+void Filter::setUnityFilter(unity::dash::Filter::Ptr unityFilter)
101+{
102+ m_unityFilter = unityFilter;
103+}
104+
105+Filter* Filter::newFromUnityFilter(unity::dash::Filter::Ptr unityFilter)
106+{
107+ Filter* filter;
108+
109+ if (typeid(*unityFilter) == typeid(unity::dash::RatingsFilter)) {
110+ filter = new RatingsFilter;
111+ } else if (typeid(*unityFilter) == typeid(unity::dash::CheckOptionFilter)) {
112+ filter = new CheckOptionFilter;
113+ } else if (typeid(*unityFilter) == typeid(unity::dash::RadioOptionFilter)) {
114+ filter = new RadioOptionFilter;
115+ } else if (typeid(*unityFilter) == typeid(unity::dash::MultiRangeFilter)) {
116+ filter = new MultiRangeFilter;
117+ } else {
118+ qWarning() << "Filter of unknown type: " << typeid(*unityFilter).name();
119+ return nullptr;
120+ }
121+
122+ filter->setUnityFilter(unityFilter);
123+ return filter;
124+}
125+
126+bool Filter::hasUnityFilter(unity::dash::Filter::Ptr unityFilter) const
127+{
128+ return m_unityFilter == unityFilter;
129+}
plugins/Unity/filter.hnew file+74
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef FILTER_H
22+#define FILTER_H
23+
24+// Qt
25+#include <QObject>
26+#include <QMetaType>
27+
28+// libunity-core
29+#include <UnityCore/Filter.h>
30+
31+class GenericOptionsModel;
32+
33+class Q_DECL_EXPORT Filter : public QObject
34+{
35+ Q_OBJECT
36+
37+ Q_PROPERTY(QString id READ id CONSTANT)
38+ Q_PROPERTY(QString name READ name CONSTANT)
39+ Q_PROPERTY(QString iconHint READ iconHint CONSTANT)
40+ Q_PROPERTY(QString rendererName READ rendererName CONSTANT)
41+ Q_PROPERTY(bool visible READ visible CONSTANT)
42+ Q_PROPERTY(bool collapsed READ collapsed CONSTANT)
43+ Q_PROPERTY(bool filtering READ filtering CONSTANT)
44+ Q_PROPERTY(GenericOptionsModel* options READ options NOTIFY optionsChanged)
45+
46+public:
47+ explicit Filter(QObject *parent = nullptr);
48+
49+ /* getters */
50+ QString id() const;
51+ QString name() const;
52+ QString iconHint() const;
53+ QString rendererName() const;
54+ bool visible() const;
55+ bool collapsed() const;
56+ bool filtering() const;
57+ virtual GenericOptionsModel* options() const = 0;
58+
59+ Q_INVOKABLE void clear();
60+
61+ static Filter* newFromUnityFilter(unity::dash::Filter::Ptr unityFilter);
62+ bool hasUnityFilter(unity::dash::Filter::Ptr unityFilter) const;
63+
64+Q_SIGNALS:
65+ void optionsChanged();
66+
67+protected:
68+ unity::dash::Filter::Ptr m_unityFilter;
69+ virtual void setUnityFilter(unity::dash::Filter::Ptr unityFilter);
70+};
71+
72+Q_DECLARE_METATYPE(Filter*)
73+
74+#endif // FILTER_H
plugins/Unity/filteroption.cppnew file+91
@@ -0,0 +1,91 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "filteroption.h"
23+
24+// libunity-core
25+#include <UnityCore/Filter.h>
26+
27+FilterOption::FilterOption(unity::dash::FilterOption::Ptr unityFilterOption, QObject *parent) :
28+ AbstractFilterOption(parent), m_unityFilterOption(nullptr)
29+{
30+ setUnityFilterOption(unityFilterOption);
31+}
32+
33+QString FilterOption::id() const
34+{
35+ return QString::fromStdString(m_unityFilterOption->id());
36+}
37+
38+QString FilterOption::name() const
39+{
40+ return QString::fromStdString(m_unityFilterOption->name());
41+}
42+
43+QString FilterOption::iconHint() const
44+{
45+ return QString::fromStdString(m_unityFilterOption->icon_hint());
46+}
47+
48+bool FilterOption::active() const
49+{
50+ return m_unityFilterOption->active();
51+}
52+
53+void FilterOption::setActive(bool active)
54+{
55+ m_unityFilterOption->active = active;
56+}
57+
58+void FilterOption::setUnityFilterOption(unity::dash::FilterOption::Ptr unityFilterOption)
59+{
60+ if (m_unityFilterOption != nullptr) {
61+ m_signals.disconnectAll();
62+ }
63+
64+ m_unityFilterOption = unityFilterOption;
65+
66+ /* Property change signals */
67+ m_signals << m_unityFilterOption->id.changed.connect(sigc::mem_fun(this, &FilterOption::onIdChanged))
68+ << m_unityFilterOption->name.changed.connect(sigc::mem_fun(this, &FilterOption::onNameChanged))
69+ << m_unityFilterOption->icon_hint.changed.connect(sigc::mem_fun(this, &FilterOption::onIconHintChanged))
70+ << m_unityFilterOption->active.changed.connect(sigc::mem_fun(this, &FilterOption::onActiveChanged));
71+}
72+
73+void FilterOption::onIdChanged(const std::string &id)
74+{
75+ Q_EMIT idChanged(QString::fromStdString(id));
76+}
77+
78+void FilterOption::onNameChanged(const std::string &name)
79+{
80+ Q_EMIT nameChanged(QString::fromStdString(name));
81+}
82+
83+void FilterOption::onIconHintChanged(const std::string &iconHint)
84+{
85+ Q_EMIT iconHintChanged(QString::fromStdString(iconHint));
86+}
87+
88+void FilterOption::onActiveChanged(bool active)
89+{
90+ Q_EMIT activeChanged(active);
91+}
plugins/Unity/filteroption.hnew file+61
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef FILTEROPTION_H
22+#define FILTEROPTION_H
23+
24+// Local
25+#include "abstractfilteroption.h"
26+#include "signalslist.h"
27+
28+// libunity-core
29+#include <UnityCore/Filter.h>
30+
31+class Q_DECL_EXPORT FilterOption : public AbstractFilterOption
32+{
33+ Q_OBJECT
34+
35+public:
36+ explicit FilterOption(unity::dash::FilterOption::Ptr unityFilterOption, QObject *parent = nullptr);
37+
38+ /* getters */
39+ QString id() const override;
40+ QString name() const override;
41+ QString iconHint() const override;
42+ bool active() const override;
43+
44+ /* setters */
45+ void setActive(bool active) override;
46+
47+private:
48+ void onIdChanged(const std::string &id);
49+ void onNameChanged(const std::string &name);
50+ void onIconHintChanged(const std::string &iconHint);
51+ void onActiveChanged(bool active);
52+
53+ void setUnityFilterOption(unity::dash::FilterOption::Ptr unityFilterOption);
54+ SignalsList m_signals;
55+
56+ unity::dash::FilterOption::Ptr m_unityFilterOption;
57+};
58+
59+Q_DECLARE_METATYPE(FilterOption*)
60+
61+#endif // FILTEROPTION_H
plugins/Unity/filters.cppnew file+165
@@ -0,0 +1,165 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "filters.h"
23+
24+// Local
25+#include "filter.h"
26+#include "genericoptionsmodel.h"
27+
28+// Qt
29+#include <QDebug>
30+
31+Filters::Filters(unity::dash::Filters::Ptr unityFilters, QObject *parent) :
32+ QAbstractListModel(parent), m_unityFilters(unityFilters)
33+{
34+ for (unsigned int i=0; i<m_unityFilters->count(); i++) {
35+ unity::dash::Filter::Ptr unityFilter = m_unityFilters->FilterAtIndex(i);
36+ addFilter(unityFilter, i);
37+ }
38+ m_unityFilters->filter_added.connect(sigc::mem_fun(this, &Filters::onFilterAdded));
39+ m_unityFilters->filter_changed.connect(sigc::mem_fun(this, &Filters::onFilterChanged));
40+ m_unityFilters->filter_removed.connect(sigc::mem_fun(this, &Filters::onFilterRemoved));
41+}
42+
43+Filters::~Filters()
44+{
45+ while (!m_filters.isEmpty()) {
46+ delete m_filters.takeFirst();
47+ }
48+}
49+
50+int Filters::rowCount(const QModelIndex& parent) const
51+{
52+ Q_UNUSED(parent)
53+
54+ return m_unityFilters->count();
55+}
56+
57+QVariant Filters::data(const QModelIndex& index, int role) const
58+{
59+ if (!index.isValid()) {
60+ return QVariant();
61+ }
62+
63+ Filter* filter = m_filters.at(index.row());
64+ switch (role)
65+ {
66+ case Filters::RoleId:
67+ return filter->id();
68+ case Filters::RoleName:
69+ case Qt::DisplayRole:
70+ return filter->name();
71+ case Filters::RoleIconHint:
72+ return filter->iconHint();
73+ case Filters::RoleRendererName:
74+ return filter->rendererName();
75+ case Filters::RoleVisible:
76+ return filter->visible();
77+ case Filters::RoleCollapsed:
78+ return filter->collapsed();
79+ case Filters::RoleFiltering:
80+ return filter->filtering();
81+ case Filters::RoleOptions:
82+ return QVariant::fromValue(filter->options());
83+ default:
84+ break;
85+ }
86+ return QVariant();
87+}
88+
89+QHash<int, QByteArray> Filters::roleNames() const
90+{
91+ QHash<int, QByteArray> roles;
92+ roles[Filters::RoleId] = "id";
93+ roles[Filters::RoleName] = "name";
94+ roles[Filters::RoleIconHint] = "iconHint";
95+ roles[Filters::RoleRendererName] = "rendererName";
96+ roles[Filters::RoleVisible] = "visible";
97+ roles[Filters::RoleCollapsed] = "collapsed";
98+ roles[Filters::RoleFiltering] = "filtering";
99+ roles[Filters::RoleOptions] = "options";
100+ return roles;
101+}
102+
103+Filter* Filters::getFilter(const QString& id) const
104+{
105+ Q_FOREACH (Filter* filter, m_filters) {
106+ if (filter->id() == id) {
107+ return filter;
108+ }
109+ }
110+ return nullptr;
111+}
112+
113+void Filters::onFilterAdded(unity::dash::Filter::Ptr unityFilter)
114+{
115+ if (unityFilter == nullptr) {
116+ return;
117+ }
118+
119+ int index = m_filters.count();
120+ addFilter(unityFilter, index);
121+}
122+
123+void Filters::onFilterChanged(unity::dash::Filter::Ptr unityFilter)
124+{
125+ if (unityFilter == nullptr) {
126+ return;
127+ }
128+
129+ QModelIndex filterIndex = index(indexForFilter(unityFilter));
130+ Q_EMIT dataChanged(filterIndex, filterIndex);
131+}
132+
133+void Filters::onFilterRemoved(unity::dash::Filter::Ptr unityFilter)
134+{
135+ removeFilter(indexForFilter(unityFilter));
136+}
137+
138+void Filters::addFilter(unity::dash::Filter::Ptr unityFilter, int index)
139+{
140+ beginInsertRows(QModelIndex(), index, index);
141+ Filter* filter = Filter::newFromUnityFilter(unityFilter);
142+ if (filter != nullptr) {
143+ m_filters.insert(index, filter);
144+ }
145+ endInsertRows();
146+}
147+
148+void Filters::removeFilter(int index)
149+{
150+ beginRemoveRows(QModelIndex(), index, index);
151+ Filter* filter = m_filters.takeAt(index);
152+ delete filter;
153+ endRemoveRows();
154+}
155+
156+int Filters::indexForFilter(unity::dash::Filter::Ptr unityFilter)
157+{
158+ for (int index=0; index<m_filters.count(); index++) {
159+ if (m_filters[index]->hasUnityFilter(unityFilter)) {
160+ return index;
161+ }
162+ }
163+ qWarning() << "Filter" << QString::fromStdString(unityFilter->name()) << "not found in local cache.";
164+ return -1;
165+}
plugins/Unity/filters.hnew file+77
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef FILTERS_H
22+#define FILTERS_H
23+
24+// Qt
25+#include <QAbstractListModel>
26+#include <QList>
27+
28+// libunity-core
29+#include <UnityCore/Filters.h>
30+#include <UnityCore/Filter.h>
31+
32+class Filter;
33+
34+class Filters : public QAbstractListModel
35+{
36+ Q_OBJECT
37+
38+ Q_ENUMS(Roles)
39+
40+public:
41+ explicit Filters(unity::dash::Filters::Ptr unityFilters, QObject *parent = nullptr);
42+ ~Filters();
43+
44+ enum Roles {
45+ RoleId = Qt::UserRole,
46+ RoleName,
47+ RoleIconHint,
48+ RoleRendererName,
49+ RoleVisible,
50+ RoleCollapsed,
51+ RoleFiltering,
52+ RoleOptions
53+ };
54+
55+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
56+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
57+ QHash<int, QByteArray> roleNames() const override;
58+
59+ Filter* getFilter(const QString& id) const;
60+
61+private:
62+ void onFilterAdded(unity::dash::Filter::Ptr unityFilter);
63+ void onFilterChanged(unity::dash::Filter::Ptr unityFilter);
64+ void onFilterRemoved(unity::dash::Filter::Ptr unityFilter);
65+
66+ void addFilter(unity::dash::Filter::Ptr unityFilter, int index);
67+ void removeFilter(int index);
68+
69+ int indexForFilter(unity::dash::Filter::Ptr unityFilter);
70+
71+ unity::dash::Filters::Ptr m_unityFilters;
72+ QList<Filter*> m_filters;
73+};
74+
75+Q_DECLARE_METATYPE(Filters*)
76+
77+#endif // FILTERS_H
plugins/Unity/genericoptionsmodel.cppnew file+170
@@ -0,0 +1,170 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// self
21+#include "genericoptionsmodel.h"
22+
23+// local
24+#include "abstractfilteroption.h"
25+
26+GenericOptionsModel::GenericOptionsModel(QObject *parent)
27+ : QAbstractListModel(parent)
28+{
29+}
30+
31+GenericOptionsModel::~GenericOptionsModel()
32+{
33+ Q_FOREACH(auto opt, m_options)
34+ {
35+ opt->deleteLater();
36+ }
37+}
38+
39+QHash<int, QByteArray> GenericOptionsModel::roleNames() const
40+{
41+ QHash<int, QByteArray> roles;
42+ roles[GenericOptionsModel::RoleId] = "id";
43+ roles[GenericOptionsModel::RoleName] = "name";
44+ roles[GenericOptionsModel::RoleIconHint] = "iconHint";
45+ roles[GenericOptionsModel::RoleActive] = "active";
46+ return roles;
47+}
48+
49+QVariant GenericOptionsModel::data(const QModelIndex& index, int role) const
50+{
51+ if (!index.isValid()) {
52+ return QVariant();
53+ }
54+
55+ auto filterOption = m_options[index.row()];
56+ switch (role)
57+ {
58+ case GenericOptionsModel::RoleId:
59+ return filterOption->id();
60+ case GenericOptionsModel::RoleName:
61+ case Qt::DisplayRole:
62+ return filterOption->name();
63+ case GenericOptionsModel::RoleIconHint:
64+ return filterOption->iconHint();
65+ case GenericOptionsModel::RoleActive:
66+ return filterOption->active();
67+ default:
68+ break;
69+ }
70+
71+ return QVariant();
72+}
73+
74+int GenericOptionsModel::rowCount(const QModelIndex& /* parent */) const
75+{
76+ return m_options.size ();
77+}
78+
79+void GenericOptionsModel::setActive(int idx, bool value)
80+{
81+ if (idx >= 0 && 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+
97+AbstractFilterOption* GenericOptionsModel::getRawOption(QVector<AbstractFilterOption *>::size_type idx) const
98+{
99+ if (idx < m_options.size())
100+ {
101+ return m_options[idx];
102+ }
103+ return nullptr;
104+}
105+
106+void GenericOptionsModel::ensureTheOnlyActive(AbstractFilterOption *activeOption)
107+{
108+ if (activeOption->active()) {
109+ // disable all other options
110+ Q_FOREACH(auto opt, m_options) {
111+ if (opt != activeOption && opt->active()) {
112+ opt->setActive(false);
113+ }
114+ }
115+ }
116+}
117+
118+void GenericOptionsModel::addOption(AbstractFilterOption *option, int index)
119+{
120+ m_options.insert(index, option);
121+
122+ connect(option, SIGNAL(idChanged(const QString &)), this, SLOT(onOptionChanged()));
123+ connect(option, SIGNAL(nameChanged(const QString &)), this, SLOT(onOptionChanged()));
124+ connect(option, SIGNAL(iconHintChanged(const QString &)), this, SLOT(onOptionChanged()));
125+ connect(option, SIGNAL(activeChanged(bool)), this, SLOT(onOptionChanged()));
126+ connect(option, SIGNAL(activeChanged(bool)), this, SLOT(onActiveChanged()));
127+}
128+
129+void GenericOptionsModel::removeOption(int index)
130+{
131+ m_options[index]->deleteLater();
132+ m_options.remove(index);
133+}
134+
135+int GenericOptionsModel::indexOf(const QString &option_id)
136+{
137+ int row = 0;
138+ Q_FOREACH(auto opt, m_options)
139+ {
140+ if (opt->id() == option_id)
141+ {
142+ return row;
143+ }
144+ ++row;
145+ }
146+ return -1;
147+}
148+
149+void GenericOptionsModel::onOptionChanged()
150+{
151+ AbstractFilterOption *option = dynamic_cast<AbstractFilterOption*>(QObject::sender());
152+ if (option)
153+ {
154+ int idx = indexOf(option->id());
155+ if (idx >= 0)
156+ {
157+ QModelIndex optionIndex = index(idx);
158+ Q_EMIT dataChanged(optionIndex, optionIndex);
159+ }
160+ }
161+}
162+
163+void GenericOptionsModel::onActiveChanged()
164+{
165+ AbstractFilterOption *option = dynamic_cast<AbstractFilterOption*>(QObject::sender());
166+ if (option)
167+ {
168+ Q_EMIT activeChanged(option);
169+ }
170+}
plugins/Unity/genericoptionsmodel.hnew file+77
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef GENERICOPTIONSMODEL_H
21+#define GENERICOPTIONSMODEL_H
22+
23+// Qt
24+#include <QObject>
25+#include <QAbstractListModel>
26+#include <QHash>
27+#include <QVector>
28+
29+class AbstractFilterOption;
30+
31+class Q_DECL_EXPORT GenericOptionsModel : public QAbstractListModel
32+{
33+ Q_OBJECT
34+
35+ Q_ENUMS(Roles)
36+
37+public:
38+ GenericOptionsModel(QObject *parent = nullptr);
39+ ~GenericOptionsModel();
40+
41+ enum Roles {
42+ RoleId = Qt::UserRole,
43+ RoleName,
44+ RoleIconHint,
45+ RoleActive
46+ };
47+
48+ QHash<int, QByteArray> roleNames() const override;
49+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
50+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
51+
52+ Q_INVOKABLE void setActive(int idx, bool value);
53+ Q_INVOKABLE void setActive(const QString &id, bool value);
54+
55+ AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
56+
57+public Q_SLOTS:
58+ virtual void ensureTheOnlyActive(AbstractFilterOption *activeOption);
59+
60+Q_SIGNALS:
61+ void activeChanged(AbstractFilterOption *option);
62+
63+protected Q_SLOTS:
64+ void onOptionChanged();
65+ void onActiveChanged();
66+
67+protected:
68+ void addOption(AbstractFilterOption *option, int index);
69+ void removeOption(int index);
70+ int indexOf(const QString &option_id);
71+
72+ QVector<AbstractFilterOption *> m_options;
73+};
74+
75+Q_DECLARE_METATYPE(GenericOptionsModel*)
76+
77+#endif // GENERICOPTIONSMODEL_H
plugins/Unity/multirangefilter.cppnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "multirangefilter.h"
23+
24+// local
25+#include "unityoptionsmodel.h"
26+
27+MultiRangeFilter::MultiRangeFilter(QObject *parent) :
28+ Filter(parent), m_unityMultiRangeFilter(nullptr), m_options(nullptr)
29+{
30+}
31+
32+GenericOptionsModel* MultiRangeFilter::options() const
33+{
34+ return m_options;
35+}
36+
37+void MultiRangeFilter::setUnityFilter(unity::dash::Filter::Ptr filter)
38+{
39+ Filter::setUnityFilter(filter);
40+ m_unityMultiRangeFilter = std::dynamic_pointer_cast<unity::dash::MultiRangeFilter>(m_unityFilter);
41+
42+ onOptionsChanged(m_unityMultiRangeFilter->options);
43+}
44+
45+void MultiRangeFilter::onOptionsChanged(unity::dash::MultiRangeFilter::Options options)
46+{
47+ if (m_options != nullptr) {
48+ m_signals.disconnectAll();
49+ delete m_options;
50+ m_options = nullptr;
51+ }
52+
53+ m_options = new UnityOptionsModel(this, options,
54+ m_unityMultiRangeFilter->option_added,
55+ m_unityMultiRangeFilter->option_removed);
56+
57+ connect(m_options, SIGNAL(activeChanged(AbstractFilterOption *)), m_options, SLOT(ensureTheOnlyActive(AbstractFilterOption *)));
58+
59+ /* Property change signals */
60+ m_signals << m_unityMultiRangeFilter->options.changed.connect(sigc::mem_fun(this, &MultiRangeFilter::onOptionsChanged));
61+
62+ Q_EMIT optionsChanged();
63+}
plugins/Unity/multirangefilter.hnew file+60
@@ -0,0 +1,60 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef MULTIRANGEFILTER_H
22+#define MULTIRANGEFILTER_H
23+
24+// Qt
25+#include <QObject>
26+#include <QMetaType>
27+
28+// libunity-core
29+#include <UnityCore/MultiRangeFilter.h>
30+
31+// Local
32+#include "filter.h"
33+#include "signalslist.h"
34+
35+class GenericOptionsModel;
36+
37+class Q_DECL_EXPORT MultiRangeFilter : public Filter
38+{
39+ Q_OBJECT
40+
41+public:
42+ explicit MultiRangeFilter(QObject *parent = nullptr);
43+
44+ /* getters */
45+ GenericOptionsModel* options() const override;
46+
47+protected:
48+ void setUnityFilter(unity::dash::Filter::Ptr filter) override;
49+
50+private:
51+ unity::dash::MultiRangeFilter::Ptr m_unityMultiRangeFilter;
52+ SignalsList m_signals;
53+ GenericOptionsModel* m_options;
54+
55+ void onOptionsChanged(unity::dash::MultiRangeFilter::Options);
56+};
57+
58+Q_DECLARE_METATYPE(MultiRangeFilter*)
59+
60+#endif // MULTIRANGEFILTER_H
plugins/Unity/plugin.cpp+4−1
@@ -17,8 +17,9 @@
17 */
17 */
18
18
19// Qt
19// Qt
20−#include <QtQml>
21#include <QDBusConnection>
20#include <QDBusConnection>
21+#include <QQmlContext>
22+#include <qqml.h>
22
23
23// self
24// self
24#include "plugin.h"
25#include "plugin.h"
@@ -33,6 +34,7 @@
33#include "categories.h"
34#include "categories.h"
34#include "categoryresults.h"
35#include "categoryresults.h"
35#include "bottombarvisibilitycommunicatorshell.h"
36#include "bottombarvisibilitycommunicatorshell.h"
37+#include "genericoptionsmodel.h"
36
38
37// libqtdee
39// libqtdee
38#include "deelistmodel.h"
40#include "deelistmodel.h"
@@ -47,6 +49,7 @@ void UnityPlugin::registerTypes(const char *uri)
47 qmlRegisterUncreatableType<PreviewAction>(uri, 0, 1, "PreviewAction", "Can't create PreviewAction object in QML.");
49 qmlRegisterUncreatableType<PreviewAction>(uri, 0, 1, "PreviewAction", "Can't create PreviewAction object in QML.");
48 qmlRegisterUncreatableType<PreviewInfoHint>(uri, 0, 1, "PreviewInfoHint", "Can't create PreviewInfoHint object in QML.");
50 qmlRegisterUncreatableType<PreviewInfoHint>(uri, 0, 1, "PreviewInfoHint", "Can't create PreviewInfoHint object in QML.");
49 qmlRegisterUncreatableType<SocialPreviewComment>(uri, 0, 1, "SocialPreviewComment", "Can't create SocialPreviewComment object in QML.");
51 qmlRegisterUncreatableType<SocialPreviewComment>(uri, 0, 1, "SocialPreviewComment", "Can't create SocialPreviewComment object in QML.");
52+ qmlRegisterUncreatableType<GenericOptionsModel>(uri, 0, 1, "GenericOptionsModel", "Can't create options model in QML.");
50 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
53 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
51 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
54 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
52 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
55 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
plugins/Unity/radiooptionfilter.cppnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "radiooptionfilter.h"
23+
24+// local
25+#include "unityoptionsmodel.h"
26+
27+RadioOptionFilter::RadioOptionFilter(QObject *parent) :
28+ Filter(parent), m_unityRadioOptionFilter(nullptr), m_options(nullptr)
29+{
30+}
31+
32+GenericOptionsModel* RadioOptionFilter::options() const
33+{
34+ return m_options;
35+}
36+
37+void RadioOptionFilter::setUnityFilter(unity::dash::Filter::Ptr filter)
38+{
39+ Filter::setUnityFilter(filter);
40+ m_unityRadioOptionFilter = std::dynamic_pointer_cast<unity::dash::RadioOptionFilter>(m_unityFilter);
41+
42+ onOptionsChanged(m_unityRadioOptionFilter->options);
43+}
44+
45+void RadioOptionFilter::onOptionsChanged(unity::dash::RadioOptionFilter::RadioOptions)
46+{
47+ if (m_options != nullptr) {
48+ m_signals.disconnectAll();
49+ delete m_options;
50+ m_options = nullptr;
51+ }
52+ m_options = new UnityOptionsModel(this, m_unityRadioOptionFilter->options,
53+ m_unityRadioOptionFilter->option_added,
54+ m_unityRadioOptionFilter->option_removed);
55+
56+ m_signals << m_unityRadioOptionFilter->options.changed.connect(sigc::mem_fun(this, &RadioOptionFilter::onOptionsChanged));
57+
58+ Q_EMIT optionsChanged();
59+}
plugins/Unity/radiooptionfilter.hnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef RADIOOPTIONFILTER_H
22+#define RADIOOPTIONFILTER_H
23+
24+// Qt
25+#include <QObject>
26+#include <QMetaType>
27+
28+// libunity-core
29+#include <UnityCore/RadioOptionFilter.h>
30+
31+// Local
32+#include "filter.h"
33+#include "filteroption.h"
34+#include "signalslist.h"
35+
36+class Q_DECL_EXPORT RadioOptionFilter : public Filter
37+{
38+ Q_OBJECT
39+
40+public:
41+ explicit RadioOptionFilter(QObject *parent = nullptr);
42+
43+ /* getters */
44+ GenericOptionsModel* options() const override;
45+
46+protected:
47+ void setUnityFilter(unity::dash::Filter::Ptr filter) override;
48+
49+private:
50+ unity::dash::RadioOptionFilter::Ptr m_unityRadioOptionFilter;
51+ GenericOptionsModel* m_options;
52+ SignalsList m_signals;
53+
54+ void onOptionsChanged(unity::dash::RadioOptionFilter::RadioOptions);
55+};
56+
57+Q_DECLARE_METATYPE(RadioOptionFilter*)
58+
59+#endif // RADIOOPTIONFILTER_H
plugins/Unity/ratingfilteroption.cppnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// self
21+#include "ratingfilteroption.h"
22+
23+RatingFilterOption::RatingFilterOption(const QString &id, float value, QObject *parent) :
24+ AbstractFilterOption(parent),
25+ m_active(false),
26+ m_id(id),
27+ m_value(value)
28+{
29+}
30+
31+QString RatingFilterOption::id() const
32+{
33+ return m_id;
34+}
35+
36+QString RatingFilterOption::name() const
37+{
38+ return QString::Null();
39+}
40+
41+QString RatingFilterOption::iconHint() const
42+{
43+ return QString::Null();
44+}
45+
46+float RatingFilterOption::value() const
47+{
48+ return m_value;
49+}
50+
51+bool RatingFilterOption::active() const
52+{
53+ return m_active;
54+}
55+
56+void RatingFilterOption::setActive(bool active)
57+{
58+ if (active != m_active) {
59+ m_active = active;
60+ Q_EMIT activeChanged(m_active);
61+ }
62+}
plugins/Unity/ratingfilteroption.hnew file+53
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef RATINGFILTEROPTION_H
21+#define RATINGFILTEROPTION_H
22+
23+// local
24+#include "abstractfilteroption.h"
25+
26+class Q_DECL_EXPORT RatingFilterOption : public AbstractFilterOption
27+{
28+ Q_OBJECT
29+
30+ Q_PROPERTY(float value READ value CONSTANT)
31+
32+public:
33+ explicit RatingFilterOption(const QString &id, float ratingValue, QObject *parent = nullptr);
34+
35+ /* getters */
36+ QString id() const override;
37+ QString name() const override;
38+ QString iconHint() const override;
39+ bool active() const override;
40+ float value() const;
41+
42+ /* setters */
43+ void setActive(bool active) override;
44+
45+private:
46+ bool m_active;
47+ QString m_id;
48+ float m_value;
49+};
50+
51+Q_DECLARE_METATYPE(RatingFilterOption*)
52+
53+#endif // RATINGFILTEROPTION_H
plugins/Unity/ratingoptionsmodel.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// self
21+#include "ratingoptionsmodel.h"
22+
23+// local
24+#include "ratingfilteroption.h"
25+
26+RatingOptionsModel::RatingOptionsModel(QObject *parent) :
27+ GenericOptionsModel(parent)
28+{
29+ for (int i=1; i<=5; i++) {
30+ auto opt = new RatingFilterOption(QString::number(i), i*0.2f, this);
31+ addOption(opt, i-1);
32+ }
33+}
plugins/Unity/ratingoptionsmodel.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef RATINGOPTIONSMODEL_H
21+#define RATINGOPTIONSMODEL_H
22+
23+#include "genericoptionsmodel.h"
24+
25+class RatingOptionsModel : public GenericOptionsModel
26+{
27+ Q_OBJECT
28+
29+public:
30+ RatingOptionsModel(QObject *parent);
31+};
32+
33+#endif // RATINGOPTIONSMODEL_H
plugins/Unity/ratingsfilter.cppnew file+72
@@ -0,0 +1,72 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "ratingsfilter.h"
23+
24+// local
25+#include "ratingfilteroption.h"
26+#include "ratingoptionsmodel.h"
27+
28+RatingsFilter::RatingsFilter(QObject *parent) :
29+ Filter(parent),
30+ m_unityRatingsFilter(nullptr),
31+ m_options(nullptr)
32+{
33+}
34+
35+GenericOptionsModel* RatingsFilter::options() const
36+{
37+ return m_options;
38+}
39+
40+float RatingsFilter::rating() const
41+{
42+ if (m_unityRatingsFilter)
43+ {
44+ return m_unityRatingsFilter->rating;
45+ }
46+ return 0.0f;
47+}
48+
49+void RatingsFilter::setUnityFilter(unity::dash::Filter::Ptr filter)
50+{
51+ Filter::setUnityFilter(filter);
52+ m_unityRatingsFilter = std::dynamic_pointer_cast<unity::dash::RatingsFilter>(m_unityFilter);
53+
54+ delete m_options;
55+
56+ m_options = new RatingOptionsModel(this);
57+ connect(m_options, SIGNAL(activeChanged(AbstractFilterOption *)), m_options, SLOT(ensureTheOnlyActive(AbstractFilterOption *)));
58+ connect(m_options, SIGNAL(activeChanged(AbstractFilterOption *)), this, SLOT(onActiveChanged(AbstractFilterOption *)));
59+
60+ Q_EMIT optionsChanged();
61+}
62+
63+void RatingsFilter::onActiveChanged(AbstractFilterOption *option)
64+{
65+ RatingFilterOption *ratingOption = dynamic_cast<RatingFilterOption*>(option);
66+ if (ratingOption != nullptr)
67+ {
68+ const float val = ratingOption->active() ? ratingOption->value() : 0.0f;
69+ m_unityRatingsFilter->rating = val;
70+ Q_EMIT ratingChanged(val);
71+ }
72+}
plugins/Unity/ratingsfilter.hnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef RATINGSFILTER_H
22+#define RATINGSFILTER_H
23+
24+// Qt
25+#include <QObject>
26+#include <QMetaType>
27+
28+// libunity-core
29+#include <UnityCore/RatingsFilter.h>
30+
31+// Local
32+#include "filter.h"
33+
34+class GenericOptionsModel;
35+class AbstractFilterOption;
36+
37+class Q_DECL_EXPORT RatingsFilter : public Filter
38+{
39+ Q_OBJECT
40+
41+ Q_PROPERTY(float rating READ rating NOTIFY ratingChanged)
42+
43+public:
44+ explicit RatingsFilter(QObject *parent = nullptr);
45+
46+ /* getters */
47+ GenericOptionsModel* options() const override;
48+ float rating() const;
49+
50+Q_SIGNALS:
51+ void ratingChanged(float);
52+
53+protected:
54+ void setUnityFilter(unity::dash::Filter::Ptr filter) override;
55+
56+protected Q_SLOTS:
57+ void onActiveChanged(AbstractFilterOption *option);
58+
59+private:
60+ unity::dash::RatingsFilter::Ptr m_unityRatingsFilter;
61+ GenericOptionsModel *m_options;
62+};
63+
64+Q_DECLARE_METATYPE(RatingsFilter*)
65+
66+#endif // RATINGSFILTER_H
plugins/Unity/scope.cpp+27−9
@@ -35,6 +35,7 @@
35#include <UnityCore/Variant.h>
35#include <UnityCore/Variant.h>
36
36
37#include <libintl.h>
37#include <libintl.h>
38+#include <glib.h>
38
39
39Scope::Scope(QObject *parent) : QObject(parent)
40Scope::Scope(QObject *parent) : QObject(parent)
40 , m_formFactor("phone")
41 , m_formFactor("phone")
@@ -102,6 +103,11 @@ QString Scope::formFactor() const
102 return m_formFactor;
103 return m_formFactor;
103}
104}
104
105
106+Filters* Scope::filters() const
107+{
108+ return m_filters.get();
109+}
110+
105void Scope::setSearchQuery(const QString& search_query)
111void Scope::setSearchQuery(const QString& search_query)
106{
112{
107 /* Checking for m_searchQuery.isNull() which returns true only when the string
113 /* Checking for m_searchQuery.isNull() which returns true only when the string
@@ -209,18 +215,11 @@ void Scope::onPreviewReady(unity::dash::LocalResult const& /* result */, unity::
209
215
210void Scope::fallbackActivate(const QString& uri)
216void Scope::fallbackActivate(const QString& uri)
211{
217{
212− /* FIXME: stripping all content before the first column because for some
213− reason the scopes give uri with junk content at their beginning.
214− */
215− QString tweakedUri = uri;
216− int firstColumnAt = tweakedUri.indexOf(":");
217− tweakedUri.remove(0, firstColumnAt+1);
218−
219 /* Tries various methods to trigger a sensible action for the given 'uri'.
218 /* Tries various methods to trigger a sensible action for the given 'uri'.
220 If it has no understanding of the given scheme it falls back on asking
219 If it has no understanding of the given scheme it falls back on asking
221 Qt to open the uri.
220 Qt to open the uri.
222 */
221 */
223− QUrl url(tweakedUri);
222+ QUrl url(uri);
224 if (url.scheme() == "file") {
223 if (url.scheme() == "file") {
225 /* Override the files place's default URI handler: we want the file
224 /* Override the files place's default URI handler: we want the file
226 manager to handle opening folders, not the dash.
225 manager to handle opening folders, not the dash.
@@ -231,7 +230,25 @@ void Scope::fallbackActivate(const QString& uri)
231 return;
230 return;
232 }
231 }
233 if (url.scheme() == "application") {
232 if (url.scheme() == "application") {
234− // TODO: implement application handling
233+ // get the full path to the desktop file
234+ QString path(url.path().isEmpty() ? url.authority() : url.path());
235+ if (path.startsWith("/")) {
236+ Q_EMIT activateApplication(path);
237+ } else {
238+ // TODO: use the new desktop file finder/parser when it's ready
239+ gchar* full_path = nullptr;
240+ GKeyFile* key_file = g_key_file_new();
241+ QString apps_path = "applications/" + path;
242+ if (g_key_file_load_from_data_dirs(key_file, apps_path.toLocal8Bit().constData(), &full_path,
243+ G_KEY_FILE_NONE, nullptr)) {
244+ path = full_path;
245+ Q_EMIT activateApplication(path);
246+ } else {
247+ qWarning() << "Unable to activate " << path;
248+ }
249+ g_key_file_free(key_file);
250+ g_free(full_path);
251+ }
235 return;
252 return;
236 }
253 }
237
254
@@ -246,6 +263,7 @@ void Scope::setUnityScope(const unity::dash::Scope::Ptr& scope)
246 m_unityScope = scope;
263 m_unityScope = scope;
247
264
248 m_categories->setUnityScope(m_unityScope);
265 m_categories->setUnityScope(m_unityScope);
266+ m_filters.reset(new Filters(m_unityScope->filters, this));
249
267
250 m_unityScope->form_factor = m_formFactor.toStdString();
268 m_unityScope->form_factor = m_formFactor.toStdString();
251 /* Property change signals */
269 /* Property change signals */
plugins/Unity/scope.h+7
@@ -30,6 +30,7 @@
30#include <UnityCore/Results.h>
30#include <UnityCore/Results.h>
31
31
32#include "categories.h"
32#include "categories.h"
33+#include "filters.h"
33
34
34class Preview;
35class Preview;
35
36
@@ -46,6 +47,7 @@ class Scope : public QObject
46 Q_PROPERTY(QString shortcut READ shortcut NOTIFY shortcutChanged)
47 Q_PROPERTY(QString shortcut READ shortcut NOTIFY shortcutChanged)
47 Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
48 Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
48 Q_PROPERTY(Categories* categories READ categories NOTIFY categoriesChanged)
49 Q_PROPERTY(Categories* categories READ categories NOTIFY categoriesChanged)
50+ Q_PROPERTY(Filters* filters READ filters NOTIFY filtersChanged)
49
51
50 Q_PROPERTY(QString searchQuery READ searchQuery WRITE setSearchQuery NOTIFY searchQueryChanged)
52 Q_PROPERTY(QString searchQuery READ searchQuery WRITE setSearchQuery NOTIFY searchQueryChanged)
51 Q_PROPERTY(QString noResultsHint READ noResultsHint WRITE setNoResultsHint NOTIFY noResultsHintChanged)
53 Q_PROPERTY(QString noResultsHint READ noResultsHint WRITE setNoResultsHint NOTIFY noResultsHintChanged)
@@ -64,6 +66,7 @@ public:
64 QString shortcut() const;
66 QString shortcut() const;
65 bool connected() const;
67 bool connected() const;
66 Categories* categories() const;
68 Categories* categories() const;
69+ Filters* filters() const;
67 QString searchQuery() const;
70 QString searchQuery() const;
68 QString noResultsHint() const;
71 QString noResultsHint() const;
69 QString formFactor() const;
72 QString formFactor() const;
@@ -98,6 +101,7 @@ Q_SIGNALS:
98 void searchQueryChanged();
101 void searchQueryChanged();
99 void noResultsHintChanged();
102 void noResultsHintChanged();
100 void formFactorChanged();
103 void formFactorChanged();
104+ void filtersChanged();
101
105
102 // signals triggered by activate(..) or preview(..) requests.
106 // signals triggered by activate(..) or preview(..) requests.
103 void previewReady(Preview *preview);
107 void previewReady(Preview *preview);
@@ -105,6 +109,8 @@ Q_SIGNALS:
105 void hideDash();
109 void hideDash();
106 void gotoUri(const QString &uri);
110 void gotoUri(const QString &uri);
107
111
112+ void activateApplication(const QString &desktop);
113+
108private Q_SLOTS:
114private Q_SLOTS:
109 void synchronizeStates();
115 void synchronizeStates();
110 void onSearchFinished(const std::string &, unity::glib::HintsMap const &);
116 void onSearchFinished(const std::string &, unity::glib::HintsMap const &);
@@ -121,6 +127,7 @@ private:
121
127
122 unity::dash::Scope::Ptr m_unityScope;
128 unity::dash::Scope::Ptr m_unityScope;
123 std::unique_ptr<Categories> m_categories;
129 std::unique_ptr<Categories> m_categories;
130+ std::unique_ptr<Filters> m_filters;
124 QString m_searchQuery;
131 QString m_searchQuery;
125 QString m_noResultsHint;
132 QString m_noResultsHint;
126 QString m_formFactor;
133 QString m_formFactor;
plugins/Unity/signalslist.cppnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#include "signalslist.h"
21+
22+void SignalsList::append(const sigc::connection &c)
23+{
24+ m_signals.append(c);
25+}
26+
27+void SignalsList::disconnectAll()
28+{
29+ Q_FOREACH(auto sig, m_signals) {
30+ sig.disconnect();
31+ }
32+ m_signals.clear();
33+}
34+
35+SignalsList& SignalsList::operator<<(const sigc::connection &c)
36+{
37+ append(c);
38+ return *this;
39+}
plugins/Unity/signalslist.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef SIGNALSLIST_H
21+#define SIGNALSLIST_H
22+
23+#include <QList>
24+#include <sigc++/sigc++.h>
25+
26+class Q_DECL_EXPORT SignalsList
27+{
28+public:
29+ void append(const sigc::connection &c);
30+ void disconnectAll();
31+
32+ SignalsList& operator<<(const sigc::connection &c);
33+
34+private:
35+ QList<sigc::connection> m_signals;
36+};
37+
38+#endif // SIGNALSLIST_H
plugins/Unity/unityoptionsmodel.cppnew file+67
@@ -0,0 +1,67 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// self
21+#include "unityoptionsmodel.h"
22+
23+// local
24+#include "filteroption.h"
25+
26+UnityOptionsModel::UnityOptionsModel(QObject *parent,
27+ const std::vector<unity::dash::FilterOption::Ptr> options,
28+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionAdded,
29+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionRemoved) :
30+ GenericOptionsModel(parent)
31+{
32+ setOptions(options, optionAdded, optionRemoved);
33+}
34+
35+void UnityOptionsModel::setOptions(const std::vector<unity::dash::FilterOption::Ptr> options,
36+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionAdded,
37+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionRemoved)
38+{
39+ for (unsigned int i=0; i<options.size(); i++)
40+ {
41+ addOption(new FilterOption(options[i], this), i);
42+ }
43+ optionAdded.connect(sigc::mem_fun(this, &UnityOptionsModel::onOptionAdded));
44+ optionRemoved.connect(sigc::mem_fun(this, &UnityOptionsModel::onOptionRemoved));
45+}
46+
47+void UnityOptionsModel::onOptionAdded(unity::dash::FilterOption::Ptr option)
48+{
49+ int index = m_options.count();
50+ if (index >= 0)
51+ {
52+ beginInsertRows(QModelIndex(), index, index);
53+ addOption(new FilterOption(option, this), index);
54+ endInsertRows();
55+ }
56+}
57+
58+void UnityOptionsModel::onOptionRemoved(unity::dash::FilterOption::Ptr option)
59+{
60+ auto index = indexOf(QString::fromStdString(option->id));
61+ if (index >= 0)
62+ {
63+ beginRemoveRows(QModelIndex(), index, index);
64+ removeOption(index);
65+ endRemoveRows();
66+ }
67+}
plugins/Unity/unityoptionsmodel.hnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef UNITYOPTIONSMODEL_H
21+#define UNITYOPTIONSMODEL_H
22+
23+#include "genericoptionsmodel.h"
24+
25+// libunity-core
26+#include <UnityCore/Filter.h>
27+
28+#include <sigc++/signal.h>
29+
30+class UnityOptionsModel : public GenericOptionsModel
31+{
32+ Q_OBJECT
33+
34+public:
35+ UnityOptionsModel(QObject *parent,
36+ const std::vector<unity::dash::FilterOption::Ptr> options,
37+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionAdded,
38+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionRemoved);
39+
40+private:
41+ void setOptions(const std::vector<unity::dash::FilterOption::Ptr> options,
42+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionAdded,
43+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionRemoved);
44+
45+ void onOptionAdded(unity::dash::FilterOption::Ptr option);
46+ void onOptionRemoved(unity::dash::FilterOption::Ptr option);
47+};
48+
49+#endif // UNITYOPTIONSMODEL_H
plugins/Utils/plugin.cpp+1−1
@@ -17,7 +17,7 @@
17 */
17 */
18
18
19// Qt
19// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
21#include <QtQuick/QQuickWindow>
21#include <QtQuick/QQuickWindow>
22
22
23// self
23// self
plugins/Utils/qlimitproxymodelqml.cpp+90−4
@@ -25,6 +25,8 @@ QLimitProxyModelQML::QLimitProxyModelQML(QObject *parent)
25 , m_limit(-1)
25 , m_limit(-1)
26 , m_sourceInserting(false)
26 , m_sourceInserting(false)
27 , m_sourceRemoving(false)
27 , m_sourceRemoving(false)
28+ , m_dataChangedBegin(-1)
29+ , m_dataChangedEnd(-1)
28{
30{
29 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
31 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
30 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
32 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
@@ -130,8 +132,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeInserted(const QModelIndex &parent, int
130 beginInsertRows(mapFromSource(parent), start, end);
132 beginInsertRows(mapFromSource(parent), start, end);
131 m_sourceInserting = true;
133 m_sourceInserting = true;
132 } else if (start < m_limit) {
134 } else if (start < m_limit) {
133− beginInsertRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
135+ const int nSourceAddedItems = end - start + 1;
134− m_sourceInserting = true;
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+ }
135 }
174 }
136}
175}
137
176
@@ -142,8 +181,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int s
142 beginRemoveRows(mapFromSource(parent), start, end);
181 beginRemoveRows(mapFromSource(parent), start, end);
143 m_sourceRemoving = true;
182 m_sourceRemoving = true;
144 } else if (start < m_limit) {
183 } else if (start < m_limit) {
145− beginRemoveRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
184+ const int nSourceRemovedItems = end - start + 1;
146− m_sourceRemoving = true;
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+ }
147 }
223 }
148}
224}
149
225
@@ -154,6 +230,11 @@ QLimitProxyModelQML::sourceRowsInserted(const QModelIndex & /*parent*/, int /*st
154 endInsertRows();
230 endInsertRows();
155 m_sourceInserting = false;
231 m_sourceInserting = false;
156 }
232 }
233+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
234+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
235+ m_dataChangedBegin = -1;
236+ m_dataChangedEnd = -1;
237+ }
157}
238}
158
239
159void
240void
@@ -163,4 +244,9 @@ QLimitProxyModelQML::sourceRowsRemoved(const QModelIndex & /*parent*/, int /*sta
163 endRemoveRows();
244 endRemoveRows();
164 m_sourceRemoving = false;
245 m_sourceRemoving = false;
165 }
246 }
247+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
248+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
249+ m_dataChangedBegin = -1;
250+ m_dataChangedEnd = -1;
251+ }
166}
252}
plugins/Utils/qlimitproxymodelqml.h+2
@@ -55,6 +55,8 @@ private:
55 int m_limit;
55 int m_limit;
56 bool m_sourceInserting;
56 bool m_sourceInserting;
57 bool m_sourceRemoving;
57 bool m_sourceRemoving;
58+ int m_dataChangedBegin;
59+ int m_dataChangedEnd;
58};
60};
59
61
60#endif // QLIMITPROXYMODELQML_H
62#endif // QLIMITPROXYMODELQML_H
plugins/Utils/qsortfilterproxymodelqml.cpp+1−1
@@ -52,7 +52,6 @@ QSortFilterProxyModelQML::setModel(QAbstractItemModel *itemModel)
52 connect(itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
52 connect(itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
53 connect(itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
53 connect(itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
54 Q_EMIT totalCountChanged();
54 Q_EMIT totalCountChanged();
55− Q_EMIT countChanged();
56 Q_EMIT modelChanged();
55 Q_EMIT modelChanged();
57 }
56 }
58}
57}
@@ -95,6 +94,7 @@ QSortFilterProxyModelQML::setInvertMatch(bool invertMatch)
95 if (invertMatch != m_invertMatch) {
94 if (invertMatch != m_invertMatch) {
96 m_invertMatch = invertMatch;
95 m_invertMatch = invertMatch;
97 Q_EMIT invertMatchChanged(invertMatch);
96 Q_EMIT invertMatchChanged(invertMatch);
97+ invalidateFilter();
98 }
98 }
99}
99}
100
100
po/ar.po+42−6
@@ -7,15 +7,17 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-26 23:26+0000\n"
11"PO-Revision-Date: 2013-06-26 23:26+0000\n"
12"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
12"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
13"Language-Team: Arabic <ar@li.org>\n"
13"Language-Team: Arabic <ar@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
18−"X-Generator: Launchpad (build 16723)\n"
18+"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
19
21
20#: Applications/applications.js:23
22#: Applications/applications.js:23
21msgid "Phone"
23msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "يوتيوب"
89msgid "Recent searches"
91msgid "Recent searches"
90msgstr "عمليات البحث الأخيرة"
92msgstr "عمليات البحث الأخيرة"
91
93
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92#: Dash/DashApps.qml:65
128#: Dash/DashApps.qml:65
93msgid "Running apps"
129msgid "Running apps"
94msgstr "البرامج المُشغّلة"
130msgstr "البرامج المُشغّلة"
@@ -153,15 +189,15 @@ msgstr "الشائع على الإنترنت"
153msgid "Videos"
189msgid "Videos"
154msgstr "فيديوهات"
190msgstr "فيديوهات"
155
191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
193msgid "Directed by:"
158msgstr "أخرجه:"
194msgstr "أخرجه:"
159
195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
197msgid "Starring:"
162msgstr "النجوم:"
198msgstr "النجوم:"
163
199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
201msgid "Author:"
166msgstr "المؤلف:"
202msgstr "المؤلف:"
167
203
po/az.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-26 23:48+0000\n"
11"PO-Revision-Date: 2013-06-26 23:48+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Azerbaijani <az@li.org>\n"
13"Language-Team: Azerbaijani <az@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Son axtarışlar"
91msgstr "Son axtarışlar"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Videolar"
189msgstr "Videolar"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/bg.po+41−6
@@ -7,7 +7,7 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 00:06+0000\n"
11"PO-Revision-Date: 2013-06-27 00:06+0000\n"
12"Last-Translator: Miroslav E. Hadzhiev (Хаджиев) "
12"Last-Translator: Miroslav E. Hadzhiev (Хаджиев) "
13"<miroslav.hadzhiev@gmail.com>\n"
13"<miroslav.hadzhiev@gmail.com>\n"
@@ -15,8 +15,9 @@ msgstr ""
15"MIME-Version: 1.0\n"
15"MIME-Version: 1.0\n"
16"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Transfer-Encoding: 8bit\n"
17"Content-Transfer-Encoding: 8bit\n"
18−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18+"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"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"
20
21
21#: Applications/applications.js:23
22#: Applications/applications.js:23
22msgid "Phone"
23msgid "Phone"
@@ -90,6 +91,40 @@ msgstr "YouTube"
90msgid "Recent searches"
91msgid "Recent searches"
91msgstr "Скорошни търсения"
92msgstr "Скорошни търсения"
92
93
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
93#: Dash/DashApps.qml:65
128#: Dash/DashApps.qml:65
94msgid "Running apps"
129msgid "Running apps"
95msgstr ""
130msgstr ""
@@ -154,15 +189,15 @@ msgstr ""
154msgid "Videos"
189msgid "Videos"
155msgstr "Видеоклипове"
190msgstr "Видеоклипове"
156
191
157−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
158msgid "Directed by:"
193msgid "Directed by:"
159msgstr ""
194msgstr ""
160
195
161−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
162msgid "Starring:"
197msgid "Starring:"
163msgstr ""
198msgstr ""
164
199
165−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
166msgid "Author:"
201msgid "Author:"
167msgstr "Автор:"
202msgstr "Автор:"
168
203
po/ca.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 00:24+0000\n"
11"PO-Revision-Date: 2013-06-27 00:24+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Catalan <ca@li.org>\n"
13"Language-Team: Catalan <ca@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Cerques recents"
91msgstr "Cerques recents"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Aplications actives"
129msgstr "Aplications actives"
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Vídeos"
189msgstr "Vídeos"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/de.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 00:37+0000\n"
11"PO-Revision-Date: 2013-06-27 00:37+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: German <de@li.org>\n"
13"Language-Team: German <de@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Letzte Suchbegriffe"
91msgstr "Letzte Suchbegriffe"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Aktive Anwendungen"
129msgstr "Aktive Anwendungen"
@@ -153,15 +188,15 @@ msgstr "Online Beliebt"
153msgid "Videos"
188msgid "Videos"
154msgstr "Videos"
189msgstr "Videos"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr "Regie:"
193msgstr "Regie:"
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr "Darsteller:"
197msgstr "Darsteller:"
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr "Autor:"
201msgstr "Autor:"
167
202
po/el.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 00:53+0000\n"
11"PO-Revision-Date: 2013-06-27 00:53+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Greek <el@li.org>\n"
13"Language-Team: Greek <el@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Πρόσφατες αναζητήσεις"
91msgstr "Πρόσφατες αναζητήσεις"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Βίντεο"
189msgstr "Βίντεο"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/en_AU.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 01:13+0000\n"
11"PO-Revision-Date: 2013-06-27 01:13+0000\n"
12"Last-Translator: Jackson Doak <Doak.jackson@gmail.com>\n"
12"Last-Translator: Jackson Doak <Doak.jackson@gmail.com>\n"
13"Language-Team: English (Australia) <en_AU@li.org>\n"
13"Language-Team: English (Australia) <en_AU@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Recent searches"
91msgstr "Recent searches"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Running apps"
129msgstr "Running apps"
@@ -153,15 +188,15 @@ msgstr "Popular Online"
153msgid "Videos"
188msgid "Videos"
154msgstr "Videos"
189msgstr "Videos"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr "Directed by:"
193msgstr "Directed by:"
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr "Starring:"
197msgstr "Starring:"
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr "Author:"
201msgstr "Author:"
167
202
po/en_GB.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 08:53+0000\n"
11"PO-Revision-Date: 2013-06-27 08:53+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
13"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Recent searches"
91msgstr "Recent searches"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Running apps"
129msgstr "Running apps"
@@ -153,15 +188,15 @@ msgstr "Popular Online"
153msgid "Videos"
188msgid "Videos"
154msgstr "Videos"
189msgstr "Videos"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr "Directed by:"
193msgstr "Directed by:"
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr "Starring:"
197msgstr "Starring:"
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr "Author:"
201msgstr "Author:"
167
202
po/eo.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-26 22:35+0000\n"
11"PO-Revision-Date: 2013-06-26 22:35+0000\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Language-Team: Esperanto <eo@li.org>\n"
13"Language-Team: Esperanto <eo@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr ""
91msgstr ""
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr ""
189msgstr ""
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/es.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-07-08 07:25+0000\n"
11"PO-Revision-Date: 2013-07-08 07:25+0000\n"
12"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
12"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
13"Language-Team: Spanish <es@li.org>\n"
13"Language-Team: Spanish <es@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Búsquedas recientes"
91msgstr "Búsquedas recientes"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Aplicaciones en ejecución"
129msgstr "Aplicaciones en ejecución"
@@ -153,15 +188,15 @@ msgstr "Populares en línea"
153msgid "Videos"
188msgid "Videos"
154msgstr "Vídeos"
189msgstr "Vídeos"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr "Dirigida por:"
193msgstr "Dirigida por:"
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr "Protagonistas:"
197msgstr "Protagonistas:"
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr "Autor:"
201msgstr "Autor:"
167
202
po/fi.po+44−9
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11−"PO-Revision-Date: 2013-08-12 17:45+0000\n"
11+"PO-Revision-Date: 2013-08-23 16:15+0000\n"
12−"Last-Translator: Aleksi Kinnunen <Unknown>\n"
12+"Last-Translator: Timo Jyrinki <timo.jyrinki@canonical.com>\n"
13"Language-Team: Finnish <fi@li.org>\n"
13"Language-Team: Finnish <fi@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-13 06:08+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Generator: Launchpad (build 16723)\n"
18+"X-Launchpad-Export-Date: 2013-08-24 05:59+0000\n"
19+"X-Generator: Launchpad (build 16738)\n"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Viimeisimmät haut"
91msgstr "Viimeisimmät haut"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr "(%1)"
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] "%1 arvostelu"
105+msgstr[1] "%1 arvostelua"
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr "Arvioi tämä"
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr "Lisää arvostelu"
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr "Arvostelu"
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr "Lähetä"
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr "Kommentit:"
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Käynnissä olevat sovellukset"
129msgstr "Käynnissä olevat sovellukset"
@@ -153,15 +188,15 @@ msgstr "Suosittua verkossa"
153msgid "Videos"
188msgid "Videos"
154msgstr "Videot"
189msgstr "Videot"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr "Ohjannut:"
193msgstr "Ohjannut:"
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr "Pääosissa:"
197msgstr "Pääosissa:"
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr "Tekijä:"
201msgstr "Tekijä:"
167
202
@@ -209,4 +244,4 @@ msgstr "Hakuun täsmääviä tuloksia ei löydy."
209#: Shell.qml:399
244#: Shell.qml:399
210#, qt-format
245#, qt-format
211msgid "Please enter %1"
246msgid "Please enter %1"
212−msgstr ""
247+msgstr "Kirjoita %1"
po/fo.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 02:57+0000\n"
11"PO-Revision-Date: 2013-06-27 02:57+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Faroese <fo@li.org>\n"
13"Language-Team: Faroese <fo@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr ""
91msgstr ""
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Sjónbond"
189msgstr "Sjónbond"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/fr.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 03:21+0000\n"
11"PO-Revision-Date: 2013-06-27 03:21+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: French <fr@li.org>\n"
13"Language-Team: French <fr@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr ""
91msgstr ""
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Vidéos"
189msgstr "Vidéos"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/gl.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 03:38+0000\n"
11"PO-Revision-Date: 2013-06-27 03:38+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Galician <gl@li.org>\n"
13"Language-Team: Galician <gl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Buscas recentes"
91msgstr "Buscas recentes"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Aplicativos en execución"
129msgstr "Aplicativos en execución"
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Vídeos"
189msgstr "Vídeos"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/he.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 03:54+0000\n"
11"PO-Revision-Date: 2013-06-27 03:54+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Hebrew <he@li.org>\n"
13"Language-Team: Hebrew <he@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "חיפושים אחרונים"
91msgstr "חיפושים אחרונים"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "סרטונים"
189msgstr "סרטונים"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/hr.po+42−6
@@ -7,15 +7,17 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 04:14+0000\n"
11"PO-Revision-Date: 2013-06-27 04:14+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Croatian <hr@li.org>\n"
13"Language-Team: Croatian <hr@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18−"X-Generator: Launchpad (build 16723)\n"
18+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
19
21
20#: Applications/applications.js:23
22#: Applications/applications.js:23
21msgid "Phone"
23msgid "Phone"
@@ -89,6 +91,40 @@ msgstr ""
89msgid "Recent searches"
91msgid "Recent searches"
90msgstr "Nedavne pretrage"
92msgstr "Nedavne pretrage"
91
93
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92#: Dash/DashApps.qml:65
128#: Dash/DashApps.qml:65
93msgid "Running apps"
129msgid "Running apps"
94msgstr ""
130msgstr ""
@@ -153,15 +189,15 @@ msgstr ""
153msgid "Videos"
189msgid "Videos"
154msgstr "Video"
190msgstr "Video"
155
191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
193msgid "Directed by:"
158msgstr ""
194msgstr ""
159
195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
197msgid "Starring:"
162msgstr ""
198msgstr ""
163
199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
201msgid "Author:"
166msgstr ""
202msgstr ""
167
203
po/hu.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 04:28+0000\n"
11"PO-Revision-Date: 2013-06-27 04:28+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Legutóbbi keresések"
91msgstr "Legutóbbi keresések"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Futó alkalmazások"
129msgstr "Futó alkalmazások"
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Videók"
189msgstr "Videók"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/hy.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 04:43+0000\n"
11"PO-Revision-Date: 2013-06-27 04:43+0000\n"
12"Last-Translator: iAbaS <sosabazyan@yandex.ru>\n"
12"Last-Translator: iAbaS <sosabazyan@yandex.ru>\n"
13"Language-Team: Armenian <hy@li.org>\n"
13"Language-Team: Armenian <hy@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Վերջին որոնումներ"
91msgstr "Վերջին որոնումներ"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Բացած ծրագրեր"
129msgstr "Բացած ծրագրեր"
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Վիդեոներ"
189msgstr "Վիդեոներ"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/id.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-26 22:35+0000\n"
11"PO-Revision-Date: 2013-06-26 22:35+0000\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Language-Team: Indonesian <id@li.org>\n"
13"Language-Team: Indonesian <id@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr ""
91msgstr ""
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr ""
189msgstr ""
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/it.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 05:14+0000\n"
11"PO-Revision-Date: 2013-06-27 05:14+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Italian <it@li.org>\n"
13"Language-Team: Italian <it@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Ricerche recenti"
91msgstr "Ricerche recenti"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Video"
189msgstr "Video"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/ja.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-26 22:36+0000\n"
11"PO-Revision-Date: 2013-06-26 22:36+0000\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Language-Team: Japanese <ja@li.org>\n"
13"Language-Team: Japanese <ja@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr ""
91msgstr ""
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr ""
189msgstr ""
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/kn.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 05:43+0000\n"
11"PO-Revision-Date: 2013-06-27 05:43+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Kannada <kn@li.org>\n"
13"Language-Team: Kannada <kn@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "ಇತ್ತೀಚಿನ ಹುಡುಕಾಟಗಳು"
91msgstr "ಇತ್ತೀಚಿನ ಹುಡುಕಾಟಗಳು"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "ವೀಡಿಯೋಗಳು"
189msgstr "ವೀಡಿಯೋಗಳು"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/ko.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 05:59+0000\n"
11"PO-Revision-Date: 2013-06-27 05:59+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Korean <ko@li.org>\n"
13"Language-Team: Korean <ko@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "요즈음 찿은 것들"
91msgstr "요즈음 찿은 것들"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "돌아가고 있는 앱"
129msgstr "돌아가고 있는 앱"
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr ""
189msgstr ""
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/ky.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 06:17+0000\n"
11"PO-Revision-Date: 2013-06-27 06:17+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Kirghiz <ky@li.org>\n"
13"Language-Team: Kirghiz <ky@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Соңку издөөлөр"
91msgstr "Соңку издөөлөр"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Видеолор"
189msgstr "Видеолор"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/lv.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 06:31+0000\n"
11"PO-Revision-Date: 2013-06-27 06:31+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Latvian <lv@li.org>\n"
13"Language-Team: Latvian <lv@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Nesen veiktās meklēšanas"
91msgstr "Nesen veiktās meklēšanas"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Video"
189msgstr "Video"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/my.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 06:47+0000\n"
11"PO-Revision-Date: 2013-06-27 06:47+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Burmese <my@li.org>\n"
13"Language-Team: Burmese <my@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "မကြာသေးခင်က ရှာဖွေခြင်း"
91msgstr "မကြာသေးခင်က ရှာဖွေခြင်း"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "ဗီဒီယိုများ"
189msgstr "ဗီဒီယိုများ"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/nb.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 07:02+0000\n"
11"PO-Revision-Date: 2013-06-27 07:02+0000\n"
12"Last-Translator: Joakim Langvand <jlangvand@gmail.com>\n"
12"Last-Translator: Joakim Langvand <jlangvand@gmail.com>\n"
13"Language-Team: Norwegian Bokmal <nb@li.org>\n"
13"Language-Team: Norwegian Bokmal <nb@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Siste søk"
91msgstr "Siste søk"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Videoer"
189msgstr "Videoer"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/nl.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 07:19+0000\n"
11"PO-Revision-Date: 2013-06-27 07:19+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Dutch <nl@li.org>\n"
13"Language-Team: Dutch <nl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Recente zoekopdrachten"
91msgstr "Recente zoekopdrachten"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Video's"
189msgstr "Video's"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/pl.po+42−6
@@ -7,15 +7,17 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 08:52+0000\n"
11"PO-Revision-Date: 2013-06-27 08:52+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Polish <pl@li.org>\n"
13"Language-Team: Polish <pl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
18−"X-Generator: Launchpad (build 16723)\n"
18+"|| n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
19
21
20#: Applications/applications.js:23
22#: Applications/applications.js:23
21msgid "Phone"
23msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "YouTube"
89msgid "Recent searches"
91msgid "Recent searches"
90msgstr "Ostatnie wyszukiwania"
92msgstr "Ostatnie wyszukiwania"
91
93
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92#: Dash/DashApps.qml:65
128#: Dash/DashApps.qml:65
93msgid "Running apps"
129msgid "Running apps"
94msgstr "Uruchomione aplikacje"
130msgstr "Uruchomione aplikacje"
@@ -153,15 +189,15 @@ msgstr "Popularne w sieci"
153msgid "Videos"
189msgid "Videos"
154msgstr "Filmy"
190msgstr "Filmy"
155
191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
193msgid "Directed by:"
158msgstr "Reżyseria:"
194msgstr "Reżyseria:"
159
195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
197msgid "Starring:"
162msgstr "Obsada:"
198msgstr "Obsada:"
163
199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
201msgid "Author:"
166msgstr "Autor:"
202msgstr "Autor:"
167
203
po/pt_BR.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 07:39+0000\n"
11"PO-Revision-Date: 2013-06-27 07:39+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
13"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Pesquisas recentes"
91msgstr "Pesquisas recentes"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "Vídeos"
189msgstr "Vídeos"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/ro.po+42−6
@@ -7,15 +7,17 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 07:53+0000\n"
11"PO-Revision-Date: 2013-06-27 07:53+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Romanian <ro@li.org>\n"
13"Language-Team: Romanian <ro@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
18−"X-Generator: Launchpad (build 16723)\n"
18+"== 0) && (n != 0))) ? 2: 1));\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
19
21
20#: Applications/applications.js:23
22#: Applications/applications.js:23
21msgid "Phone"
23msgid "Phone"
@@ -89,6 +91,40 @@ msgstr ""
89msgid "Recent searches"
91msgid "Recent searches"
90msgstr "Căutări recente"
92msgstr "Căutări recente"
91
93
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92#: Dash/DashApps.qml:65
128#: Dash/DashApps.qml:65
93msgid "Running apps"
129msgid "Running apps"
94msgstr ""
130msgstr ""
@@ -153,15 +189,15 @@ msgstr ""
153msgid "Videos"
189msgid "Videos"
154msgstr "Video"
190msgstr "Video"
155
191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
193msgid "Directed by:"
158msgstr ""
194msgstr ""
159
195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
197msgid "Starring:"
162msgstr ""
198msgstr ""
163
199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
201msgid "Author:"
166msgstr ""
202msgstr ""
167
203
po/ru.po+42−6
@@ -7,15 +7,17 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-28 06:40+0000\n"
11"PO-Revision-Date: 2013-06-28 06:40+0000\n"
12"Last-Translator: Eugene Marshal <Unknown>\n"
12"Last-Translator: Eugene Marshal <Unknown>\n"
13"Language-Team: Russian <ru@li.org>\n"
13"Language-Team: Russian <ru@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18−"X-Generator: Launchpad (build 16723)\n"
18+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
19
21
20#: Applications/applications.js:23
22#: Applications/applications.js:23
21msgid "Phone"
23msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "YouTube"
89msgid "Recent searches"
91msgid "Recent searches"
90msgstr "Последние поисковые запросы"
92msgstr "Последние поисковые запросы"
91
93
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92#: Dash/DashApps.qml:65
128#: Dash/DashApps.qml:65
93msgid "Running apps"
129msgid "Running apps"
94msgstr "Запущенные приложения"
130msgstr "Запущенные приложения"
@@ -153,15 +189,15 @@ msgstr ""
153msgid "Videos"
189msgid "Videos"
154msgstr "Видео"
190msgstr "Видео"
155
191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
193msgid "Directed by:"
158msgstr "Режиссёр"
194msgstr "Режиссёр"
159
195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
197msgid "Starring:"
162msgstr "В главных ролях"
198msgstr "В главных ролях"
163
199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
201msgid "Author:"
166msgstr "Автор:"
202msgstr "Автор:"
167
203
po/si.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-26 22:36+0000\n"
11"PO-Revision-Date: 2013-06-26 22:36+0000\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Language-Team: Sinhalese <si@li.org>\n"
13"Language-Team: Sinhalese <si@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr ""
91msgstr ""
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr ""
189msgstr ""
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/sl.po+42−6
@@ -7,15 +7,17 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-07-02 12:59+0000\n"
11"PO-Revision-Date: 2013-07-02 12:59+0000\n"
12"Last-Translator: Damir Jerovšek <Unknown>\n"
12"Last-Translator: Damir Jerovšek <Unknown>\n"
13"Language-Team: Slovenian <sl@li.org>\n"
13"Language-Team: Slovenian <sl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
18−"X-Generator: Launchpad (build 16723)\n"
18+"n%100==4 ? 3 : 0);\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
19
21
20#: Applications/applications.js:23
22#: Applications/applications.js:23
21msgid "Phone"
23msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "YouTube"
89msgid "Recent searches"
91msgid "Recent searches"
90msgstr "Nedavna iskanja"
92msgstr "Nedavna iskanja"
91
93
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92#: Dash/DashApps.qml:65
128#: Dash/DashApps.qml:65
93msgid "Running apps"
129msgid "Running apps"
94msgstr ""
130msgstr ""
@@ -153,15 +189,15 @@ msgstr "Priljubljeno na spletu"
153msgid "Videos"
189msgid "Videos"
154msgstr "Videi"
190msgstr "Videi"
155
191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
193msgid "Directed by:"
158msgstr ""
194msgstr ""
159
195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
197msgid "Starring:"
162msgstr ""
198msgstr ""
163
199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
201msgid "Author:"
166msgstr ""
202msgstr ""
167
203
po/sr.po+42−6
@@ -7,15 +7,17 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 09:19+0000\n"
11"PO-Revision-Date: 2013-06-27 09:19+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Serbian <sr@li.org>\n"
13"Language-Team: Serbian <sr@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18−"X-Generator: Launchpad (build 16723)\n"
18+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
19
21
20#: Applications/applications.js:23
22#: Applications/applications.js:23
21msgid "Phone"
23msgid "Phone"
@@ -89,6 +91,40 @@ msgstr ""
89msgid "Recent searches"
91msgid "Recent searches"
90msgstr "Скорашње претраге"
92msgstr "Скорашње претраге"
91
93
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92#: Dash/DashApps.qml:65
128#: Dash/DashApps.qml:65
93msgid "Running apps"
129msgid "Running apps"
94msgstr ""
130msgstr ""
@@ -153,15 +189,15 @@ msgstr ""
153msgid "Videos"
189msgid "Videos"
154msgstr "Видео"
190msgstr "Видео"
155
191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
193msgid "Directed by:"
158msgstr ""
194msgstr ""
159
195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
197msgid "Starring:"
162msgstr ""
198msgstr ""
163
199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
201msgid "Author:"
166msgstr ""
202msgstr ""
167
203
po/sv.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-26 22:36+0000\n"
11"PO-Revision-Date: 2013-06-26 22:36+0000\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Language-Team: Swedish <sv@li.org>\n"
13"Language-Team: Swedish <sv@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr ""
91msgstr ""
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr ""
189msgstr ""
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/te.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 09:57+0000\n"
11"PO-Revision-Date: 2013-06-27 09:57+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Telugu <te@li.org>\n"
13"Language-Team: Telugu <te@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr ""
91msgstr ""
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr ""
129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr ""
189msgstr ""
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
po/tr.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 10:08+0000\n"
11"PO-Revision-Date: 2013-06-27 10:08+0000\n"
12"Last-Translator: Adar Cem EREN <adarcemeren34@gmail.com>\n"
12"Last-Translator: Adar Cem EREN <adarcemeren34@gmail.com>\n"
13"Language-Team: Turkish <tr@li.org>\n"
13"Language-Team: Turkish <tr@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "Son Arananlar"
91msgstr "Son Arananlar"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "Çalışan uygulamalar"
129msgstr "Çalışan uygulamalar"
@@ -153,15 +188,15 @@ msgstr "Genellikle Çevrimiçi"
153msgid "Videos"
188msgid "Videos"
154msgstr "Videolar"
189msgstr "Videolar"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr "Yönetmen"
193msgstr "Yönetmen"
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr "Yazar:"
201msgstr "Yazar:"
167
202
po/ug.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 11:02+0000\n"
11"PO-Revision-Date: 2013-06-27 11:02+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Uyghur <ug@li.org>\n"
13"Language-Team: Uyghur <ug@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "يېقىنقى ئىزدەشلەر"
91msgstr "يېقىنقى ئىزدەشلەر"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "ئىجرا بولۇۋاتقان ئەپلەر"
129msgstr "ئىجرا بولۇۋاتقان ئەپلەر"
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "سىنلار"
189msgstr "سىنلار"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr "ئاپتور:"
201msgstr "ئاپتور:"
167
202
po/uk.po+42−6
@@ -7,15 +7,17 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-07-21 05:47+0000\n"
11"PO-Revision-Date: 2013-07-21 05:47+0000\n"
12"Last-Translator: Ярослав Смук <Smuk.Slavko@yandex.ua>\n"
12"Last-Translator: Ярослав Смук <Smuk.Slavko@yandex.ua>\n"
13"Language-Team: Ukrainian <uk@li.org>\n"
13"Language-Team: Ukrainian <uk@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18−"X-Generator: Launchpad (build 16723)\n"
18+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
19
21
20#: Applications/applications.js:23
22#: Applications/applications.js:23
21msgid "Phone"
23msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "YouTube"
89msgid "Recent searches"
91msgid "Recent searches"
90msgstr "Нещодавні пошуки"
92msgstr "Нещодавні пошуки"
91
93
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92#: Dash/DashApps.qml:65
128#: Dash/DashApps.qml:65
93msgid "Running apps"
129msgid "Running apps"
94msgstr "Запущені програми"
130msgstr "Запущені програми"
@@ -153,15 +189,15 @@ msgstr "Популярне у мережі"
153msgid "Videos"
189msgid "Videos"
154msgstr "Відео"
190msgstr "Відео"
155
191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
193msgid "Directed by:"
158msgstr "Режисер:"
194msgstr "Режисер:"
159
195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
197msgid "Starring:"
162msgstr "Актори:"
198msgstr "Актори:"
163
199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
201msgid "Author:"
166msgstr "Автор:"
202msgstr "Автор:"
167
203
po/zh_CN.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 10:33+0000\n"
11"PO-Revision-Date: 2013-06-27 10:33+0000\n"
12"Last-Translator: Anthony Wong <anthony.wong@canonical.com>\n"
12"Last-Translator: Anthony Wong <anthony.wong@canonical.com>\n"
13"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
13"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "近期搜索"
91msgstr "近期搜索"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "正在运行的应用程序"
129msgstr "正在运行的应用程序"
@@ -153,15 +188,15 @@ msgstr "经常在线"
153msgid "Videos"
188msgid "Videos"
154msgstr "视频"
189msgstr "视频"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr "导演:"
193msgstr "导演:"
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr "主演:"
197msgstr "主演:"
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr "作者:"
201msgstr "作者:"
167
202
po/zh_TW.po+41−6
@@ -7,15 +7,16 @@ msgid ""
7msgstr ""
7msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11"PO-Revision-Date: 2013-06-27 10:24+0000\n"
11"PO-Revision-Date: 2013-06-27 10:24+0000\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
12"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
13"Language-Team: Chinese (Traditional) <zh_TW@li.org>\n"
13"Language-Team: Chinese (Traditional) <zh_TW@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18−"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"
19
20
20#: Applications/applications.js:23
21#: Applications/applications.js:23
21msgid "Phone"
22msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
89msgid "Recent searches"
90msgid "Recent searches"
90msgstr "最近搜尋"
91msgstr "最近搜尋"
91
92
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92#: Dash/DashApps.qml:65
127#: Dash/DashApps.qml:65
93msgid "Running apps"
128msgid "Running apps"
94msgstr "執行中的程式"
129msgstr "執行中的程式"
@@ -153,15 +188,15 @@ msgstr ""
153msgid "Videos"
188msgid "Videos"
154msgstr "影片"
189msgstr "影片"
155
190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157msgid "Directed by:"
192msgid "Directed by:"
158msgstr ""
193msgstr ""
159
194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161msgid "Starring:"
196msgid "Starring:"
162msgstr ""
197msgstr ""
163
198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165msgid "Author:"
200msgid "Author:"
166msgstr ""
201msgstr ""
167
202
src/Panel/Indicators/client/indicatorsclient.cpp+12
@@ -26,6 +26,8 @@
26#include <QQmlEngine>
26#include <QQmlEngine>
27#include <QDebug>
27#include <QDebug>
28
28
29+namespace {
30+
29void prependImportPaths(QQmlEngine *engine, const QStringList &paths)
31void prependImportPaths(QQmlEngine *engine, const QStringList &paths)
30{
32{
31 QStringList importPathList = engine->importPathList();
33 QStringList importPathList = engine->importPathList();
@@ -51,11 +53,21 @@ void appendImportPaths(QQmlEngine *engine, const QStringList &paths)
51 engine->setImportPathList(importPathList);
53 engine->setImportPathList(importPathList);
52}
54}
53
55
56+void resolveIconTheme() {
57+ QString ubuntuIconTheme = getenv("UBUNTU_ICON_THEME");
58+ if (ubuntuIconTheme.isEmpty()) {
59+ QIcon::setThemeName("ubuntu-mobile");
60+ }
61+}
62+} // namespace
63+
54IndicatorsClient::IndicatorsClient(int &argc, char **argv)
64IndicatorsClient::IndicatorsClient(int &argc, char **argv)
55 : QObject(0),
65 : QObject(0),
56 m_view(0)
66 m_view(0)
57{
67{
58 m_application = new QApplication(argc, argv);
68 m_application = new QApplication(argc, argv);
69+ resolveIconTheme();
70+
59 QStringList args = m_application->arguments();
71 QStringList args = m_application->arguments();
60
72
61 m_view = new QQuickView;
73 m_view = new QQuickView;
src/Panel/Indicators/servicefiles/CMakeLists.txt−4
@@ -1,9 +1,5 @@
1file(GLOB INDICATOR_SERVICE_FILES
1file(GLOB INDICATOR_SERVICE_FILES
2− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.battery
3 ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.messages
2 ${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
7)
3)
8
4
9# copy indicators files into build directory for shadow builds
5# copy indicators files into build directory for shadow builds
src/Panel/Indicators/servicefiles/com.canonical.indicator.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 @@
1[Indicator Service]
1[Indicator Service]
2−Name=indicator-messaging
2+Name=indicator-messages
3ObjectPath=/com/canonical/indicator/messages
3ObjectPath=/com/canonical/indicator/messages
4Position=50
4Position=50
5
5
src/Panel/Indicators/servicefiles/com.canonical.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=indicator-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=indicator-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
15from time import sleep
15from time import sleep
16import random
16import random
17import math
17import math
18+import unittest
18
19
19class TestDisplayMenus(IndicatorsTestCase):
20class TestDisplayMenus(IndicatorsTestCase):
20 def setUp(self):
21 def setUp(self):
21 super(TestDisplayMenus, self).setUp(self.geometry, self.grid_unit)
22 super(TestDisplayMenus, self).setUp(self.geometry, self.grid_unit)
22
23
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):
23 # This opens the messaging menu so you don't have to do that in
33 # This opens the messaging menu so you don't have to do that in
24 # every test case
34 # every test case
25 self.assertThat(lambda: self.main_window.get_battery_menu(), Eventually(NotEquals(None)));
35 self.assertThat(lambda: self.main_window.get_battery_menu(), Eventually(NotEquals(None)));
@@ -31,14 +41,6 @@ class TestDisplayMenus(IndicatorsTestCase):
31 page_loader = fn_loader();
41 page_loader = fn_loader();
32 self.assertThat(page_loader.progress, Eventually(Equals(1.0)))
42 self.assertThat(page_loader.progress, Eventually(Equals(1.0)))
33
43
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−
42 def switch_auto_brightness(self, auto_brightness):
44 def switch_auto_brightness(self, auto_brightness):
43 ab_switch = auto_brightness.select_single("CheckBox");
45 ab_switch = auto_brightness.select_single("CheckBox");
44 self.assertThat(ab_switch, NotEquals(None))
46 self.assertThat(ab_switch, NotEquals(None))
@@ -65,8 +67,10 @@ class TestDisplayMenus(IndicatorsTestCase):
65
67
66 old_ab_value = brightness_slider.value
68 old_ab_value = brightness_slider.value
67
69
70+ @unittest.skip("Battery indicator has been removed.")
68 def test_auto_bright_switch(self):
71 def test_auto_bright_switch(self):
69 """Test the auto-bright switch"""
72 """Test the auto-bright switch"""
73+ self.open_battery_menu()
70
74
71 # wait for the switch menu item
75 # wait for the switch menu item
72 fn_auto_brightness = lambda: self.app.select_single("SwitchMenuItem", objectName="auto-brightness");
76 fn_auto_brightness = lambda: self.app.select_single("SwitchMenuItem", objectName="auto-brightness");
@@ -84,8 +88,10 @@ class TestDisplayMenus(IndicatorsTestCase):
84 # TODO - should check backend when it's introspectable.
88 # TODO - should check backend when it's introspectable.
85 self.assertThat(auto_brightness.checked, Eventually(NotEquals(old_ab_value)))
89 self.assertThat(auto_brightness.checked, Eventually(NotEquals(old_ab_value)))
86
90
91+ @unittest.skip("Battery indicator has been removed.")
87 def test_brightness_slider(self):
92 def test_brightness_slider(self):
88 """Test the auto-bright switch"""
93 """Test the auto-bright switch"""
94+ self.open_battery_menu()
89
95
90 fn_brightness_menu = lambda: self.app.select_single("SliderMenuItem", objectName="brightness");
96 fn_brightness_menu = lambda: self.app.select_single("SliderMenuItem", objectName="brightness");
91 # FIXME: this should go away when we switch to indicator-power
97 # FIXME: this should go away when we switch to indicator-power
tests/autopilot/unity8/shell/emulators/create_interactive_notification.pynew file+102
@@ -0,0 +1,102 @@
1+#!/usr/bin/env python
2+
3+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4+#
5+# Unity Autopilot Test Suite
6+# Copyright (C) 2012-2013 Canonical
7+#
8+# This program is free software: you can redistribute it and/or modify
9+# it under the terms of the GNU General Public License as published by
10+# the Free Software Foundation, either version 3 of the License, or
11+# (at your option) any later version.
12+#
13+# This program is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+# GNU General Public License for more details.
17+#
18+# You should have received a copy of the GNU General Public License
19+# along with this program. If not, see <http://www.gnu.org/licenses/>.
20+#
21+
22+import argparse
23+from gi.repository import GLib, Notify
24+import signal
25+
26+
27+def action_callback(notification, action_id, data):
28+ print action_id
29+
30+
31+def quit_callback(notification):
32+ loop.quit()
33+
34+
35+if __name__ == '__main__':
36+ parser = argparse.ArgumentParser(
37+ description='Create an interactive notification'
38+ )
39+
40+ parser.add_argument(
41+ '--summary',
42+ help='summary text of the notification',
43+ default='Summary'
44+ )
45+ parser.add_argument(
46+ '--body',
47+ help='body text of the notification',
48+ default='Body'
49+ )
50+ parser.add_argument(
51+ '--icon',
52+ help='path to the icon to display',
53+ default=None
54+ )
55+ parser.add_argument(
56+ '--action',
57+ help='id and label for the callback in the format: id,label',
58+ action='append',
59+ default=[]
60+ )
61+ parser.add_argument(
62+ '--urgency',
63+ help='LOW, NORMAL, CRITICAL',
64+ choices=['LOW', 'NORMAL', 'CRITICAL'],
65+ default='NORMAL'
66+ )
67+ parser.add_argument(
68+ '--hints',
69+ help='list of comma sep options',
70+ action='append',
71+ default=[]
72+ )
73+
74+ args = parser.parse_args()
75+
76+ Notify.init('Interactive Notifications')
77+
78+ notification = Notify.Notification.new(args.summary, args.body, args.icon)
79+
80+ for hint in args.hints:
81+ key, value = hint.split(',', 1)
82+ notification.set_hint_string(key, value)
83+
84+ for action in args.action:
85+ action_id, action_label = action.split(',', 1)
86+ notification.add_action(
87+ action_id,
88+ action_label,
89+ action_callback,
90+ None,
91+ None
92+ )
93+
94+ def signal_handler(signam, frame):
95+ loop.quit()
96+
97+ signal.signal(signal.SIGINT, signal_handler)
98+
99+ loop = GLib.MainLoop.new(None, False)
100+ notification.connect('closed', quit_callback)
101+ notification.show()
102+ loop.run()
tests/autopilot/unity8/shell/emulators/dash.py+3−3
@@ -25,9 +25,9 @@ class Dash(UnityEmulatorBase):
25 """An emulator that understands the Dash."""
25 """An emulator that understands the Dash."""
26
26
27 def get_home_applications_grid(self):
27 def get_home_applications_grid(self):
28− return self.select_single(
28+ return self.get_scope('home').select_single(
29− "ApplicationsFilterGrid",
29+ "GenericFilterGrid",
30− objectName="dashHomeApplicationsGrid"
30+ objectName="applications.scope"
31 )
31 )
32
32
33 def get_application_icon(self, text):
33 def get_application_icon(self, text):
tests/autopilot/unity8/shell/tests/__init__.py+20−20
@@ -51,26 +51,25 @@ UNITYSHELL_GSETTINGS_PATH = "/org/compiz/profiles/unity/plugins/unityshell/"
51UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
51UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
52UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
52UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
53
53
54−def _get_device_emulation_scenarios():
54+def _get_device_emulation_scenarios(devices='All'):
55+ nexus4 = ('Desktop Nexus 4',
56+ dict(app_width=768, app_height=1280, grid_unit_px=18))
57+ nexus10 = ('Desktop Nexus 10',
58+ dict(app_width=2560, app_height=1600, grid_unit_px=20))
59+ native = ('Native Device',
60+ dict(app_width=0, app_height=0, grid_unit_px=0))
61+
55 if model() == 'Desktop':
62 if model() == 'Desktop':
56− return [
63+ if devices == 'All':
57− (
64+ return [nexus4, nexus10]
58− 'Desktop Nexus 4',
65+ elif devices == 'Nexus4':
59− dict(app_width=768, app_height=1280, grid_unit_px=18)
66+ return [nexus4]
60− ),
67+ elif devices == 'Nexus10':
61− (
68+ return [nexus10]
62− 'Desktop Nexus 10',
69+ else:
63− dict(app_width=2560, app_height=1600, grid_unit_px=20)
70+ raise RuntimeException('Unrecognized device-option "%s" passed.' % devices)
64− ),
65− ]
66 else:
71 else:
67− return [
72+ return [native]
68− (
69− 'Native Device',
70− dict(app_width=0, app_height=0, grid_unit_px=0)
71− )
72− ]
73−
74
73
75class UnityTestCase(AutopilotTestCase):
74class UnityTestCase(AutopilotTestCase):
76
75
@@ -148,7 +147,7 @@ class UnityTestCase(AutopilotTestCase):
148 divisor = divisor * 2
147 divisor = divisor * 2
149 return divisor
148 return divisor
150
149
151− def launch_unity(self):
150+ def launch_unity(self, **kwargs):
152 """Launch the unity shell, return a proxy object for it."""
151 """Launch the unity shell, return a proxy object for it."""
153 binary_path = get_binary_path()
152 binary_path = get_binary_path()
154 lib_path = get_lib_path()
153 lib_path = get_lib_path()
@@ -167,7 +166,8 @@ class UnityTestCase(AutopilotTestCase):
167 binary_path,
166 binary_path,
168 *self.unity_geometry_args,
167 *self.unity_geometry_args,
169 app_type='qt',
168 app_type='qt',
170− emulator_base=UnityEmulatorBase
169+ emulator_base=UnityEmulatorBase,
170+ **kwargs
171 )
171 )
172 self._set_proxy(app_proxy)
172 self._set_proxy(app_proxy)
173
173
tests/autopilot/unity8/shell/tests/test_lock_screen.py+17−2
@@ -145,7 +145,9 @@ class TestLockscreen(UnityTestCase):
145
145
146 pinentryField = self.main_window.get_pinentryField()
146 pinentryField = self.main_window.get_pinentryField()
147 self.touch.tap_object(pinentryField)
147 self.touch.tap_object(pinentryField)
148− self.keyboard.type(passphrase)
148+ self.assertThat(pinentryField.activeFocus, Eventually(Equals(True)))
149+ for character in passphrase:
150+ self._type_character(character, pinentryField)
149 logger.debug("Typed passphrase: %s", pinentryField.text)
151 logger.debug("Typed passphrase: %s", pinentryField.text)
150 self.keyboard.type("\n")
152 self.keyboard.type("\n")
151
153
@@ -162,6 +164,19 @@ class TestLockscreen(UnityTestCase):
162
164
163 prompt = self.main_window.get_greeter().get_prompt()
165 prompt = self.main_window.get_greeter().get_prompt()
164 self.touch.tap_object(prompt)
166 self.touch.tap_object(prompt)
165− self.keyboard.type(passphrase)
167+ self.assertThat(prompt.activeFocus, Eventually(Equals(True)))
168+ for character in passphrase:
169+ self._type_character(character, prompt)
166 logger.debug("Typed passphrase: %s", prompt.text)
170 logger.debug("Typed passphrase: %s", prompt.text)
167 self.keyboard.type("\n")
171 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)
181+ else:
182+ raise
tests/autopilot/unity8/shell/tests/test_notifications.pynew file+737
@@ -0,0 +1,737 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Tests for Notifications"""
21+
22+from __future__ import absolute_import
23+
24+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
25+
26+from testtools.matchers import Equals, NotEquals
27+from autopilot.matchers import Eventually
28+
29+from gi.repository import Notify
30+import time
31+import os
32+import logging
33+import signal
34+import subprocess
35+
36+logger = logging.getLogger(__name__)
37+
38+
39+class NotificationsBase(UnityTestCase):
40+ """Base class for all notification tests that provides helper methods."""
41+
42+ scenarios = _get_device_emulation_scenarios('Nexus4')
43+
44+ def _get_icon_path(self, icon_name):
45+ """Given an icons file name returns the full path (either system or
46+ source tree.
47+
48+ Consider the graphics directory as root so for example (runnign tests
49+ from installed unity8-autopilot package):
50+ >>> self.get_icon_path('clock.png')
51+ /usr/share/unity8/graphics/clock.png
52+
53+ >>> self.get_icon_path('applicationIcons/facebook.png')
54+ /usr/share/unity8/graphics/applicationIcons/facebook.png
55+
56+ """
57+ if os.path.abspath(__file__).startswith('/usr/'):
58+ return '/usr/share/unity8/graphics/' + icon_name
59+ else:
60+ return os.path.abspath(
61+ os.getcwd() + "/../../graphics/" + icon_name
62+ )
63+
64+ def _get_notifications_list(self):
65+ main_view = self.main_window.get_qml_view()
66+ return main_view.select_single(
67+ "QQuickListView",
68+ objectName='notificationList'
69+ )
70+
71+ def _assert_notification(
72+ self,
73+ notification,
74+ summary=None,
75+ body=None,
76+ icon=True,
77+ secondary_icon=False,
78+ opacity=None
79+ ):
80+ """Assert that the expected qualities of a notification are as
81+ expected.
82+
83+ """
84+
85+ if summary is not None:
86+ self.assertThat(notification.summary, Eventually(Equals(summary)))
87+
88+ if body is not None:
89+ self.assertThat(notification.body, Eventually(Equals(body)))
90+
91+ if icon:
92+ self.assertThat(notification.iconSource, Eventually(NotEquals("")))
93+ else:
94+ self.assertThat(notification.iconSource, Eventually(Equals("")))
95+
96+ if secondary_icon:
97+ self.assertThat(
98+ notification.secondaryIconSource,
99+ Eventually(NotEquals(""))
100+ )
101+ else:
102+ self.assertThat(
103+ notification.secondaryIconSource,
104+ Eventually(Equals(""))
105+ )
106+
107+ if opacity is not None:
108+ self.assertThat(notification.opacity, Eventually(Equals(opacity)))
109+
110+
111+class InteractiveNotificationBase(NotificationsBase):
112+ """Collection of test for Interactive tests including snap decisions."""
113+
114+ def setUp(self):
115+ super(InteractiveNotificationBase, self).setUp()
116+ # Need to keep track when we launch the notification script.
117+ self._notify_proc = None
118+
119+ def test_interactive(self):
120+ """Interactive notification must react upon click on itself."""
121+ self.launch_unity()
122+ greeter = self.main_window.get_greeter()
123+ greeter.swipe()
124+
125+ notify_list = self._get_notifications_list()
126+
127+ summary = "Interactive notification"
128+ body = "This notification can be clicked on to trigger an action."
129+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
130+ actions = [("action_id", "dummy")]
131+ hints = [
132+ ("x-canonical-switch-to-application", "true"),
133+ (
134+ "x-canonical-secondary-icon",
135+ self._get_icon_path('applicationIcons/phone-app.png')
136+ )
137+ ]
138+
139+ self._create_interactive_notification(
140+ summary,
141+ body,
142+ icon_path,
143+ "NORMAL",
144+ actions,
145+ hints,
146+ )
147+
148+ get_notification = lambda: notify_list.select_single('Notification')
149+ self.assertThat(get_notification, Eventually(NotEquals(None)))
150+ notification = get_notification()
151+
152+ self.touch.tap_object(
153+ notification.select_single(objectName="interactiveArea")
154+ )
155+
156+ self.assert_notification_action_id_was_called('action_id')
157+
158+ def test_sd_incoming_call(self):
159+ """Rejecting a call should make notification expand and offer more options."""
160+ self.launch_unity()
161+ greeter = self.main_window.get_greeter()
162+ greeter.swipe()
163+
164+ notify_list = self._get_notifications_list()
165+
166+ summary = "Incoming call"
167+ body = "Frank Zappa\n+44 (0)7736 027340"
168+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
169+ hints = [
170+ (
171+ "x-canonical-secondary-icon",
172+ self._get_icon_path('applicationIcons/phone-app.png')
173+ ),
174+ ("x-canonical-snap-decisions", "true"),
175+ ]
176+
177+ actions = [
178+ ('action_accept', 'Accept'),
179+ ('action_decline_1', 'Decline'),
180+ ('action_decline_2', '"Can\'t talk now, what\'s up?"'),
181+ ('action_decline_3', '"I call you back."'),
182+ ('action_decline_4', 'Send custom message...'),
183+ ]
184+
185+ self._create_interactive_notification(
186+ summary,
187+ body,
188+ icon_path,
189+ "NORMAL",
190+ actions,
191+ hints
192+ )
193+
194+ get_notification = lambda: notify_list.select_single('Notification')
195+ self.assertThat(get_notification, Eventually(NotEquals(None)))
196+ notification = get_notification()
197+ self._assert_notification(notification, None, None, True, True, 1.0)
198+ initial_height = notification.height
199+ self.touch.tap_object(notification.select_single(objectName="button1"))
200+ self.assertThat(
201+ notification.height,
202+ Eventually(Equals(initial_height +
203+ 3 * notification.select_single(objectName="buttonColumn").spacing +
204+ 3 * notification.select_single(objectName="button4").height)))
205+ self.touch.tap_object(notification.select_single(objectName="button4"))
206+ self.assert_notification_action_id_was_called("action_decline_4")
207+
208+ def _create_interactive_notification(
209+ self,
210+ summary="",
211+ body="",
212+ icon=None,
213+ urgency="NORMAL",
214+ actions=[],
215+ hints=[]
216+ ):
217+ """Create a interactive notification command.
218+
219+ :param summary: Summary text for the notification
220+ :param body: Body text to display in the notification
221+ :param icon: Path string to the icon to use
222+ :param urgency: Urgency string for the noticiation, either: 'LOW',
223+ 'NORMAL', 'CRITICAL'
224+ :param actions: List of tuples containing the 'id' and 'label' for all
225+ the actions to add
226+ :param hint_strings: List of tuples containing the 'name' and value for
227+ setting the hint strings for the notification
228+
229+ """
230+
231+ logger.info(
232+ "Creating snap-decision notification with summary(%s), body(%s) "
233+ "and urgency(%r)",
234+ summary,
235+ body,
236+ urgency
237+ )
238+
239+ script_args = [
240+ '--summary', summary,
241+ '--body', body,
242+ '--urgency', urgency
243+ ]
244+
245+ if icon is not None:
246+ script_args.extend(['--icon', icon])
247+
248+ for hint in hints:
249+ key, value = hint
250+ script_args.extend(['--hint', "%s,%s" % (key, value)])
251+
252+ for action in actions:
253+ action_id, action_label = action
254+ action_string = "%s,%s" % (action_id, action_label)
255+ script_args.extend(['--action', action_string])
256+
257+ python_bin = subprocess.check_output(['which', 'python']).strip()
258+ command = [python_bin, self._get_notify_script()] + script_args
259+ logger.info("Launching snap-decision notification as: %s", command)
260+ self._notify_proc = subprocess.Popen(
261+ command,
262+ stdin=subprocess.PIPE,
263+ stdout=subprocess.PIPE,
264+ stderr=subprocess.PIPE,
265+ close_fds=True,
266+ )
267+
268+ self.addCleanup(self._tidy_up_script_process)
269+
270+ poll_result = self._notify_proc.poll()
271+ if poll_result is not None and self._notify_proc.returncode != 0:
272+ error_output = self._notify_proc.communicate()[1]
273+ raise RuntimeError("Call to script failed with: %s" % error_output)
274+
275+ def _get_notify_script(self):
276+ """Returns the path to the interactive notification creation script."""
277+ file_path = "../../emulators/create_interactive_notification.py"
278+
279+ the_path = os.path.abspath(
280+ os.path.join(__file__, file_path))
281+
282+ return the_path
283+
284+ def _tidy_up_script_process(self):
285+ if self._notify_proc is not None and self._notify_proc.poll() is None:
286+ logger.error("Notification process wasn't killed, killing now.")
287+ os.killpg(self._notify_proc.pid, signal.SIGTERM)
288+
289+ def assert_notification_action_id_was_called(self, action_id, timeout=10):
290+ """Assert that the interactive notification callback of id *action_id*
291+ was called.
292+
293+ :raises AssertionError: If no interactive notification has actually
294+ been created.
295+ :raises AssertionError: When *action_id* does not match the actual
296+ returned.
297+ :raises AssertionError: If no callback was called at all.
298+ """
299+
300+ if self._notify_proc is None:
301+ raise AssertionError("No interactive notification was created.")
302+
303+ for i in xrange(timeout):
304+ self._notify_proc.poll()
305+ if self._notify_proc.returncode is not None:
306+ output = self._notify_proc.communicate()
307+ actual_action_id = output[0].strip("\n")
308+ if actual_action_id != action_id:
309+ raise AssertionError(
310+ "action id '%s' does not match actual returned '%s'"
311+ % (action_id, actual_action_id)
312+ )
313+ else:
314+ return
315+ time.sleep(1)
316+
317+ os.killpg(self._notify_proc.pid, signal.SIGTERM)
318+ self._notify_proc = None
319+ raise AssertionError(
320+ "No callback was called, killing interactivenotification script"
321+ )
322+
323+
324+class EphemeralNotificationsTests(NotificationsBase):
325+ """Collection of tests for Emphemeral notifications (non-interactive.)"""
326+
327+ def setUp(self):
328+ super(EphemeralNotificationsTests, self).setUp()
329+ # Because we are using the Notify library we need to init and un-init
330+ # otherwise we get crashes.
331+ Notify.init("Autopilot Ephemeral Notification Tests")
332+ self.addCleanup(Notify.uninit)
333+
334+ def test_icon_summary_body(self):
335+ """Notification must display the expected summary and body text."""
336+ self.launch_unity()
337+ greeter = self.main_window.get_greeter()
338+ greeter.swipe()
339+
340+ notify_list = self._get_notifications_list()
341+
342+ summary = "Icon-Summary-Body"
343+ body = "Hey pal, what's up with the party next weekend? Will you " \
344+ "join me and Anna?"
345+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
346+ hints = [
347+ (
348+ "x-canonical-secondary-icon",
349+ self._get_icon_path('applicationIcons/phone-app.png')
350+ )
351+ ]
352+
353+ notification = self._create_ephemeral_notification(
354+ summary,
355+ body,
356+ icon_path,
357+ hints,
358+ "NORMAL",
359+ )
360+
361+ notification.show()
362+
363+ get_notification = lambda: notify_list.select_single('Notification')
364+ self.assertThat(get_notification, Eventually(NotEquals(None)))
365+ notification = get_notification()
366+ self._assert_notification(notification, summary, body, True, True, 1.0)
367+
368+ def test_icon_summary(self):
369+ """Notification must display the expected summary and secondary icon."""
370+ self.launch_unity()
371+ greeter = self.main_window.get_greeter()
372+ greeter.swipe()
373+
374+ notify_list = self._get_notifications_list()
375+
376+ summary = "Upload of image completed"
377+ hints = [
378+ (
379+ "x-canonical-secondary-icon",
380+ self._get_icon_path('applicationIcons/facebook.png')
381+ )
382+ ]
383+
384+ notification = self._create_ephemeral_notification(
385+ summary,
386+ None,
387+ None,
388+ hints,
389+ "NORMAL",
390+ )
391+
392+ notification.show()
393+
394+ get_notification = lambda: notify_list.select_single('Notification')
395+ self.assertThat(get_notification, Eventually(NotEquals(None)))
396+ notification = get_notification()
397+ self._assert_notification(
398+ notification,
399+ summary,
400+ None,
401+ False,
402+ True,
403+ 1.0
404+ )
405+
406+ def test_urgency_order(self):
407+ """Notifications must be displayed in order according to their urgency."""
408+ self.launch_unity()
409+ greeter = self.main_window.get_greeter()
410+ greeter.swipe()
411+
412+ notify_list = self._get_notifications_list()
413+
414+ summary_low = 'Low Urgency'
415+ body_low = "No, I'd rather see paint dry, pal *yawn*"
416+ icon_path_low = self._get_icon_path('avatars/amanda.png')
417+
418+ summary_normal = 'Normal Urgency'
419+ body_normal = "Hey pal, what's up with the party next weekend? Will " \
420+ "you join me and Anna?"
421+ icon_path_normal = self._get_icon_path('avatars/funky.png')
422+
423+ summary_critical = 'Critical Urgency'
424+ body_critical = 'Dude, this is so urgent you have no idea :)'
425+ icon_path_critical = self._get_icon_path('avatars/anna_olsson.png')
426+
427+ notification_normal = self._create_ephemeral_notification(
428+ summary_normal,
429+ body_normal,
430+ icon_path_normal,
431+ urgency="NORMAL"
432+ )
433+ notification_normal.show()
434+
435+ notification_low = self._create_ephemeral_notification(
436+ summary_low,
437+ body_low,
438+ icon_path_low,
439+ urgency="LOW"
440+ )
441+ notification_low.show()
442+
443+ notification_critical = self._create_ephemeral_notification(
444+ summary_critical,
445+ body_critical,
446+ icon_path_critical,
447+ urgency="CRITICAL"
448+ )
449+ notification_critical.show()
450+
451+ get_notification = lambda: notify_list.select_single(
452+ 'Notification',
453+ summary=summary_critical
454+ )
455+ self.assertThat(get_notification, Eventually(NotEquals(None)))
456+
457+ notification = get_notification()
458+ self._assert_notification(
459+ notification,
460+ summary_critical,
461+ body_critical,
462+ True,
463+ False,
464+ 1.0
465+ )
466+
467+ get_normal_notification = lambda: notify_list.select_single(
468+ 'Notification',
469+ summary=summary_normal
470+ )
471+ self.assertThat(get_normal_notification, Eventually(NotEquals(None)))
472+ notification = get_normal_notification()
473+ self._assert_notification(
474+ notification,
475+ summary_normal,
476+ body_normal,
477+ True,
478+ False,
479+ 1.0
480+ )
481+
482+ get_low_notification = lambda: notify_list.select_single(
483+ 'Notification',
484+ summary=summary_low
485+ )
486+ self.assertThat(get_low_notification, Eventually(NotEquals(None)))
487+ notification = get_low_notification()
488+ self._assert_notification(
489+ notification,
490+ summary_low,
491+ body_low,
492+ True,
493+ False,
494+ 1.0
495+ )
496+
497+ def test_summary_and_body(self):
498+ """Notification must display the expected summary- and body-text."""
499+ self.launch_unity()
500+ greeter = self.main_window.get_greeter()
501+ greeter.swipe()
502+
503+ notify_list = self._get_notifications_list()
504+
505+ summary = 'Summary-Body'
506+ body = 'This is a superfluous notification'
507+
508+ notification = self._create_ephemeral_notification(summary, body)
509+ notification.show()
510+
511+ get_notification = lambda: notify_list.select_single('Notification')
512+ self.assertThat(get_notification, Eventually(NotEquals(None)))
513+ notification = get_notification()
514+ self._assert_notification(
515+ notification,
516+ summary,
517+ body,
518+ False,
519+ False,
520+ 1.0
521+ )
522+
523+ def test_summary_only(self):
524+ """Notification must display only the expected summary-text."""
525+ self.launch_unity()
526+ greeter = self.main_window.get_greeter()
527+ greeter.swipe()
528+
529+ notify_list = self._get_notifications_list()
530+
531+ summary = 'Summary-Only'
532+
533+ notification = self._create_ephemeral_notification(summary)
534+ notification.show()
535+
536+ get_notification = lambda: notify_list.select_single('Notification')
537+ self.assertThat(get_notification, Eventually(NotEquals(None)))
538+ notification = get_notification()
539+
540+ self._assert_notification(notification, summary, '', False, False, 1.0)
541+
542+ def test_update_notification_same_layout(self):
543+ """Notification must allow updating its contents while being displayed."""
544+ self.launch_unity()
545+ greeter = self.main_window.get_greeter()
546+ greeter.swipe()
547+
548+ notify_list = self._get_notifications_list()
549+
550+ summary = 'Initial notification'
551+ body = 'This is the original content of this notification-bubble.'
552+ icon_path = self._get_icon_path('avatars/funky.png')
553+
554+ notification = self._create_ephemeral_notification(
555+ summary,
556+ body,
557+ icon_path
558+ )
559+ notification.show()
560+
561+ get_notification = lambda: notify_list.select_single('Notification', summary=summary)
562+ self.assertThat(get_notification, Eventually(NotEquals(None)))
563+ self._assert_notification(
564+ get_notification(),
565+ summary,
566+ body,
567+ True,
568+ False,
569+ 1.0
570+ )
571+
572+ summary = 'Updated notification'
573+ body = 'Here the same bubble with new title- and body-text, even ' \
574+ 'the icon can be changed on the update.'
575+ icon_path = self._get_icon_path('avatars/amanda.png')
576+ notification.update(summary, body, icon_path)
577+ notification.show()
578+ self.assertThat(get_notification, Eventually(NotEquals(None)))
579+ self._assert_notification(get_notification(), summary, body)
580+
581+ def test_update_notification_layout_change(self):
582+ """Notification must allow updating its contents and layout while being displayed."""
583+ self.launch_unity()
584+ greeter = self.main_window.get_greeter()
585+ greeter.swipe()
586+
587+ notify_list = self._get_notifications_list()
588+
589+ summary = 'Initial layout'
590+ body = 'This bubble uses the icon-title-body layout with a ' \
591+ 'secondary icon.'
592+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
593+ hint_icon = self._get_icon_path('applicationIcons/phone-app.png')
594+
595+ notification = self._create_ephemeral_notification(
596+ summary,
597+ body,
598+ icon_path
599+ )
600+ notification.set_hint_string(
601+ 'x-canonical-secondary-icon',
602+ hint_icon
603+ )
604+ notification.show()
605+
606+ get_notification = lambda: notify_list.select_single('Notification')
607+ self.assertThat(get_notification, Eventually(NotEquals(None)))
608+ self._assert_notification(
609+ get_notification(),
610+ summary,
611+ body,
612+ True,
613+ True,
614+ 1.0
615+ )
616+
617+ notification.clear_hints()
618+ summary = 'Updated layout'
619+ body = 'After the update we now have a bubble using the title-body ' \
620+ 'layout.'
621+ notification.update(summary, body, None)
622+ notification.show()
623+
624+ self.assertThat(get_notification, Eventually(NotEquals(None)))
625+ self._assert_notification(get_notification(), summary, body, False)
626+
627+ def test_append_hint(self):
628+ """Notification has to accumulate body-text using append-hint."""
629+ self.launch_unity()
630+ greeter = self.main_window.get_greeter()
631+ greeter.swipe()
632+
633+ notify_list = self._get_notifications_list()
634+
635+ summary = 'Cole Raby'
636+ body = 'Hey Bro Coly!'
637+ icon_path = self._get_icon_path('avatars/amanda.png')
638+ body_sum = body
639+ notification = self._create_ephemeral_notification(
640+ summary,
641+ body,
642+ icon_path,
643+ hints=[('x-canonical-append', 'true')]
644+ )
645+
646+ notification.show()
647+
648+ get_notification = lambda: notify_list.select_single('Notification')
649+ self.assertThat(get_notification, Eventually(NotEquals(None)))
650+ notification = get_notification()
651+ self._assert_notification(
652+ notification,
653+ summary,
654+ body_sum,
655+ True,
656+ False,
657+ 1.0
658+ )
659+
660+ bodies = [
661+ 'What\'s up dude?',
662+ 'Did you watch the air-race in Oshkosh last week?',
663+ 'Phil owned the place like no one before him!',
664+ 'Did really everything in the race work according to regulations?',
665+ 'Somehow I think to remember Burt Williams did cut corners and was not punished for this.',
666+ 'Hopefully the referees will watch the videos of the race.',
667+ 'Burt could get fined with US$ 50000 for that rule-violation :)'
668+ ]
669+
670+ for new_body in bodies:
671+ body = new_body
672+ body_sum += '\n' + body
673+ notification = self._create_ephemeral_notification(
674+ summary,
675+ body,
676+ icon_path,
677+ hints=[('x-canonical-append', 'true')]
678+ )
679+ notification.show()
680+ get_notification = lambda: notify_list.select_single(
681+ 'Notification'
682+ )
683+ self.assertThat(get_notification, Eventually(NotEquals(None)))
684+ notification = get_notification()
685+ self._assert_notification(
686+ notification,
687+ summary,
688+ body_sum,
689+ True,
690+ False,
691+ 1.0
692+ )
693+
694+ def _create_ephemeral_notification(
695+ self,
696+ summary="",
697+ body="",
698+ icon=None,
699+ hints=[],
700+ urgency="NORMAL"
701+ ):
702+ """Create an ephemeral (non-interactive) notification
703+
704+ :param summary: Summary text for the notification
705+ :param body: Body text to display in the notification
706+ :param icon: Path string to the icon to use
707+ :param hint_strings: List of tuples containing the 'name' and value
708+ for setting the hint strings for the notification
709+ :param urgency: Urgency string for the noticiation, either: 'LOW',
710+ 'NORMAL', 'CRITICAL'
711+
712+ """
713+ logger.info(
714+ "Creating ephemeral: summary(%s), body(%s), urgency(%r) "
715+ "and Icon(%s)",
716+ summary,
717+ body,
718+ urgency,
719+ icon
720+ )
721+
722+ n = Notify.Notification.new(summary, body, icon)
723+
724+ for hint in hints:
725+ key, value = hint
726+ n.set_hint_string(key, value)
727+ logger.info("Adding hint to notification: (%s, %s)", key, value)
728+ n.set_urgency(self._get_urgency(urgency))
729+
730+ return n
731+
732+ def _get_urgency(self, urgency):
733+ """Translates urgency string to enum."""
734+ _urgency_enums = {'LOW': Notify.Urgency.LOW,
735+ 'NORMAL': Notify.Urgency.NORMAL,
736+ 'CRITICAL': Notify.Urgency.CRITICAL}
737+ return _urgency_enums.get(urgency.upper())
tests/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+3−1
@@ -1,6 +1,8 @@
1+add_subdirectory(GSettings.1.0)
2+add_subdirectory(HudClient)
1add_subdirectory(libusermetrics)
3add_subdirectory(libusermetrics)
2add_subdirectory(LightDM)
4add_subdirectory(LightDM)
5+add_subdirectory(Powerd)
3add_subdirectory(QMenuModel)
6add_subdirectory(QMenuModel)
4add_subdirectory(Ubuntu)
7add_subdirectory(Ubuntu)
5add_subdirectory(Unity)
8add_subdirectory(Unity)
6−add_subdirectory(HudClient)
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 <QObject>
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/qqml.h>
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/CMakeLists.txtnew file+21
@@ -0,0 +1,21 @@
1+add_definitions(-DSM_BUSNAME=sessionBus)
2+
3+add_library(MockPowerd-qml MODULE
4+ plugin.cpp
5+ Powerd.cpp
6+ )
7+
8+qt5_use_modules(MockPowerd-qml Qml)
9+
10+# copy qmldir file into build directory for shadow builds
11+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
12+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
13+ )
14+
15+install(TARGETS MockPowerd-qml
16+ DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Powerd
17+ )
18+
19+install(FILES qmldir
20+ DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Powerd
21+ )
tests/mocks/Powerd/Powerd.cppnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "Powerd.h"
20+
21+Powerd::Powerd(QObject* parent)
22+ : QObject(parent)
23+{
24+}
tests/mocks/Powerd/Powerd.hnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef UNITY_MOCK_POWERD_H
20+#define UNITY_MOCK_POWERD_H
21+
22+#include <QtCore/QObject>
23+
24+class Powerd: public QObject
25+{
26+ Q_OBJECT
27+ Q_ENUMS(Status)
28+ Q_FLAGS(DisplayFlag DisplayFlags)
29+
30+public:
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+
43+ explicit Powerd(QObject *parent = 0);
44+
45+Q_SIGNALS:
46+ void displayPowerStateChange(int status, unsigned int flags);
47+};
48+
49+#endif
tests/mocks/Powerd/plugin.cppnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "plugin.h"
20+#include "Powerd.h"
21+
22+#include <QtQml/qqml.h>
23+
24+static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25+{
26+ Q_UNUSED(engine)
27+ Q_UNUSED(scriptEngine)
28+ return new Powerd();
29+}
30+
31+void PowerdPlugin::registerTypes(const char *uri)
32+{
33+ Q_ASSERT(uri == QLatin1String("Powerd"));
34+ qmlRegisterSingletonType<Powerd>(uri, 0, 1, "Powerd", powerd_provider);
35+}
tests/mocks/Powerd/plugin.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef MOCK_POWER_PLUGIN_H
20+#define MOCK_POWER_PLUGIN_H
21+
22+#include <QtQml/QQmlEngine>
23+#include <QtQml/QQmlExtensionPlugin>
24+
25+class PowerdPlugin : public QQmlExtensionPlugin
26+{
27+ Q_OBJECT
28+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29+
30+public:
31+ void registerTypes(const char *uri);
32+};
33+
34+#endif
tests/mocks/Powerd/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module Powerd
2+plugin MockPowerd-qml
tests/mocks/Ubuntu/DownloadDaemonListener/plugin.cpp−1
@@ -17,7 +17,6 @@
17#include "plugin.h"
17#include "plugin.h"
18#include "MockDownloadTracker.h"
18#include "MockDownloadTracker.h"
19
19
20−#include <QtQml>
21#include <QtQml/QQmlContext>
20#include <QtQml/QQmlContext>
22#include <qqml.h>
21#include <qqml.h>
23
22
tests/mocks/Unity/Indicators/IndicatorsModel.qml+1−1
@@ -64,7 +64,7 @@ ListModel {
64 isValid: "";
64 isValid: "";
65 }
65 }
66 ListElement {
66 ListElement {
67− identifier: "indicator-fake4";
67+ identifier: "indicator-fake5";
68 priority: 4
68 priority: 4
69 title: "Menu5";
69 title: "Menu5";
70 description: "";
70 description: "";
tests/mocks/Unity/Indicators/fakeplugin.cpp+1−1
@@ -17,7 +17,7 @@
17 */
17 */
18
18
19// Qt
19// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
21
21
22// self
22// self
23#include "fakeplugin.h"
23#include "fakeplugin.h"
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+2−2
@@ -31,8 +31,8 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
31 m_pinned(false),
31 m_pinned(false),
32 m_running(false),
32 m_running(false),
33 m_recent(false),
33 m_recent(false),
34− m_progress(8),
34+ m_progress(-1),
35− m_count(8),
35+ m_count(0),
36 m_quickList(new MockQuickListModel(this))
36 m_quickList(new MockQuickListModel(this))
37{
37{
38
38
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+48−18
@@ -22,34 +22,40 @@
22
22
23MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
23MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
24{
24{
25− MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app");
25+ MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
26+ item->setProgress(0);
26 m_list.append(item);
27 m_list.append(item);
27− item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera");
28+ item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29+ item->setProgress(10);
28 m_list.append(item);
30 m_list.append(item);
29− item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery");
31+ item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
32+ item->setProgress(50);
30 m_list.append(item);
33 m_list.append(item);
31− item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook");
34+ item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
35+ item->setProgress(150);
32 m_list.append(item);
36 m_list.append(item);
33− item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser");
37+ item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
38+ item->setCount(1);
34 m_list.append(item);
39 m_list.append(item);
35− item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter");
40+ item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
41+ item->setCount(12);
36 m_list.append(item);
42 m_list.append(item);
37− item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail");
43+ item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
44+ item->setCount(123);
38 m_list.append(item);
45 m_list.append(item);
39− item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather");
46+ item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
47+ item->setCount(1234567890);
40 m_list.append(item);
48 m_list.append(item);
41− item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad");
49+ item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50+ item->setProgress(50);
51+ item->setCount(5);
42 m_list.append(item);
52 m_list.append(item);
43− item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar");
53+ item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
44 m_list.append(item);
54 m_list.append(item);
45}
55}
46
56
47MockLauncherModel::~MockLauncherModel()
57MockLauncherModel::~MockLauncherModel()
48{
58{
49− while (!m_list.empty())
50− {
51− m_list.takeFirst()->deleteLater();
52− }
53}
59}
54
60
55int MockLauncherModel::rowCount(const QModelIndex& parent) const
61int MockLauncherModel::rowCount(const QModelIndex& parent) const
@@ -95,9 +101,29 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
95
101
96void MockLauncherModel::move(int oldIndex, int newIndex)
102void MockLauncherModel::move(int oldIndex, int newIndex)
97{
103{
98− 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);
99 m_list.move(oldIndex, newIndex);
123 m_list.move(oldIndex, newIndex);
100 endMoveRows();
124 endMoveRows();
125+
126+ pin(m_list.at(newIndex)->appId());
101}
127}
102
128
103void MockLauncherModel::pin(const QString &appId, int index)
129void MockLauncherModel::pin(const QString &appId, int index)
@@ -135,9 +161,7 @@ void MockLauncherModel::requestRemove(const QString &appId)
135
161
136void MockLauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
162void MockLauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
137{
163{
138− Q_UNUSED(appId)
164+ Q_EMIT quickListTriggered(appId, actionIndex);
139− Q_UNUSED(actionIndex)
140− // Nothing to mock yet...
141}
165}
142
166
143int MockLauncherModel::findApp(const QString &appId)
167int MockLauncherModel::findApp(const QString &appId)
@@ -150,3 +174,9 @@ int MockLauncherModel::findApp(const QString &appId)
150 }
174 }
151 return -1;
175 return -1;
152}
176}
177+
178+void MockLauncherModel::setUser(const QString &username)
179+{
180+ Q_UNUSED(username)
181+ // TODO: implement this...
182+}
tests/mocks/Unity/Launcher/MockLauncherModel.h+4
@@ -43,6 +43,10 @@ public:
43 Q_INVOKABLE void pin(const QString &appId, int index = -1);
43 Q_INVOKABLE void pin(const QString &appId, int index = -1);
44 Q_INVOKABLE void requestRemove(const QString &appId);
44 Q_INVOKABLE void requestRemove(const QString &appId);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
46+ Q_INVOKABLE void setUser(const QString &username);
47+
48+Q_SIGNALS:
49+ void quickListTriggered(const QString &appId, int index);
46
50
47private:
51private:
48 int findApp(const QString &appId);
52 int findApp(const QString &appId);
tests/mocks/Unity/Launcher/MockQuickListModel.cpp+2
@@ -35,6 +35,8 @@ QVariant MockQuickListModel::data(const QModelIndex &index, int role) const
35 return QLatin1String("test menu entry ") + QString::number(index.row());
35 return QLatin1String("test menu entry ") + QString::number(index.row());
36 case RoleIcon:
36 case RoleIcon:
37 return QLatin1String("copy.png");
37 return QLatin1String("copy.png");
38+ case RoleClickable:
39+ return index.row() == 1 ? false : true;
38 }
40 }
39 return QVariant();
41 return QVariant();
40}
42}
tests/mocks/Unity/Launcher/plugin.cpp+3
@@ -20,6 +20,7 @@
20#include "plugin.h"
20#include "plugin.h"
21#include "MockLauncherModel.h"
21#include "MockLauncherModel.h"
22#include "MockLauncherItem.h"
22#include "MockLauncherItem.h"
23+#include "MockQuickListModel.h"
23
24
24#include <unity/shell/launcher/LauncherModelInterface.h>
25#include <unity/shell/launcher/LauncherModelInterface.h>
25#include <unity/shell/launcher/LauncherItemInterface.h>
26#include <unity/shell/launcher/LauncherItemInterface.h>
@@ -39,7 +40,9 @@ void TestLauncherPlugin::registerTypes(const char* uri)
39 // @uri Unity.Launcher
40 // @uri Unity.Launcher
40 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
41 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
41 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
42 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
43+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
42
44
43 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
45 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
44 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
46 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
47+ qmlRegisterUncreatableType<MockQuickListModel>(uri, 0, 1, "QuickListModel", "Can't create QuickLists in QML. Get them from the LauncherItems");
45}
48}
tests/mocks/Unity/fake_unity_plugin.cpp+2−1
@@ -26,7 +26,8 @@
26
26
27// External
27// External
28#include <glib-object.h>
28#include <glib-object.h>
29−#include <QtQml>
29+
30+#include <QtQml/qqml.h>
30
31
31void FakeUnityPlugin::registerTypes(const char *uri)
32void FakeUnityPlugin::registerTypes(const char *uri)
32{
33{
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp+44−1
@@ -19,6 +19,7 @@
19#include <QAbstractItemModel>
19#include <QAbstractItemModel>
20#include <QQmlEngine>
20#include <QQmlEngine>
21#include <QQuickView>
21#include <QQuickView>
22+#include <QSignalSpy>
22#include <QtTestGui>
23#include <QtTestGui>
23#pragma GCC diagnostic push
24#pragma GCC diagnostic push
24#pragma GCC diagnostic ignored "-pedantic"
25#pragma GCC diagnostic ignored "-pedantic"
@@ -1521,7 +1522,7 @@ private Q_SLOTS:
1521 QCOMPARE(lvwph->m_firstVisibleIndex, 1);
1522 QCOMPARE(lvwph->m_firstVisibleIndex, 1);
1522 verifyItem(0, -458., 1000., false);
1523 verifyItem(0, -458., 1000., false);
1523 verifyItem(1, 542., 350., true);
1524 verifyItem(1, 542., 350., true);
1524− QCOMPARE(lvwph->m_minYExtent, 362.5);
1525+ QCOMPARE(lvwph->m_minYExtent, 525.);
1525 QCOMPARE(lvwph->m_clipItem->y(), 658.);
1526 QCOMPARE(lvwph->m_clipItem->y(), 658.);
1526 QCOMPARE(lvwph->m_clipItem->clip(), false);
1527 QCOMPARE(lvwph->m_clipItem->clip(), false);
1527 QCOMPARE(lvwph->m_headerItem->y(), 0.);
1528 QCOMPARE(lvwph->m_headerItem->y(), 0.);
@@ -1612,6 +1613,48 @@ private Q_SLOTS:
1612 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1613 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1613 }
1614 }
1614
1615
1616+ void testMaximizeVisibleAreaWithItemResize()
1617+ {
1618+ model->setProperty(0, "size", 1000);
1619+
1620+ bool res = lvwph->maximizeVisibleArea(1);
1621+ QVERIFY(res);
1622+ QTRY_VERIFY(!lvwph->m_contentYAnimation->isRunning());
1623+
1624+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 3);
1625+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1626+ verifyItem(0, -658., 1000., false);
1627+ verifyItem(1, 342., 200., false);
1628+ verifyItem(2, 542, 350., true);
1629+ QCOMPARE(lvwph->m_minYExtent, 0.);
1630+ QCOMPARE(lvwph->m_clipItem->y(), 708.);
1631+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1632+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1633+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1634+ QCOMPARE(lvwph->contentY(), 708.);
1635+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1636+ }
1637+
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+
1615private:
1658private:
1616 QQuickView *view;
1659 QQuickView *view;
1617 ListViewWithPageHeader *lvwph;
1660 ListViewWithPageHeader *lvwph;
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsection.cpp+44−1
@@ -19,6 +19,7 @@
19#include <QAbstractItemModel>
19#include <QAbstractItemModel>
20#include <QQmlEngine>
20#include <QQmlEngine>
21#include <QQuickView>
21#include <QQuickView>
22+#include <QSignalSpy>
22#include <QtTestGui>
23#include <QtTestGui>
23#pragma GCC diagnostic push
24#pragma GCC diagnostic push
24#pragma GCC diagnostic ignored "-pedantic"
25#pragma GCC diagnostic ignored "-pedantic"
@@ -1875,7 +1876,7 @@ private Q_SLOTS:
1875 QCOMPARE(lvwph->m_firstVisibleIndex, 1);
1876 QCOMPARE(lvwph->m_firstVisibleIndex, 1);
1876 verifyItem(0, -498., 1040., false, "Regular", true);
1877 verifyItem(0, -498., 1040., false, "Regular", true);
1877 verifyItem(1, 542., 390., true, "Mild", true);
1878 verifyItem(1, 542., 390., true, "Mild", true);
1878− QCOMPARE(lvwph->m_minYExtent, 1250/3.);
1879+ QCOMPARE(lvwph->m_minYExtent, 525.);
1879 QCOMPARE(lvwph->m_clipItem->y(), 738.);
1880 QCOMPARE(lvwph->m_clipItem->y(), 738.);
1880 QCOMPARE(lvwph->m_clipItem->clip(), false);
1881 QCOMPARE(lvwph->m_clipItem->clip(), false);
1881 QCOMPARE(lvwph->m_headerItem->y(), 0.);
1882 QCOMPARE(lvwph->m_headerItem->y(), 0.);
@@ -2033,6 +2034,48 @@ private Q_SLOTS:
2033 QCOMPARE(lvwph->m_topSectionItem->y(), 0.);
2034 QCOMPARE(lvwph->m_topSectionItem->y(), 0.);
2034 }
2035 }
2035
2036
2037+ void testMaximizeVisibleAreaWithItemResize()
2038+ {
2039+ model->setProperty(0, "size", 1000);
2040+
2041+ bool res = lvwph->maximizeVisibleArea(1);
2042+ QVERIFY(res);
2043+ QTRY_VERIFY(!lvwph->m_contentYAnimation->isRunning());
2044+
2045+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 3);
2046+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
2047+ verifyItem(0, -738., 1040., false, "Agressive", true);
2048+ verifyItem(1, 302., 240., false, "Regular", false);
2049+ verifyItem(2, 542, 390., true, "Mild", true);
2050+ QCOMPARE(lvwph->m_minYExtent, 0.);
2051+ QCOMPARE(lvwph->m_clipItem->y(), 788.);
2052+ QCOMPARE(lvwph->m_clipItem->clip(), false);
2053+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
2054+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
2055+ QCOMPARE(lvwph->contentY(), 788.);
2056+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
2057+ }
2058+
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+
2036private:
2079private:
2037 QQuickView *view;
2080 QQuickView *view;
2038 ListViewWithPageHeader *lvwph;
2081 ListViewWithPageHeader *lvwph;
tests/plugins/Unity/CMakeLists.txt+9−1
@@ -2,12 +2,16 @@ add_subdirectory(Indicators)
2
2
3pkg_check_modules(UNITYCORE REQUIRED unity-core-6.0)
3pkg_check_modules(UNITYCORE REQUIRED unity-core-6.0)
4pkg_check_modules(LIBUNITYPROTO REQUIRED unity-protocol-private)
4pkg_check_modules(LIBUNITYPROTO REQUIRED unity-protocol-private)
5+pkg_check_modules(LIBUNITY REQUIRED unity)
6+pkg_check_modules(LIBSIGCPP REQUIRED sigc++-2.0)
5
7
6include_directories(
8include_directories(
7 ${Unity-qml_SOURCE_DIR}
9 ${Unity-qml_SOURCE_DIR}
8 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Unity
10 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Unity
9 ${CMAKE_CURRENT_BINARY_DIR}
11 ${CMAKE_CURRENT_BINARY_DIR}
10 ${LIBUNITYPROTO_INCLUDE_DIRS}
12 ${LIBUNITYPROTO_INCLUDE_DIRS}
13+ ${LIBUNITY_INCLUDE_DIRS}
14+ ${LIBSIGCPP_INCLUDE_DIRS}
11 ${UNITYCORE_INCLUDE_DIRS}
15 ${UNITYCORE_INCLUDE_DIRS}
12 )
16 )
13
17
@@ -20,7 +24,7 @@ macro(run_tests)
20 -o -,txt)
24 -o -,txt)
21 add_test(NAME test${CLASSNAME}${_test} COMMAND ${testCommand})
25 add_test(NAME test${CLASSNAME}${_test} COMMAND ${testCommand})
22 add_custom_target(${_test} ${testCommand})
26 add_custom_target(${_test} ${testCommand})
23− add_executable(${_test}Exec ${_test}.cpp previewbindingstest.cpp)
27+ add_executable(${_test}Exec ${_test}.cpp)
24 qt5_use_modules(${_test}Exec Test Core Qml)
28 qt5_use_modules(${_test}Exec Test Core Qml)
25 set_tests_properties(test${CLASSNAME}${_test}
29 set_tests_properties(test${CLASSNAME}${_test}
26 PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Unity:${LIBUNITYPROTO_LIBRARY_DIRS}")
30 PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Unity:${LIBUNITYPROTO_LIBRARY_DIRS}")
@@ -28,7 +32,9 @@ macro(run_tests)
28 target_link_libraries(${_test}Exec
32 target_link_libraries(${_test}Exec
29 Unity-qml
33 Unity-qml
30 ${UNITYCORE_LDFLAGS}
34 ${UNITYCORE_LDFLAGS}
35+ ${LIBUNITY_LDFLAGS}
31 ${LIBUNITYPROTO_LDFLAGS}
36 ${LIBUNITYPROTO_LDFLAGS}
37+ ${LIBSIGCPP_LDFLAGS}
32 )
38 )
33
39
34 set(_test_list "${_test_list};${_test}")
40 set(_test_list "${_test_list};${_test}")
@@ -36,5 +42,7 @@ macro(run_tests)
36endmacro(run_tests)
42endmacro(run_tests)
37
43
38run_tests(
44run_tests(
45+ signalslisttest
39 previewbindingstest
46 previewbindingstest
47+ filtersbindingstest
40 )
48 )
tests/plugins/Unity/Indicators/indicatorsmanagertest.cpp−1
@@ -77,7 +77,6 @@ private Q_SLOTS:
77 // Check that the initial properties have been set.
77 // Check that the initial properties have been set.
78 QVariantMap props = indicator->indicatorProperties().toMap();
78 QVariantMap props = indicator->indicatorProperties().toMap();
79 QCOMPARE(props.count(), 4);
79 QCOMPARE(props.count(), 4);
80− QCOMPARE(props["busType"].toInt(), 1);
81 QCOMPARE(props["busName"].toString(), QString("com.canonical.indicator.fake1"));
80 QCOMPARE(props["busName"].toString(), QString("com.canonical.indicator.fake1"));
82 QCOMPARE(props["actionsObjectPath"].toString(), QString("/com/canonical/indicator/fake1"));
81 QCOMPARE(props["actionsObjectPath"].toString(), QString("/com/canonical/indicator/fake1"));
83
82
tests/plugins/Unity/filtersbindingstest.cppnew file+484
@@ -0,0 +1,484 @@
1+/*
2+* Copyright (C) 2013 Canonical, Ltd.
3+*
4+* Authors:
5+* Pawel Stolowski <pawel.stolowski@canonical.com>
6+*
7+* This program is free software; you can redistribute it and/or modify
8+* it under the terms of the GNU General Public License as published by
9+* the Free Software Foundation; version 3.
10+*
11+* This program is distributed in the hope that it will be useful,
12+* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+* GNU General Public License for more details.
15+*
16+* You should have received a copy of the GNU General Public License
17+* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+*/
19+
20+#include "filtersbindingstest.h"
21+
22+// libunity-core
23+#include <UnityCore/Filter.h>
24+#include <UnityCore/MultiRangeFilter.h>
25+
26+// Qt
27+#include <QTest>
28+#include <QSignalSpy>
29+
30+// libunity
31+#include <unity.h>
32+
33+// local
34+#include "filter.h"
35+#include "multirangefilter.h"
36+#include "checkoptionfilter.h"
37+#include "radiooptionfilter.h"
38+#include "ratingsfilter.h"
39+#include "ratingfilteroption.h"
40+#include "genericoptionsmodel.h"
41+
42+void FiltersBindingsTest::initTestCase()
43+{
44+}
45+
46+GVariant* FiltersBindingsTest::createOptions(int numOfOptions)
47+{
48+ auto vtype = g_variant_type_new("a(sssb)");
49+ auto vb = g_variant_builder_new(vtype);
50+ for (int i = 0; i<numOfOptions; i++) {
51+ const std::string optId = "opt" + std::to_string(i);
52+ const std::string optName = "Option " + std::to_string(i);
53+ g_variant_builder_add(vb, "(sssb)", optId.c_str(), optName.c_str(), "", false);
54+ }
55+ return g_variant_builder_end(vb);
56+}
57+
58+DeeModel* FiltersBindingsTest::createFilterModel()
59+{
60+ const char* filter_schema[8] = {"s", "s", "s", "s", "a{sv}", "b", "b", "b"};
61+ auto model = dee_sequence_model_new();
62+ dee_model_set_schema_full (DEE_MODEL(model), filter_schema, 8);
63+ return model;
64+}
65+
66+void FiltersBindingsTest::createMultiRangeFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
67+{
68+ createFilter(model, "filter-multirange", id, name, optionCount);
69+}
70+
71+void FiltersBindingsTest::createRadioOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
72+{
73+ createFilter(model, "filter-radiooption", id, name, optionCount);
74+}
75+
76+void FiltersBindingsTest::createCheckOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
77+{
78+ createFilter(model, "filter-checkoption", id, name, optionCount);
79+}
80+
81+void FiltersBindingsTest::createRatingsFilter(DeeModel *model, const std::string &id, const std::string &name)
82+{
83+ GVariant* row[8];
84+
85+ row[0] = g_variant_new_string(id.c_str());
86+ row[1] = g_variant_new_string(name.c_str());
87+ row[2] = g_variant_new_string(""); // icon hint
88+ row[3] = g_variant_new_string("filter-ratings");
89+
90+ GVariant *children[2];
91+ GVariant *key1 = g_variant_new_string("show-all-button");
92+ GVariant *key2 = g_variant_new_string("rating");
93+ children[0] = g_variant_new_dict_entry(key1, g_variant_new_variant(g_variant_new_boolean(false)));
94+ children[1] = g_variant_new_dict_entry(key2, g_variant_new_variant(g_variant_new_double(0.0f)));
95+ auto fhints = g_variant_new_array(G_VARIANT_TYPE("{sv}"), children, 2);
96+
97+ row[4] = fhints;
98+ row[5] = g_variant_new_boolean(true); // visible
99+ row[6] = g_variant_new_boolean(false); // collapsed
100+ row[7] = g_variant_new_boolean(false); // filtering
101+
102+ dee_model_append_row(model, row);
103+}
104+
105+void FiltersBindingsTest::createFilter(DeeModel *model, const std::string &renderer, const std::string &id, const std::string &name, int optionCount)
106+{
107+ GVariant* row[8];
108+
109+ row[0] = g_variant_new_string(id.c_str());
110+ row[1] = g_variant_new_string(name.c_str());
111+ row[2] = g_variant_new_string(""); // icon hint
112+ row[3] = g_variant_new_string(renderer.c_str()); // renderer
113+
114+ GVariant *children[2];
115+ GVariant *key1 = g_variant_new_string("show-all-button");
116+ GVariant *key2 = g_variant_new_string("options");
117+ children[0] = g_variant_new_dict_entry(key1, g_variant_new_variant(g_variant_new_boolean(false)));
118+ children[1] = g_variant_new_dict_entry(key2, g_variant_new_variant(createOptions(optionCount)));
119+ auto fhints = g_variant_new_array(G_VARIANT_TYPE("{sv}"), children, 2);
120+
121+ row[4] = fhints;
122+ row[5] = g_variant_new_boolean(true); // visible
123+ row[6] = g_variant_new_boolean(false); // collapsed
124+ row[7] = g_variant_new_boolean(false); // filtering
125+
126+ dee_model_append_row(model, row);
127+}
128+
129+void FiltersBindingsTest::testMultiRangeFilter()
130+{
131+ auto model = createFilterModel();
132+
133+ createMultiRangeFilter(model, "f1", "Filter1", 1);
134+ createMultiRangeFilter(model, "f2", "Filter2", 2);
135+ createMultiRangeFilter(model, "f3", "Filter3", 3);
136+
137+ // create filter out of 1st row
138+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
139+ {
140+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
141+ QVERIFY(core_filter != nullptr);
142+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
143+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
144+ QVERIFY(multi_filter != nullptr);
145+ auto options = multi_filter->options();
146+ QCOMPARE(options->rowCount(), 1);
147+
148+ auto idx = options->index(0);
149+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
150+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
151+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
152+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
153+
154+ QCOMPARE(id_var.toString(), QString("opt0"));
155+ QCOMPARE(name_var.toString(), QString("Option 0"));
156+ QCOMPARE(icon_var.toString(), QString(""));
157+ QCOMPARE(active_var.toBool(), false);
158+
159+ delete multi_filter;
160+ }
161+
162+ // create filter out of 2nd row
163+ iter = dee_model_next(DEE_MODEL(model), iter);
164+ {
165+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
166+ QVERIFY(core_filter != nullptr);
167+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
168+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
169+ QVERIFY(multi_filter != nullptr);
170+ auto options = multi_filter->options();
171+ QCOMPARE(options->rowCount(), 2);
172+
173+ auto idx = options->index(0);
174+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
175+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
176+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
177+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
178+
179+ QCOMPARE(id_var.toString(), QString("opt0"));
180+ QCOMPARE(name_var.toString(), QString("Option 0"));
181+ QCOMPARE(icon_var.toString(), QString(""));
182+ QCOMPARE(active_var.toBool(), false);
183+
184+ AbstractFilterOption* opt1 = options->getRawOption(0);
185+ QCOMPARE(opt1->id(), QString("opt0"));
186+
187+ AbstractFilterOption* opt2 = options->getRawOption(1);
188+ QCOMPARE(opt2->id(), QString("opt1"));
189+
190+ delete multi_filter;
191+ }
192+
193+ // create filter out of 3nd row
194+ iter = dee_model_next(DEE_MODEL(model), iter);
195+ {
196+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
197+ QVERIFY(core_filter != nullptr);
198+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
199+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
200+ QVERIFY(multi_filter != nullptr);
201+ auto options = multi_filter->options();
202+ QCOMPARE(options->rowCount(), 3);
203+
204+ AbstractFilterOption* opt0 = options->getRawOption(0);
205+ QCOMPARE(opt0->id(), QString("opt0"));
206+
207+ AbstractFilterOption* opt1 = options->getRawOption(1);
208+ QCOMPARE(opt1->id(), QString("opt1"));
209+
210+ AbstractFilterOption* opt2 = options->getRawOption(2);
211+ QCOMPARE(opt2->id(), QString("opt2"));
212+
213+ // verify combined filter options are bound to correct underlying unity options
214+ auto core_multi_filter = std::dynamic_pointer_cast<unity::dash::MultiRangeFilter>(core_filter);
215+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[0]->id()), QString("opt0"));
216+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[1]->id()), QString("opt1"));
217+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[2]->id()), QString("opt2"));
218+
219+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
220+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
221+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
222+
223+ // test active property changes
224+ QCOMPARE(opt0->active(), false);
225+
226+ opt0->setActive(true); // activate 1st option
227+ QCOMPARE(opt0spy.count(), 1);
228+ QCOMPARE(opt1spy.count(), 0);
229+ QCOMPARE(opt2spy.count(), 0);
230+ QCOMPARE(opt0->active(), true);
231+ QCOMPARE(opt1->active(), false);
232+ QCOMPARE(opt2->active(), false);
233+
234+ opt1->setActive(true);
235+ QCOMPARE(opt0spy.count(), 2);
236+ QCOMPARE(opt1spy.count(), 1);
237+ QCOMPARE(opt2spy.count(), 0);
238+ QCOMPARE(opt0->active(), false); //1st combined option gets deactivated
239+ QCOMPARE(opt1->active(), true);
240+ QCOMPARE(opt2->active(), false);
241+
242+ opt1->setActive(false); // and de-activate it
243+ QCOMPARE(opt0spy.count(), 2);
244+ QCOMPARE(opt1spy.count(), 2);
245+ QCOMPARE(opt2spy.count(), 0);
246+ QCOMPARE(opt0->active(), false);
247+ QCOMPARE(opt1->active(), false);
248+ QCOMPARE(opt2->active(), false);
249+
250+ opt2->setActive(true); // activate another combined option
251+ QCOMPARE(opt0spy.count(), 2);
252+ QCOMPARE(opt1spy.count(), 2);
253+ QCOMPARE(opt2spy.count(), 1);
254+ QCOMPARE(opt0->active(), false);
255+ QCOMPARE(opt1->active(), false);
256+ QCOMPARE(opt2->active(), true);
257+
258+ delete multi_filter;
259+ }
260+}
261+
262+void FiltersBindingsTest::testCheckOptionFilter()
263+{
264+ auto model = createFilterModel();
265+
266+ createCheckOptionFilter(model, "f1", "Filter1", 3);
267+
268+ // create filter out of 1st row
269+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
270+ {
271+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
272+ QVERIFY(core_filter != nullptr);
273+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
274+ auto check_filter = dynamic_cast<CheckOptionFilter*>(bind_filter);
275+ QVERIFY(check_filter != nullptr);
276+ auto options = check_filter->options();
277+ QCOMPARE(options->rowCount(), 3);
278+
279+ QCOMPARE(check_filter->id(), QString("f1"));
280+ QCOMPARE(check_filter->name(), QString("Filter1"));
281+ QCOMPARE(check_filter->iconHint(), QString(""));
282+ QCOMPARE(check_filter->rendererName(), QString("filter-checkoption"));
283+ QCOMPARE(check_filter->visible(), true);
284+ QCOMPARE(check_filter->collapsed(), false);
285+ QCOMPARE(check_filter->filtering(), false);
286+
287+ auto idx = options->index(0);
288+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
289+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
290+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
291+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
292+
293+ QCOMPARE(id_var.toString(), QString("opt0"));
294+ QCOMPARE(name_var.toString(), QString("Option 0"));
295+ QCOMPARE(icon_var.toString(), QString(""));
296+ QCOMPARE(active_var.toBool(), false);
297+
298+ idx = options->index(1);
299+ id_var = options->data(idx, GenericOptionsModel::RoleId);
300+ name_var = options->data(idx, GenericOptionsModel::RoleName);
301+ icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
302+ active_var = options->data(idx, GenericOptionsModel::RoleActive);
303+
304+ QCOMPARE(id_var.toString(), QString("opt1"));
305+ QCOMPARE(name_var.toString(), QString("Option 1"));
306+ QCOMPARE(icon_var.toString(), QString(""));
307+ QCOMPARE(active_var.toBool(), false);
308+
309+ AbstractFilterOption* opt0 = options->getRawOption(0);
310+ AbstractFilterOption* opt1 = options->getRawOption(1);
311+ AbstractFilterOption* opt2 = options->getRawOption(2);
312+
313+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
314+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
315+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
316+ QSignalSpy dataChangedSpy(options, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
317+
318+ // test active property changes
319+ QCOMPARE(opt0->active(), false);
320+ QCOMPARE(opt1->active(), false);
321+ QCOMPARE(opt2->active(), false);
322+
323+ opt0->setActive(true);
324+ QCOMPARE(opt0spy.count(), 1);
325+ QCOMPARE(opt1spy.count(), 0);
326+ QCOMPARE(opt2spy.count(), 0);
327+ QCOMPARE(opt0->active(), true);
328+ QCOMPARE(opt1->active(), false);
329+ QCOMPARE(opt2->active(), false);
330+
331+ opt1->setActive(true);
332+ QCOMPARE(opt0spy.count(), 2);
333+ QCOMPARE(opt1spy.count(), 1);
334+ QCOMPARE(opt2spy.count(), 0);
335+ QCOMPARE(opt0->active(), false);
336+ QCOMPARE(opt1->active(), true);
337+ QCOMPARE(opt2->active(), false);
338+
339+ QCOMPARE(dataChangedSpy.count(), 3);
340+
341+ delete check_filter;
342+ }
343+}
344+
345+void FiltersBindingsTest::testRadioOptionFilter()
346+{
347+ auto model = createFilterModel();
348+
349+ createRadioOptionFilter(model, "f1", "Filter1", 3);
350+
351+ // create filter out of 1st row
352+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
353+ {
354+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
355+ QVERIFY(core_filter != nullptr);
356+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
357+ auto radio_filter = dynamic_cast<RadioOptionFilter*>(bind_filter);
358+ QVERIFY(radio_filter != nullptr);
359+ auto options = radio_filter->options();
360+ QCOMPARE(options->rowCount(), 3);
361+
362+ auto idx = options->index(0);
363+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
364+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
365+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
366+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
367+
368+ QCOMPARE(id_var.toString(), QString("opt0"));
369+ QCOMPARE(name_var.toString(), QString("Option 0"));
370+ QCOMPARE(icon_var.toString(), QString(""));
371+ QCOMPARE(active_var.toBool(), false);
372+
373+ idx = options->index(1);
374+ id_var = options->data(idx, GenericOptionsModel::RoleId);
375+ name_var = options->data(idx, GenericOptionsModel::RoleName);
376+ icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
377+ active_var = options->data(idx, GenericOptionsModel::RoleActive);
378+
379+ QCOMPARE(id_var.toString(), QString("opt1"));
380+ QCOMPARE(name_var.toString(), QString("Option 1"));
381+ QCOMPARE(icon_var.toString(), QString(""));
382+ QCOMPARE(active_var.toBool(), false);
383+
384+ AbstractFilterOption* opt0 = options->getRawOption(0);
385+ AbstractFilterOption* opt1 = options->getRawOption(1);
386+ AbstractFilterOption* opt2 = options->getRawOption(2);
387+
388+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
389+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
390+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
391+ QSignalSpy dataChangedSpy(options, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
392+
393+ // test active property changes
394+ QCOMPARE(opt0->active(), false);
395+ QCOMPARE(opt1->active(), false);
396+ QCOMPARE(opt2->active(), false);
397+
398+ opt0->setActive(true);
399+ QCOMPARE(opt0spy.count(), 1);
400+ QCOMPARE(opt1spy.count(), 0);
401+ QCOMPARE(opt2spy.count(), 0);
402+ QCOMPARE(opt0->active(), true);
403+ QCOMPARE(opt1->active(), false);
404+ QCOMPARE(opt2->active(), false);
405+
406+ opt1->setActive(true);
407+ QCOMPARE(opt0spy.count(), 2);
408+ QCOMPARE(opt1spy.count(), 1);
409+ QCOMPARE(opt2spy.count(), 0);
410+ QCOMPARE(opt0->active(), false);
411+ QCOMPARE(opt1->active(), true);
412+ QCOMPARE(opt2->active(), false);
413+
414+ QCOMPARE(dataChangedSpy.count(), 3);
415+
416+ delete radio_filter;
417+ }
418+}
419+
420+void FiltersBindingsTest::testRatingsFilter()
421+{
422+ auto model = createFilterModel();
423+
424+ createRatingsFilter(model, "f1", "Filter1");
425+
426+ // create filter out of 1st row
427+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
428+ {
429+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
430+ QVERIFY(core_filter != nullptr);
431+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
432+ auto rating_filter = dynamic_cast<RatingsFilter*>(bind_filter);
433+ QVERIFY(rating_filter != nullptr);
434+ auto options = rating_filter->options();
435+ QCOMPARE(options->rowCount(), 5);
436+
437+ RatingFilterOption* opt[5];
438+ for (int i = 0; i<5; i++) {
439+ opt[i] = dynamic_cast<RatingFilterOption *>(options->getRawOption(i));
440+ }
441+
442+ QSignalSpy opt0spy(opt[0], SIGNAL(activeChanged(bool)));
443+ QSignalSpy opt4spy(opt[4], SIGNAL(activeChanged(bool)));
444+ QSignalSpy dataChangedSpy(options, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
445+
446+ QVERIFY(qAbs(opt[0]->value() - 0.2f) <= 0.0001f);
447+ QVERIFY(qAbs(opt[1]->value() - 0.4f) <= 0.0001f);
448+ QVERIFY(qAbs(opt[2]->value() - 0.6f) <= 0.0001f);
449+ QVERIFY(qAbs(opt[3]->value() - 0.8f) <= 0.0001f);
450+ QVERIFY(qAbs(opt[4]->value() - 1.0f) <= 0.0001f);
451+
452+ QVERIFY(rating_filter->rating() <= 0.0001f); // rating equals zero
453+
454+ // all options initially inactive
455+ for (int i=0; i<5; i++) {
456+ QCOMPARE(opt[i]->active(), false);
457+ }
458+
459+ // verify activation for all options
460+ for (int i=0; i<5; i++) {
461+ opt[i]->setActive(true);
462+ QCOMPARE(opt[i]->active(), true);
463+ QVERIFY(rating_filter->rating() - ((i+1)*0.2f) <= 0.0001f);
464+
465+ // verify that all other options are inactive
466+ for (int j=0; j<5; j++) {
467+ if (i != j) {
468+ QCOMPARE(opt[j]->active(), false);
469+ }
470+ }
471+ }
472+
473+ QCOMPARE(opt0spy.count(), 2);
474+ QCOMPARE(opt4spy.count(), 1);
475+
476+ opt[4]->setActive(false);
477+ QVERIFY(rating_filter->rating() <= 0.0001f); // rating equals zero
478+ QCOMPARE(opt4spy.count(), 2);
479+
480+ QCOMPARE(dataChangedSpy.count(), 10);
481+ }
482+}
483+
484+QTEST_MAIN(FiltersBindingsTest)
tests/plugins/Unity/filtersbindingstest.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef FILTERSBINDINGSTEST_H
21+#define FILTERSBINDINGSTEST_H
22+
23+#include <QObject>
24+#include <dee.h>
25+
26+class FiltersBindingsTest : public QObject
27+{
28+ Q_OBJECT
29+
30+ private Q_SLOTS:
31+ void initTestCase();
32+ void testMultiRangeFilter();
33+ void testCheckOptionFilter();
34+ void testRadioOptionFilter();
35+ void testRatingsFilter();
36+
37+ private:
38+ DeeModel* createFilterModel();
39+ GVariant* createOptions(int numOfOptions);
40+ void createMultiRangeFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount);
41+ void createCheckOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount);
42+ void createRadioOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount);
43+ void createRatingsFilter(DeeModel *model, const std::string &id, const std::string &name);
44+ void createFilter(DeeModel *model, const std::string &renderer, const std::string &id, const std::string &name, int optionCount);
45+};
46+
47+#endif // FILTERSBINDINGSTEST_H
tests/plugins/Unity/signalslisttest.cppnew file+57
@@ -0,0 +1,57 @@
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+// local
18+#include "signalslisttest.h"
19+#include "signalslist.h"
20+
21+// Qt
22+#include <QTest>
23+
24+class TestSource
25+{
26+ public:
27+ TestSource(): m_count(0) {}
28+ void update()
29+ {
30+ countChanged(++m_count);
31+ }
32+
33+ sigc::signal<void, int> countChanged;
34+ int m_count;
35+};
36+
37+void SignalsListTest::testSignalsList()
38+{
39+ TestSource src;
40+ SignalsList signals;
41+ int counter = -1;
42+
43+ signals << src.countChanged.connect([&counter](int val) {
44+ counter = val;
45+ });
46+
47+ src.update();
48+ QCOMPARE(counter, 1);
49+ src.update();
50+ QCOMPARE(counter, 2);
51+
52+ signals.disconnectAll();
53+ src.update();
54+ QCOMPARE(counter, 2);
55+}
56+
57+QTEST_MAIN(SignalsListTest)
tests/plugins/Unity/signalslisttest.hnew file+30
@@ -0,0 +1,30 @@
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 SIGNALSLISTTEST_H
18+#define SIGNALSLISTTEST_H
19+
20+#include <QObject>
21+
22+class SignalsListTest : public QObject
23+{
24+ Q_OBJECT
25+
26+ private Q_SLOTS:
27+ void testSignalsList();
28+};
29+
30+#endif // SIGNALSLISTTEST_H
tests/plugins/Utils/qlimitproxymodeltest.cpp+133−8
@@ -58,10 +58,17 @@ public:
58 m_roles = roles;
58 m_roles = roles;
59 }
59 }
60
60
61+ void clear()
62+ {
63+ beginRemoveRows(QModelIndex(), 0, m_list.count() - 1);
64+ m_list.clear();
65+ endRemoveRows();
66+ }
67+
61 bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
68 bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
62 beginInsertRows(parent, row, row+count-1);
69 beginInsertRows(parent, row, row+count-1);
63 for (int i=0; i<count; i++) {
70 for (int i=0; i<count; i++) {
64− m_list.insert(i+row, "test"+i);
71+ m_list.insert(i+row, QString::number(i));
65 }
72 }
66 endInsertRows();
73 endInsertRows();
67 return true;
74 return true;
@@ -88,6 +95,60 @@ private:
88 QHash<int, QByteArray> m_roles;
95 QHash<int, QByteArray> m_roles;
89};
96};
90
97
98+class MirrorModel : public QObject
99+{
100+ Q_OBJECT
101+
102+public:
103+ MirrorModel(QAbstractItemModel *model) : m_mirror(model)
104+ {
105+ connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)));
106+ connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)));
107+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
108+ }
109+
110+ void check()
111+ {
112+ QCOMPARE(m_list.count(), m_mirror->rowCount());
113+ for (int i = 0; i < m_list.count(); ++i) {
114+ QCOMPARE(m_list[i], m_mirror->data(m_mirror->index(i, 0)).toString());
115+ }
116+ }
117+
118+private Q_SLOTS:
119+ void rowsInserted(const QModelIndex &parent, int start, int end)
120+ {
121+ QVERIFY(!parent.isValid());
122+ for (int i = start; i <= end; ++i) {
123+ m_list.insert(i, m_mirror->data(m_mirror->index(i, 0)).toString());
124+ }
125+ }
126+
127+ void rowsRemoved(const QModelIndex &parent, int start, int end)
128+ {
129+ QVERIFY(!parent.isValid());
130+ for (int i = end; i >= start; --i) {
131+ m_list.removeAt(i);
132+ }
133+ }
134+
135+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
136+ {
137+ QVERIFY(topLeft.isValid());
138+ QVERIFY(bottomRight.isValid());
139+ QVERIFY(!topLeft.parent().isValid());
140+ QVERIFY(!bottomRight.parent().isValid());
141+ for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
142+ m_list[i] = m_mirror->data(m_mirror->index(i, 0)).toString();
143+ }
144+ }
145+
146+
147+private:
148+ QStringList m_list;
149+ QAbstractItemModel *m_mirror;
150+};
151+
91class QLimitProxyModelTest : public QObject
152class QLimitProxyModelTest : public QObject
92{
153{
93 Q_OBJECT
154 Q_OBJECT
@@ -401,15 +462,10 @@ private Q_SLOTS:
401 QCOMPARE(spyOnCountChanged.count(), 0);
462 QCOMPARE(spyOnCountChanged.count(), 0);
402
463
403 model.removeRows(2, 2);
464 model.removeRows(2, 2);
404− QCOMPARE(spyOnRowsRemoved.count(), 1);
465+ QCOMPARE(spyOnRowsRemoved.count(), 0);
405 QCOMPARE(spyOnRowsInserted.count(), 0);
466 QCOMPARE(spyOnRowsInserted.count(), 0);
406− QCOMPARE(spyOnCountChanged.count(), 1);
467+ QCOMPARE(spyOnCountChanged.count(), 0);
407− arguments = spyOnRowsRemoved.takeFirst();
408− QCOMPARE(arguments.at(1).toInt(), 2);
409− QCOMPARE(arguments.at(2).toInt(), 3);
410 QCOMPARE(proxy.rowCount(), 7);
468 QCOMPARE(proxy.rowCount(), 7);
411− spyOnRowsInserted.clear();
412− spyOnCountChanged.clear();
413
469
414 model.removeRows(0, 7);
470 model.removeRows(0, 7);
415 QCOMPARE(spyOnRowsRemoved.count(), 1);
471 QCOMPARE(spyOnRowsRemoved.count(), 1);
@@ -479,6 +535,75 @@ private Q_SLOTS:
479 QCOMPARE(proxy.rowCount(), 5);
535 QCOMPARE(proxy.rowCount(), 5);
480 QCOMPARE(spyOnCountChanged.count(), 1);
536 QCOMPARE(spyOnCountChanged.count(), 1);
481 }
537 }
538+
539+ void testMirrorModel() {
540+ QLimitProxyModelQML proxy;
541+ MockListModel model;
542+ MirrorModel mirror(&proxy);
543+
544+ proxy.setModel(&model);
545+ proxy.setLimit(5);
546+
547+ // Checks what happens when the model already has
548+ // more items than the limit and we add stuff to
549+ // the front
550+ model.insertRows(0, 7);
551+ mirror.check();
552+ model.insertRows(1, 3);
553+ mirror.check();
554+ model.clear();
555+ mirror.check();
556+
557+ // Checks what happens when the model does
558+ // not has more items than limit but adding
559+ // stuff to its front makes it go past the limit
560+ model.insertRows(0, 3);
561+ mirror.check();
562+ model.insertRows(1, 3);
563+ mirror.check();
564+ model.clear();
565+ mirror.check();
566+
567+ // Checks what happens when the model does
568+ // not has more items than limit and adding
569+ // stuff to its front makes it not go past the limit
570+ model.insertRows(0, 1);
571+ mirror.check();
572+ model.insertRows(1, 3);
573+ mirror.check();
574+ model.clear();
575+ mirror.check();
576+
577+ // Checks what happens when the model already has
578+ // more items than the limit and we remove stuff from
579+ // the front but it still has more than the limit
580+ model.insertRows(0, 10);
581+ mirror.check();
582+ model.removeRows(1, 3);
583+ mirror.check();
584+ model.clear();
585+ mirror.check();
586+
587+ // Checks what happens when the model already has
588+ // more items than the limit and we remove stuff from
589+ // the front but it has less than the limit
590+ model.insertRows(0, 6);
591+ mirror.check();
592+ model.removeRows(1, 3);
593+ mirror.check();
594+ model.clear();
595+ mirror.check();
596+
597+ // Checks what happens when the model has
598+ // less items than the limit and we remove stuff from
599+ // the front
600+ model.insertRows(0, 4);
601+ mirror.check();
602+ model.removeRows(1, 3);
603+ mirror.check();
604+ model.clear();
605+ mirror.check();
606+ }
482};
607};
483
608
484QTEST_GUILESS_MAIN(QLimitProxyModelTest)
609QTEST_GUILESS_MAIN(QLimitProxyModelTest)
tests/qmltests/CMakeLists.txt+1−1
@@ -26,7 +26,7 @@ set(qmltest_DEFAULT_TARGETS qmluitests)
26set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
26set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
27set(qmltest_DEFAULT_PROPERTIES "")
27set(qmltest_DEFAULT_PROPERTIES "")
28
28
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
30 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
30 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
31add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
31add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
32add_qml_test(Components DraggingArea)
32add_qml_test(Components DraggingArea)
tests/qmltests/Components/tst_FilterGrid.qml−35
@@ -42,15 +42,6 @@ Rectangle {
42 values: [1,2,3,4]
42 values: [1,2,3,4]
43 selectedIndex: 1
43 selectedIndex: 1
44 }
44 }
45− Row {
46− spacing: units.gu(1)
47− Label { anchors.verticalCenter: parent.verticalCenter
48− text: "Expandable" }
49− CheckBox {
50− id: expandableCheckBox
51− checked: true
52− }
53− }
54 Row {
45 Row {
55 spacing: units.gu(1)
46 spacing: units.gu(1)
56 Label { anchors.verticalCenter: parent.verticalCenter
47 Label { anchors.verticalCenter: parent.verticalCenter
@@ -94,7 +85,6 @@ Rectangle {
94 collapsedRowCount:
85 collapsedRowCount:
95 collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex]
86 collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex]
96 filter: filterCheckBox.checked
87 filter: filterCheckBox.checked
97− expandable: expandableCheckBox.checked
98 minimumHorizontalSpacing: units.gu(1)
88 minimumHorizontalSpacing: units.gu(1)
99 delegateWidth: units.gu(6)
89 delegateWidth: units.gu(6)
100 delegateHeight: units.gu(6)
90 delegateHeight: units.gu(6)
@@ -142,31 +132,6 @@ Rectangle {
142 collapsedRowCountSelector.selectedIndex = 1
132 collapsedRowCountSelector.selectedIndex = 1
143 }
133 }
144
134
145− // Checks that the filter toggle button, the one that says "View All (xy)",
146− // shows up only when it's possible for the grid to be expanded.
147− function test_filterToggleButton() {
148− var filterToggleButton = findChild(filterGrid, "filterToggleButton");
149−
150− for (var i = 0; i < 4; ++i) {
151− collapsedRowCountSelector.selectedIndex = i
152− // row count == index + 1
153− // The total number of rows is 4.
154−
155− expandableCheckBox.checked = false
156− compare(filterToggleButton.visible, false)
157− expandableCheckBox.checked = true
158−
159− if ((i+1) < 4)
160− compare(filterToggleButton.visible, true)
161− else
162− compare(filterToggleButton.visible, false)
163− }
164−
165− // back to initial state
166− collapsedRowCountSelector.selectedIndex = 1
167− expandableCheckBox.checked = true
168− }
169−
170 function countVisibleDelegates() {
135 function countVisibleDelegates() {
171 return __countVisibleDelegates(filterGrid.visibleChildren, 0)
136 return __countVisibleDelegates(filterGrid.visibleChildren, 0)
172 }
137 }
tests/qmltests/Dash/Apps/tst_AppPreview.qml+17−3
@@ -35,7 +35,7 @@ Item {
35
35
36 property string commentary: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus a mi vitae augue rhoncus lobortis ut rutrum metus. Curabitur tortor leo, tristique sed mollis quis, condimentum venenatis nibh.";
36 property string commentary: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus a mi vitae augue rhoncus lobortis ut rutrum metus. Curabitur tortor leo, tristique sed mollis quis, condimentum venenatis nibh.";
37 QtObject { id: screenshots; property var value: ["fake_image1.png", "fake_image2.png", "fake_image3.png"] }
37 QtObject { id: screenshots; property var value: ["fake_image1.png", "fake_image2.png", "fake_image3.png"] }
38− QtObject { id: rating; property int value: 8 }
38+ QtObject { id: rating; property real value: 0.8 }
39 QtObject { id: rated; property int value: 120 }
39 QtObject { id: rated; property int value: 120 }
40 QtObject { id: reviews; property int value: 8 }
40 QtObject { id: reviews; property int value: 8 }
41 QtObject { id: progress; property string value: "source" }
41 QtObject { id: progress; property string value: "source" }
@@ -51,12 +51,12 @@ Item {
51 property string title: "Unity App"
51 property string title: "Unity App"
52 property string appIcon: "fake_image.png"
52 property string appIcon: "fake_image.png"
53 property string description: "This is an Application description"
53 property string description: "This is an Application description"
54+ property real rating: rating.value
55+ property int numRatings: reviews.value
54 property var execute: root.fake_call
56 property var execute: root.fake_call
55 property var infoMap: {
57 property var infoMap: {
56 "more-screenshots": screenshots,
58 "more-screenshots": screenshots,
57− "rating": rating,
58 "rated": rated,
59 "rated": rated,
59− "reviews": reviews,
60 "comments": comments
60 "comments": comments
61 }
61 }
62 property var actions: [
62 property var actions: [
@@ -88,6 +88,7 @@ Item {
88 var reviewField = findChild(appPreview, "reviewField");
88 var reviewField = findChild(appPreview, "reviewField");
89 reviewField.focus = false;
89 reviewField.focus = false;
90 reviewField.text = "";
90 reviewField.text = "";
91+ data.rating = rating.value;
91 }
92 }
92
93
93 function test_actions() {
94 function test_actions() {
@@ -148,5 +149,18 @@ Item {
148 compare(comment, comments.value[i][3], "Comment don't match");
149 compare(comment, comments.value[i][3], "Comment don't match");
149 }
150 }
150 }
151 }
152+
153+ function test_negative_rating() {
154+ data.rating = -1.0;
155+ var rated = findChild(appPreview, "reviewsLabel");
156+ verify(rated.visible == false);
157+ var commentsArea = findChild(appPreview, "commentsArea");
158+ verify(commentsArea.visible == false);
159+ var appReviews = findChild(appPreview, "appReviews");
160+ verify(appReviews.visible == false);
161+ var buttons = findChild(appPreview, "gridButtons");
162+ verify(buttons.visible == true);
163+ }
164+
151 }
165 }
152}
166}
tests/qmltests/Greeter/tst_Clock.qml+1−1
@@ -70,7 +70,7 @@ Rectangle {
70 compare(clock.__timerRunning, true, "Timer should be running")
70 compare(clock.__timerRunning, true, "Timer should be running")
71 wait(0) // spin event loop to trigger the timer
71 wait(0) // spin event loop to trigger the timer
72 verify(dateLabel.text !== dateString)
72 verify(dateLabel.text !== dateString)
73− if (timeLabel.text == "11:13") wait(60000) // next test will fail at 11:13, wait 1 minute
73+ if (timeLabel.text.indexOf("11:13") != -1) wait(60000) // next test will fail at 11:13, wait 1 minute
74 verify(timeLabel.text !== timeString)
74 verify(timeLabel.text !== timeString)
75 }
75 }
76
76
tests/qmltests/Launcher/tst_Launcher.qml+183−6
@@ -19,6 +19,7 @@ import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20import ".."
20import ".."
21import "../../../Launcher"
21import "../../../Launcher"
22+import Unity.Launcher 0.1
22
23
23/* Nothing is shown at first. If you drag from left edge you will bring up the
24/* Nothing is shown at first. If you drag from left edge you will bring up the
24 launcher. */
25 launcher. */
@@ -48,6 +49,11 @@ Item {
48 property int maxPanelX: 0
49 property int maxPanelX: 0
49 }
50 }
50
51
52+ SignalSpy {
53+ id: signalSpy
54+ target: LauncherModel
55+ }
56+
51 Connections {
57 Connections {
52 target: testCase.findChild(launcher, "launcherPanel")
58 target: testCase.findChild(launcher, "launcherPanel")
53
59
@@ -176,6 +182,22 @@ Item {
176 revealer.waitUntilLauncherDisappears();
182 revealer.waitUntilLauncherDisappears();
177 launcher.available = true;
183 launcher.available = true;
178 }
184 }
185+
186+ function test_countEmblems() {
187+ var launcherListView = findChild(launcher, "launcherListView");
188+ for (var i = 0; i < launcherListView.count; ++i) {
189+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
190+ compare(findChild(delegate, "countEmblem").visible, LauncherModel.get(i).count > 0)
191+ }
192+ }
193+
194+ function test_progressOverlays() {
195+ var launcherListView = findChild(launcher, "launcherListView");
196+ for (var i = 0; i < launcherListView.count; ++i) {
197+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
198+ compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
199+ }
200+ }
179 }
201 }
180
202
181 UT.UnityTestCase {
203 UT.UnityTestCase {
@@ -227,11 +249,10 @@ Item {
227 name: "LauncherInit"
249 name: "LauncherInit"
228 when: windowShown
250 when: windowShown
229
251
230− /*
252+ function initTestCase() {
231− * FIXME: There is a bug in ListView which makes it snap to an item
253+ var listView = findChild(launcher, "launcherListView");
232− * instead of the edge at startup. Enable this test once our patch for
254+ tryCompare(listView, "flicking", false)
233− * ListView has landed upstream.
255+ }
234− * https://bugreports.qt-project.org/browse/QTBUG-32251
235
256
236 function test_initFirstUnfolded() {
257 function test_initFirstUnfolded() {
237
258
@@ -245,6 +266,162 @@ Item {
245 // Now do check that snapping is in fact enabled
266 // Now do check that snapping is in fact enabled
246 compare(listView.snapMode, ListView.SnapToItem, "Snapping is not enabled");
267 compare(listView.snapMode, ListView.SnapToItem, "Snapping is not enabled");
247 }
268 }
248− */
269+ }
270+
271+ UT.UnityTestCase {
272+ id: dndTestCase
273+ name: "Drag and Drop"
274+ when: windowShown && initTestCase.completed
275+
276+ function test_dragndrop_data() {
277+ return [
278+ {tag: "startDrag", fullDrag: false },
279+ {tag: "fullDrag horizontal", fullDrag: true, orientation: ListView.Horizontal },
280+ {tag: "fullDrag vertical", fullDrag: true, orientation: ListView.Vertical },
281+ ];
282+ }
283+
284+ function test_dragndrop(data) {
285+ revealer.dragLauncherIntoView();
286+ var draggedItem = findChild(launcher, "launcherDelegate4")
287+ var item0 = findChild(launcher, "launcherDelegate0")
288+ var fakeDragItem = findChild(launcher, "fakeDragItem")
289+ var initialItemHeight = draggedItem.height
290+ var item4 = LauncherModel.get(4).appId
291+ var item3 = LauncherModel.get(3).appId
292+
293+ var listView = findChild(launcher, "launcherListView");
294+ listView.flick(0, units.gu(200));
295+ tryCompare(listView, "flicking", false);
296+
297+ // Initial state
298+ compare(draggedItem.itemOpacity, 1, "Item's opacity is not 1 at beginning")
299+ compare(fakeDragItem.visible, false, "FakeDragItem isn't invisible at the beginning")
300+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
301+
302+ // Doing longpress
303+ var currentMouseX = draggedItem.width / 2
304+ var currentMouseY = draggedItem.height / 2
305+ mousePress(draggedItem, currentMouseX, currentMouseY)
306+ // DraggedItem needs to hide and fakeDragItem become visible
307+ tryCompare(draggedItem, "itemOpacity", 0)
308+ tryCompare(fakeDragItem, "visible", true)
309+
310+ // Dragging a bit (> 1.5 gu)
311+ currentMouseX -= units.gu(2)
312+ mouseMove(draggedItem, currentMouseX, currentMouseY)
313+ // Other items need to expand and become 0.6 opaque
314+ tryCompare(item0, "angle", 0)
315+ tryCompare(item0, "itemOpacity", 0.6)
316+
317+ if (data.fullDrag) {
318+ // Dragging a bit more
319+ if (data.orientation == ListView.Horizontal) {
320+ currentMouseX -= units.gu(15)
321+ mouseMove(draggedItem, currentMouseX, currentMouseY, 100)
322+
323+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
324+ tryCompare(draggedItem, "height", units.gu(1))
325+
326+ // Dragging downwards. Item needs to move in the model
327+ currentMouseY -= initialItemHeight * 1.5
328+ mouseMove(draggedItem, currentMouseX, currentMouseY)
329+ } else if (data.orientation == ListView.Vertical) {
330+ currentMouseY -= initialItemHeight * 1.5
331+ mouseMove(draggedItem, currentMouseX, currentMouseY, 100)
332+
333+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
334+ tryCompare(draggedItem, "height", units.gu(1))
335+ }
336+
337+ waitForRendering(draggedItem)
338+ compare(LauncherModel.get(4).appId, item3)
339+ compare(LauncherModel.get(3).appId, item4)
340+ }
341+
342+ // Releasing and checking if initial values are restored
343+ mouseRelease(draggedItem)
344+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
345+ tryCompare(draggedItem, "itemOpacity", 1)
346+ tryCompare(fakeDragItem, "visible", false)
347+
348+ // Click somewhere in the empty space to make it hide in case it isn't
349+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
350+ revealer.waitUntilLauncherDisappears();
351+ }
352+
353+ function test_quicklist_dismiss() {
354+ revealer.dragLauncherIntoView();
355+ var draggedItem = findChild(launcher, "launcherDelegate5")
356+ var item0 = findChild(launcher, "launcherDelegate0")
357+ var fakeDragItem = findChild(launcher, "fakeDragItem")
358+ var dndArea = findChild(launcher, "dndArea")
359+
360+ // Initial state
361+ compare(dndArea.quickListPopover, null)
362+
363+ // Doing longpress
364+ mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
365+ tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
366+ verify(dndArea.quickListPopover != null)
367+
368+ // Dragging a bit (> 1.5 gu)
369+ mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
370+
371+ // QuickList needs to be closed when a drag operation starts
372+ tryCompare(dndArea, "quickListPopover", null)
373+
374+ mouseRelease(draggedItem);
375+ }
376+
377+ function test_quicklist_click_data() {
378+ return [
379+ {tag: "non-clickable", index: 1, clickable: false },
380+ {tag: "clickable", index: 2, clickable: true },
381+ ];
382+ }
383+
384+ function test_quicklist_click(data) {
385+ revealer.dragLauncherIntoView();
386+ var clickedItem = findChild(launcher, "launcherDelegate5")
387+ var dndArea = findChild(launcher, "dndArea")
388+
389+ // Initial state
390+ compare(dndArea.quickListPopover, null)
391+
392+ // Doing longpress
393+ mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
394+ tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
395+ verify(dndArea.quickListPopover != null)
396+
397+ mouseRelease(clickedItem);
398+
399+ var quickListEntry = findChild(dndArea.quickListPopover, "quickListEntry" + data.index)
400+
401+ signalSpy.clear();
402+ signalSpy.signalName = "quickListTriggered"
403+
404+ mouseClick(quickListEntry, quickListEntry.width / 2, quickListEntry.height / 2)
405+
406+ if (data.clickable) {
407+ // QuickList needs to be closed when some clickable item is clicked
408+ tryCompare(dndArea, "quickListPopover", null)
409+
410+ compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
411+ compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
412+ compare(signalSpy.signalArguments[0][1], 2)
413+
414+ } else {
415+
416+ // QuickList must not be closed when a non-clickable item is clicked
417+ verify(dndArea.quickListPopover != null)
418+
419+ compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
420+
421+ // Click somewhere in the empty space to dismiss the quicklist
422+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
423+ tryCompare(dndArea, "quickListPopover", null)
424+ }
425+ }
249 }
426 }
250}
427}
tests/qmltests/Panel/tst_MenuContent.qml+32−47
@@ -85,29 +85,29 @@ Item {
85
85
86 function activate_content(index)
86 function activate_content(index)
87 {
87 {
88− menuContent.currentMenuIndex = index
88+ menuContent.setCurrentMenuIndex(index)
89 }
89 }
90
90
91 function get_test_menu_objecName(index) {
91 function get_test_menu_objecName(index) {
92 return "menu_page"+(index+1);
92 return "menu_page"+(index+1);
93 }
93 }
94
94
95− property string test_menu_objectName : ""
95+ property string testTabObjectName : ""
96− function current_menu_equals_test_menu() {
96+
97− var current_loader = menu_content_test.findChild(menuContent, "menus").currentItem
97+ function selected_tab_equals_test_tab() {
98− if (current_loader == undefined) {
98+ var currentTab = menu_content_test.findChild(menuContent, "tabs").selectedTab
99− console.log("current_loader 'menus' undefined");
99+ if (currentTab == undefined) {
100+ console.log("selected tab undefined");
100 return false;
101 return false;
101 }
102 }
102
103
103− var menu = menu_content_test.findChild(menuContent, test_menu_objectName);
104+ var testTab = menu_content_test.findChild(menuContent, testTabObjectName);
104− if (menu == undefined) {
105+ if (testTab == undefined) {
105− console.log("test_menu " + test_menu_objectName + " undefined");
106+ console.log("test_tab " + testTabObjectName + " undefined");
106 return false;
107 return false;
107 }
108 }
108
109
109− // The parent of the menu will be the loader
110+ return testTab == currentTab;
110− return menu.parent == current_loader;
111 }
111 }
112
112
113 UT.UnityTestCase {
113 UT.UnityTestCase {
@@ -123,81 +123,66 @@ Item {
123
123
124 // Check that the correct menus are displayed for the requested item.
124 // Check that the correct menus are displayed for the requested item.
125 function test_show_menu() {
125 function test_show_menu() {
126− var menu_count = indicatorsModel.count;
126+ var menuCount = indicatorsModel.count;
127− verify(menu_count > 0, "Menu count should be greater than zero");
127+ verify(menuCount > 0, "Menu count should be greater than zero");
128
128
129− var menus = menu_content_test.findChild(menuContent, "menus")
129+ var tabs = menu_content_test.findChild(menuContent, "tabs")
130
130
131 // Loop over twice to test jump between last and first.
131 // Loop over twice to test jump between last and first.
132− for (var i = 0; i < menu_count*2; i++) {
132+ for (var i = 0; i < menuCount*2; i++) {
133
133
134− var menu_index = i%menu_count;
134+ var menuIndex = i%menuCount;
135
135
136− activate_content(menu_index);
136+ activate_content(menuIndex);
137− test_menu_objectName = get_test_menu_objecName(menu_index);
137+ testTabObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
138− compare(menus.currentIndex, menu_index, "Current menu index does not match selected menu index");
138+ compare(tabs.selectedTabIndex, menuIndex, "Current tab index does not match selected tab index");
139− tryCompareFunction(current_menu_equals_test_menu, true);
139+ tryCompareFunction(selected_tab_equals_test_tab, true);
140 }
140 }
141 }
141 }
142
142
143 // Calling activateContent should call start on all menus
143 // Calling activateContent should call start on all menus
144 function test_activate_content() {
144 function test_activate_content() {
145− var menu_count = indicatorsModel.count;
145+ var menuCount = indicatorsModel.count;
146− verify(menu_count > 0, "Menu count should be greater than zero");
146+ verify(menuCount > 0, "Menu count should be greater than zero");
147
147
148 // Ensure all the menus are stopped first
148 // Ensure all the menus are stopped first
149 menuContent.__contentActive = false;
149 menuContent.__contentActive = false;
150− for (var i = 0; i < menu_count; i++) {
150+ for (var i = 0; i < menuCount; i++) {
151 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
151 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
152 }
152 }
153
153
154 // activate content the content to call stop.
154 // activate content the content to call stop.
155 menuContent.activateContent();
155 menuContent.activateContent();
156− for (var i = 0; i < menu_count; i++) {
156+ for (var i = 0; i < menuCount; i++) {
157 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
157 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
158 }
158 }
159 }
159 }
160
160
161 // Calling activateContent should call stop on all menus.
161 // Calling activateContent should call stop on all menus.
162 function test_release_content() {
162 function test_release_content() {
163− var menu_count = indicatorsModel.count;
163+ var menuCount = indicatorsModel.count;
164− verify(menu_count > 0, "Menu count should be greater than zero");
164+ verify(menuCount > 0, "Menu count should be greater than zero");
165
165
166 // Ensure all the menus are started first
166 // Ensure all the menus are started first
167 menuContent.__contentActive = true;
167 menuContent.__contentActive = true;
168− for (var i = 0; i < menu_count; i++) {
168+ for (var i = 0; i < menuCount; i++) {
169 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
169 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
170 }
170 }
171 // release the content to call stop.
171 // release the content to call stop.
172 menuContent.releaseContent();
172 menuContent.releaseContent();
173− for (var i = 0; i < menu_count; i++) {
173+ for (var i = 0; i < menuCount; i++) {
174 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
174 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
175 }
175 }
176 }
176 }
177
177
178− // Header title should be the same as the item
179− function test_menu_header() {
180− var menu_count = indicatorsModel.count;
181− verify(menu_count > 0, "Menu count should be greater than zero");
182−
183− var header = findChild(menuContent, "header")
184−
185− for (var i = 0; i < menu_count; i++) {
186− activate_content(i);
187−
188− var menu_title = indicatorsModel.get(i).title;
189− compare(header.title, menu_title, "Header doesnt match menu title for menu " + i);
190− }
191− }
192−
193 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
178 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
194 function test_multi_activate() {
179 function test_multi_activate() {
195− var menu_count = indicatorsModel.count;
180+ var menuCount = indicatorsModel.count;
196− verify(menu_count > 0, "Menu count should be greater than zero");
181+ verify(menuCount > 0, "Menu count should be greater than zero");
197
182
198 for (var i = 0; i < 100; i++) {
183 for (var i = 0; i < 100; i++) {
199− activate_content(i % menu_count);
184+ activate_content(i % menuCount);
200− compare(menuContent.currentMenuIndex, i%menu_count);
185+ compare(menuContent.currentMenuIndex, i%menuCount);
201 }
186 }
202 wait(100);
187 wait(100);
203 }
188 }
tests/qmltests/tst_Shell.qml+77−18
@@ -19,8 +19,10 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21import QtTest 1.0
21import QtTest 1.0
22+import GSettings 1.0
22import Ubuntu.Application 0.1
23import Ubuntu.Application 0.1
23import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
25+import Powerd 0.1
24
26
25import "../.."
27import "../.."
26
28
@@ -53,26 +55,30 @@ Item {
53 when: windowShown
55 when: windowShown
54
56
55 function initTestCase() {
57 function initTestCase() {
56− // swipe away the greeter/lockscreen
58+ swipeAwayGreeter();
57− var touchX = shell.width - (shell.edgeSize / 2);
58− var touchY = shell.height / 2;
59− touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
60−
61− var dash = findChild(shell, "dash");
62− // wait until the animation has finished
63− tryCompare(dash, "contentScale", 1.0);
64− tryCompare(dash, "opacity", 1.0);
65 }
59 }
66
60
67 function cleanup() {
61 function cleanup() {
62+ // If a test invoked the greeter, make sure we swipe it away again
63+ var greeter = findChild(shell, "greeter");
64+ if (greeter.shown) {
65+ swipeAwayGreeter();
66+ }
67+
68 // kill all (fake) running apps
68 // kill all (fake) running apps
69− ApplicationManager.mainStageApplications.clear();
69+ killApps(ApplicationManager.mainStageApplications);
70− ApplicationManager.sideStageApplications.clear();
70+ killApps(ApplicationManager.sideStageApplications);
71
71
72 var dashHome = findChild(shell, "DashHome");
72 var dashHome = findChild(shell, "DashHome");
73 swipeUntilScopeViewIsReached(dashHome);
73 swipeUntilScopeViewIsReached(dashHome);
74 }
74 }
75
75
76+ function killApps(apps) {
77+ while (apps.count > 0) {
78+ ApplicationManager.stopProcess(apps.get(0));
79+ }
80+ }
81+
76 /*
82 /*
77 Test the effect of a right-edge drag on the dash in 3 situations:
83 Test the effect of a right-edge drag on the dash in 3 situations:
78 1 - when no application has been launched yet
84 1 - when no application has been launched yet
@@ -100,15 +106,52 @@ Item {
100 // Minimize that application once again
106 // Minimize that application once again
101 swipeFromLeftEdge();
107 swipeFromLeftEdge();
102
108
103− // kill it
109+ // Right edge behavior should now be the same as before that app,
104− ApplicationManager.mainStageApplications.clear();
110+ // was launched. Manually cleanup beforehand to get to initial
105−
111+ // state.
112+ cleanup();
106 waitForUIToSettle();
113 waitForUIToSettle();
107−
108− // Right edge behavior should now be the same as before that app was launched
109 checkRightEdgeDragWithNoRunningApps();
114 checkRightEdgeDragWithNoRunningApps();
110 }
115 }
111
116
117+ function test_suspend() {
118+ var greeter = findChild(shell, "greeter");
119+
120+ // Launch an app from the launcher
121+ dragLauncherIntoView();
122+ tapOnAppIconInLauncher();
123+ waitUntilApplicationWindowIsFullyVisible();
124+
125+ var mainApp = ApplicationManager.mainStageFocusedApplication;
126+ tryCompareFunction(function() { return mainApp != null; }, true);
127+
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);
134+ tryCompare(greeter, "showProgress", 1);
135+ tryCompare(ApplicationManager, "mainStageFocusedApplication", null);
136+
137+ // And wake up
138+ Powerd.displayPowerStateChange(Powerd.On, 0);
139+ tryCompare(ApplicationManager, "mainStageFocusedApplication", mainApp);
140+ tryCompare(greeter, "showProgress", 1);
141+ }
142+
143+ function swipeAwayGreeter() {
144+ var greeter = findChild(shell, "greeter");
145+ tryCompare(greeter, "showProgress", 1);
146+
147+ var touchX = shell.width - (shell.edgeSize / 2);
148+ var touchY = shell.height / 2;
149+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
150+
151+ // wait until the animation has finished
152+ tryCompare(greeter, "showProgress", 0);
153+ }
154+
112 /*
155 /*
113 Perform a right-edge drag when the Dash is being show and there are
156 Perform a right-edge drag when the Dash is being show and there are
114 no running/minimized apps to be restored.
157 no running/minimized apps to be restored.
@@ -123,8 +166,8 @@ Item {
123
166
124 var dash = findChild(shell, "dash");
167 var dash = findChild(shell, "dash");
125 // check that dash has normal scale and opacity
168 // check that dash has normal scale and opacity
126− compare(dash.contentScale, 1.0);
169+ tryCompare(dash.contentScale, 1.0);
127− compare(dash.opacity, 1.0);
170+ tryCompare(dash.opacity, 1.0);
128
171
129 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
172 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
130 true /* beginTouch */, false /* endTouch */);
173 true /* beginTouch */, false /* endTouch */);
@@ -277,5 +320,21 @@ Item {
277 }
320 }
278 } while (!isStill);
321 } while (!isStill);
279 }
322 }
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+ }
280 }
339 }
281}
340}
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+1−1
@@ -19,7 +19,7 @@
19
19
20#include <QtCore/QObject>
20#include <QtCore/QObject>
21#include <QtTest/QTest>
21#include <QtTest/QTest>
22−#include <QtQml/QtQml>
22+#include <QtQml/qqml.h>
23
23
24class TouchEventSequenceWrapper : public QObject
24class TouchEventSequenceWrapper : public QObject
25{
25{
tests/utils/modules/Unity/Test/UnityTestCase.qml+21−2
@@ -69,9 +69,28 @@ TestCase {
69 }
69 }
70
70
71
71
72− // Find an object with the given name recursively, starting
72+ // Find an object with the given name in the children tree of "obj"
73− // at the given object "obj"
74 function findChild(obj,objectName) {
73 function findChild(obj,objectName) {
74+ var childs = new Array(0);
75+ childs.push(obj)
76+ while (childs.length > 0) {
77+ if (childs[0].objectName == objectName) {
78+ return childs[0]
79+ }
80+ for (var i in childs[0].children) {
81+ childs.push(childs[0].children[i])
82+ }
83+ childs.splice(0, 1);
84+ }
85+ return undefined;
86+ }
87+
88+ // Find an object with the given name in the children tree of "obj"
89+ // Including invisible children like animations, timers etc.
90+ // Note: you should use findChild if you're not sure you need this
91+ // as this tree is much bigger and might contain stuff that goes
92+ // away randomly.
93+ function findInvisibleChild(obj,objectName) {
75 var childs = new Array(0);
94 var childs = new Array(0);
76 childs.push(obj)
95 childs.push(obj)
77 while (childs.length > 0) {
96 while (childs.length > 0) {