on xenial help sign in
merge trunk
commit14c99788931e2c4355387e93828edf0944f5dbb7 authorMichael Zanetti <michael.zanetti@canonical.com> dateMon 18 Nov 2013, 16:20 (13 years ago) parentbdb07c695427df
186 files changed+4650−1776
Bottombar/Bottombar.qml+4−4 Components/Carousel.qml+26−10 Components/DragHandle.qml+22−24 Components/DraggingArea.qml+1−2 Components/EdgeDemoOverlay.qml+2 Components/EdgeDragArea.qml+12−4 Components/FilterGrid.qml+3 Components/LazyImage.qml+2−2 Components/PageHeader.qml+4 Components/PinPadButton.qml+40−13 Components/ResponsiveGridView.qml+10 Components/Revealer.qml+1−2 Components/Stage.qml+8−7 Components/Tile.qml+9 Components/Time.js+9−9 Components/UbuntuShapeForItem.qml+1 Dash/Apps/AppPreview.qml+9 Dash/Apps/ApplicationsFilterGrid.qml+14−5 Dash/Apps/DashPluginFilterGrid.qml+2−4 Dash/Apps/RunningApplicationTile.qml−2 Dash/DashBar.qml+2−2 Dash/DashContent.qml+2−2 Dash/DashPreview.qml+1−3 tests/mocks/QMenuModel/UnityMenuModel.qml → Dash/DashPreviewPlaceholder.qml+6−7 Dash/Generic/GenericCarouselLoader.qml+30 Dash/Generic/GenericFilterGrid.qml+4−6 Dash/Generic/WeatherFilterGrid.qml+2−4 Dash/GenericScopeView.qml+229−67 Dash/Music/AlbumTile.qml−116 Launcher/QuicklistForegroundStyle.qml → Dash/Music/MusicCarouselLoader.qml+6−5 Dash/Music/MusicFilterGrid.qml+2−4 Dash/SearchableResultModel.qml+4 Dash/Video/VideosCarousel.qml → Dash/Video/VideoCarousel.qml Components/Math.js → Dash/Video/VideoCarouselLoader.qml+6−8 Dash/Video/VideoFilterGrid.qml+2−4 Dash/graphics/tooltip_arrow@27.png Greeter/Greeter.qml+3−3 Greeter/GreeterContent.qml+23−20 Greeter/Lockscreen.qml+8−4 Greeter/PinLockscreen.qml+34−4 Launcher/Launcher.qml+2−2 Launcher/LauncherPanel.qml+87−46 Notifications/NotificationMenuItemFactory.qml+20−6 Panel/Indicators.qml+1−2 Panel/Indicators/DefaultIndicatorWidget.qml+32−10 Shell.qml+9−3 data/unity8.conf+20 debian/changelog+102 debian/control+1−1 main.cpp+8−8 plugins/ListViewWithPageHeader/listviewwithpageheader.cpp+4−1 plugins/ListViewWithPageHeader/listviewwithpageheader.h+1 plugins/Ubuntu/Gestures/AxisVelocityCalculator.cpp+9−18 plugins/Ubuntu/Gestures/AxisVelocityCalculator.h+9−16 plugins/Ubuntu/Gestures/CMakeLists.txt+1 plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+307−71 plugins/Ubuntu/Gestures/DirectionalDragArea.h+50−13 plugins/Ubuntu/Gestures/TimeSource.cpp+49 plugins/Ubuntu/Gestures/TimeSource.h+54 plugins/Unity/CMakeLists.txt−1 plugins/Unity/Indicators/CMakeLists.txt+1 plugins/Unity/Indicators/Messaging/qml/MessageMenuItemFactory.qml+3−4 plugins/Unity/Indicators/Messaging/qml/SimpleTextMessage.qml+9−2 plugins/Unity/Indicators/Messaging/qml/SnapDecision.qml+9−3 plugins/Unity/Indicators/plugin.cpp+2 plugins/Unity/Indicators/qml/IndicatorPage.qml+27−6 plugins/Unity/Indicators/qml/IndicatorWidget.qml+1−1 plugins/Unity/Indicators/qml/SliderMenuItem.qml+3−2 plugins/Unity/Indicators/timeformatter.cpp+172 plugins/Unity/Indicators/timeformatter.h+54 plugins/Unity/Indicators/unitymenumodelstack.cpp+98−7 plugins/Unity/Indicators/unitymenumodelstack.h+10−2 plugins/Unity/Launcher/launcheritem.cpp+4−2 plugins/Unity/plugin.cpp−12 plugins/Utils/CMakeLists.txt+1 plugins/Unity/bottombarvisibilitycommunicatorshell.cpp → plugins/Utils/bottombarvisibilitycommunicatorshell.cpp plugins/Unity/bottombarvisibilitycommunicatorshell.h → plugins/Utils/bottombarvisibilitycommunicatorshell.h plugins/Utils/plugin.cpp+12 po/ar.po+9−5 po/ast.po+8−25 po/az.po+8−10 po/bg.po+8−16 po/ca.po+8−25 po/cs.po+8−19 po/de.po+8−4 po/el.po+8−10 po/en_AU.po+8−25 po/en_GB.po+8−25 po/eo.po+8−4 po/es.po+8−25 po/fa.po+227 po/fi.po+9−26 po/fo.po+8−10 po/fr.po+8−10 po/gd.po+8−25 po/gl.po+8−22 po/he.po+8−25 po/hr.po+8−10 po/hu.po+8−10 po/hy.po+8−10 po/id.po+8−4 po/is.po+8−25 po/it.po+8−25 po/ja.po+8−4 po/km.po+10−27 po/kn.po+8−10 po/ko.po+8−7 po/ky.po+8−10 po/lo.po+8−10 po/lv.po+8−10 po/my.po+8−19 po/nb.po+8−10 po/nl.po+8−10 po/pl.po+8−4 po/pt_BR.po+8−25 po/ro.po+8−10 po/ru.po+8−25 po/shn.po+8−4 po/si.po+8−4 po/sl.po+8−25 po/sr.po+8−10 po/sv.po+8−4 po/ta.po+233 po/te.po+8−7 po/tr.po+8−25 po/ug.po+8−19 po/uk.po+8−4 po/unity8.pot+20−7 po/xh.po+8−4 po/zh_CN.po+8−25 po/zh_TW.po+8−10 run_on_device+24−4 tests/autopilot/unity8/process_helpers.py+116 tests/autopilot/unity8/shell/emulators/create_interactive_notification.py tests/autopilot/unity8/shell/emulators/dash.py+7−17 tests/autopilot/unity8/shell/emulators/hud.py+12−4 tests/autopilot/unity8/shell/emulators/main_window.py+7−1 tests/autopilot/unity8/shell/tests/__init__.py+58−27 tests/autopilot/unity8/shell/tests/test_greeter.py+38 tests/autopilot/unity8/shell/tests/test_notifications.py+49−42 tests/mocks/QMenuModel/CMakeLists.txt+23 tests/mocks/QMenuModel/UnityMenuAction.qml+30 tests/mocks/QMenuModel/actionstateparser.cpp+19 tests/mocks/QMenuModel/actionstateparser.h+35 tests/mocks/QMenuModel/plugin.cpp+30 tests/mocks/QMenuModel/plugin.h+34 tests/mocks/QMenuModel/qmldir+3−1 tests/mocks/QMenuModel/unitymenumodel.cpp+239 tests/mocks/QMenuModel/unitymenumodel.h+96 tests/mocks/Unity/Indicators/IndicatorBase.qml+37 plugins/Unity/Indicators/Messaging/qml/utils.js → tests/mocks/Unity/Indicators/IndicatorWidget.qml+8−7 tests/mocks/Unity/Indicators/RootActionState.qml+17−9 tests/mocks/Unity/Indicators/qmldir+2 tests/mocks/Unity/fake_preview.cpp+9−1 tests/mocks/Unity/fake_preview.h+2 tests/mocks/Unity/fake_scope.cpp+21−17 tests/mocks/Unity/fake_scope.h+7−2 tests/mocks/Unity/fake_unity_plugin.cpp+3 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp+20 tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml+2−10 tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml+5−13 tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml+3−10 tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml+5−13 tests/plugins/Ubuntu/Gestures/edgeDragExample.qml+7−1 tests/plugins/Ubuntu/Gestures/tst_AxisVelocityCalculator.cpp+3−3 tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+285−96 tests/plugins/Unity/Indicators/CMakeLists.txt+36−26 tests/plugins/Unity/Indicators/timeformattertest.cpp+55 tests/plugins/Unity/Indicators/unitymenumodelstacktest.cpp+206 tests/qmltests/CMakeLists.txt+5−3 tests/qmltests/Components/tst_DragHandle.cpp+4−5 tests/qmltests/Components/tst_EdgeDemoOverlay.qml+2−2 tests/qmltests/Components/tst_MathLocal.qml−91 tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml+1 tests/qmltests/Dash/qml/FakeScopeView.qml+1 tests/qmltests/Dash/tst_Dash.qml+2 tests/qmltests/Dash/tst_DashContent.qml+14 tests/qmltests/Dash/tst_GenericScopeView.qml+126−13 tests/qmltests/Greeter/tst_Clock.qml+16 tests/qmltests/Greeter/tst_Lockscreen.qml+41−4 tests/qmltests/Greeter/tst_Phone.qml+23−10 tests/qmltests/Launcher/tst_Launcher.qml+47−11 tests/qmltests/Panel/Indicators/tst_DefaultIndicatorWidget.qml+97 tests/qmltests/Panel/tst_Panel.qml−1 tests/qmltests/tst_Shell.qml+85−22 tests/utils/modules/Unity/Test/UnityTestCase.qml+34
Bottombar/Bottombar.qml+4−4
@@ -16,10 +16,10 @@
1616
1717import QtQuick 2.0
1818import "../Components"
19−import "../Components/Math.js" as MathLocal
20−import Unity 0.1
19+import Utils 0.1
2120import Unity.Application 0.1
2221import Ubuntu.Gestures 0.1
22+import Ubuntu.Components 0.1
2323
2424Item {
2525 id: bottombar
@@ -82,7 +82,7 @@ Item {
8282
8383 readonly property bool centeredHud: parent.width < units.gu(68) // Nexus 7 has 67 gu width
8484
85− x: centeredHud ? parent.width / 2 - width / 2 : MathLocal.clamp(dragArea.touchStartX - (width / 2), 0, bottombar.width - width)
85+ x: centeredHud ? parent.width / 2 - width / 2 : MathUtils.clamp(dragArea.touchStartX - (width / 2), 0, bottombar.width - width)
8686 y: bottombar.height - bottomEdgeButtonCenterDistance - (height / 2) - bottomMargin
8787 z: 1
8888 visible: opacity != 0
@@ -136,7 +136,7 @@ Item {
136136 readonly property real distanceFromThreshold: (-distance) - distanceThreshold // distance is negative
137137 readonly property real revealDistance: units.gu(2)
138138 readonly property real commitDistance: units.gu(6)
139− readonly property real commitProgress: MathLocal.clamp(distanceFromThreshold / commitDistance, 0, 1)
139+ readonly property real commitProgress: MathUtils.clamp(distanceFromThreshold / commitDistance, 0, 1)
140140
141141 onStatusChanged: {
142142 if (status === DirectionalDragArea.WaitingForTouch) {
Components/Carousel.qml+26−10
@@ -45,18 +45,22 @@ Item {
4545 property int drawBuffer: width / pathItemCount // an "ok" value - but values used from the listView cause loops
4646 /// The selected item can be shown in a different size controlled by selectedItemScaleFactor
4747 property real selectedItemScaleFactor: 1.1
48+ /// The index of the item that should be highlighted
49+ property alias highlightIndex: listView.highlightIndex
50+ /// exposes the delegate of the currentItem
51+ readonly property alias currentItem: listView.currentItem
52+ /// exposes the distance to the next row (only one row in carousel, so it's the topMargins)
53+ readonly property alias verticalSpacing: listView.verticalMargin
4854
4955 /// Emitted when the user clicked on an item
5056 /// @param index is the index of the clicked item
51− /// @param delegateItem is the clicked component/delegate itself
5257 /// @param itemY is y of the clicked delegate
53− signal clicked(int index, var delegateItem, real itemY)
58+ signal clicked(int index, real itemY)
5459
5560 /// Emitted when the user pressed and held on an item
5661 /// @param index is the index of the held item
57− /// @param delegateItem is the held component/delegate itself
5862 /// @param itemY is y of the held delegate
59− signal pressAndHold(int index, var delegateItem, real itemY)
63+ signal pressAndHold(int index, real itemY)
6064
6165 implicitHeight: listView.tileHeight * selectedItemScaleFactor
6266
@@ -75,6 +79,7 @@ Item {
7579 property real newContentX: disabledNewContentX
7680 property real pathItemCount: referenceWidth / referenceTileWidth
7781 property real tileAspectRatio: 1
82+ property int highlightIndex: -1
7883
7984 /* The positioning and scaling of the items in the carousel is based on the variable
8085 'continuousIndex', a continuous real variable between [0, 'carousel.model.count'],
@@ -136,13 +141,14 @@ Item {
136141 height: listView.tileHeight
137142 }
138143
139− boundsBehavior: Flickable.StopAtBounds
144+ boundsBehavior: Flickable.DragOverBounds
140145 cacheBuffer: carousel.cacheBuffer
141146 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
142147 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
143148 orientation: ListView.Horizontal
144149
145150 function itemClicked(index, delegateItem) {
151+ listView.currentIndex = index
146152 var x = CarouselJS.getXFromContinuousIndex(index,
147153 realWidth,
148154 realContentWidth,
@@ -151,11 +157,11 @@ Item {
151157 gapToEndPhase,
152158 carousel.drawBuffer)
153159
154− if (Math.abs(x - contentX) < 1) {
160+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
155161 /* We're clicking the selected item and
156162 we're in the neighbourhood of radius 1 pixel from it.
157163 Let's emit the clicked signal. */
158− carousel.clicked(index, delegateItem, delegateItem.y)
164+ carousel.clicked(index, delegateItem.y)
159165 return
160166 }
161167
@@ -175,11 +181,11 @@ Item {
175181 gapToEndPhase,
176182 carousel.drawBuffer);
177183
178− if (Math.abs(x - contentX) < 1) {
184+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
179185 /* We're pressAndHold the selected item and
180186 we're in the neighbourhood of radius 1 pixel from it.
181187 Let's emit the pressAndHold signal. */
182− carousel.pressAndHold(index, delegateItem, delegateItem.y);
188+ carousel.pressAndHold(index, delegateItem.y);
183189 return;
184190 }
185191
@@ -190,6 +196,12 @@ Item {
190196 newContentXAnimation.start();
191197 }
192198
199+ onHighlightIndexChanged: {
200+ if (highlightIndex != -1) {
201+ itemClicked(highlightIndex)
202+ }
203+ }
204+
193205 onMovementStarted: {
194206 stepAnimation.stop()
195207 newContentXAnimation.stop()
@@ -273,14 +285,18 @@ Item {
273285 itemTranslationScale,
274286 listView.maximumItemTranslation)
275287
288+ readonly property real xTransform: listView.viewTranslation + translationX * listView.scaleFactor
289+ readonly property real center: x - listView.contentX + xTransform - drawBuffer + (width/2)
290+
276291 width: listView.tileWidth
277292 height: listView.tileHeight
278293 scale: itemScale * explicitScaleFactor
279294 sourceComponent: itemComponent
280295 z: cachedTiles - Math.abs(index - listView.selectedIndex)
296+ opacity: highlightIndex == -1 ? 1 : (highlightIndex == index ? 0.6 : 0.2)
281297
282298 transform: Translate {
283− x: listView.viewTranslation + translationX * listView.scaleFactor
299+ x: xTransform
284300 }
285301
286302 Behavior on explicitScaleFactor {
Components/DragHandle.qml+22−24
@@ -17,7 +17,6 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import Ubuntu.Gestures 0.1
20−import "Math.js" as MathLocal
2120
2221/*
2322 Put a DragHandle inside a Showable to enable the user to drag it from that handle.
@@ -48,9 +47,12 @@ EdgeDragArea {
4847 id: dragArea
4948 objectName: "dragHandle"
5049
51− // Disable gesture detection by default when hinting is used.
52− // It doesn't make sense to have both.
53− distanceThreshold: hintDisplacement > 0 ? 0 : units.gu(1.5)
50+ // Disable most of the gesture recognition parameters by default when hinting is used as
51+ // it conflicts with the hinting idea.
52+ // The only part we keep in this situation is that it must be a single-finger gesture.
53+ distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold
54+ maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime
55+ maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation
5456
5557 property bool stretch: false
5658
@@ -113,8 +115,8 @@ EdgeDragArea {
113115 }
114116 }
115117
116− function limitMovement(step) {
117− var targetValue = MathLocal.clamp(dragParent[targetProp] + step, minValue, maxValue);
118+ function limitMovement(inputStep) {
119+ var targetValue = MathUtils.clamp(dragParent[targetProp] + inputStep, minValue, maxValue);
118120 var step = targetValue - dragParent[targetProp];
119121
120122 if (hintDisplacement == 0) {
@@ -181,29 +183,25 @@ EdgeDragArea {
181183 }
182184 }
183185
184− onDraggingChanged: {
185− if (dragging) {
186− if (hintDisplacement > 0) {
187− hintingAnimation.targetValue = d.startValue;
188− hintingAnimation.start();
189− }
190− } else {
191− hintingAnimation.stop();
192− }
193− }
194−
195186 onStatusChanged: {
196187 if (status === DirectionalDragArea.WaitingForTouch) {
188+ hintingAnimation.stop();
197189 if (d.previousStatus === DirectionalDragArea.Recognized) {
198190 d.onFinishedRecognizedGesture();
191+ } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
192+ // Gesture was rejected.
193+ d.rollbackDrag();
194+ }
195+ } else /* Undecided || Recognized */ {
196+ if (d.previousStatus === DirectionalDragArea.WaitingForTouch ||
197+ d.previousStatus === undefined) {
198+ dragEvaluator.reset();
199+ d.startValue = parent[d.targetProp];
200+ }
201+ if (hintDisplacement > 0) {
202+ hintingAnimation.targetValue = d.startValue;
203+ hintingAnimation.start();
199204 }
200− d.startValue = parent[d.targetProp];
201− }
202−
203− if (d.previousStatus === DirectionalDragArea.WaitingForTouch ||
204− d.previousStatus === undefined) {
205− dragEvaluator.reset();
206− d.startValue = parent[d.targetProp];
207205 }
208206
209207 d.previousStatus = status;
Components/DraggingArea.qml+1−2
@@ -16,7 +16,6 @@
1616
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
19−import "Math.js" as MathLocal
2019
2120MouseArea {
2221 id: draggingArea
@@ -26,7 +25,7 @@ MouseArea {
2625 property real dragVelocity: 0
2726 property real dragValue: (orientation == Qt.Vertical ? (mouseY - __pressedPosition.y)
2827 : (mouseX - __pressedPosition.x))
29− property real lateralPosition: orientation == Qt.Horizontal ? MathLocal.clamp(mouseY, 0, height) : MathLocal.clamp(mouseX, 0, width)
28+ property real lateralPosition: orientation == Qt.Horizontal ? MathUtils.clamp(mouseY, 0, height) : MathUtils.clamp(mouseX, 0, width)
3029 property point __pressedPosition: Qt.point(0, 0)
3130 property var __dragEvents: []
3231 property bool clickValidated: true
Components/EdgeDemoOverlay.qml+2
@@ -105,6 +105,8 @@ Showable {
105105 visible: overlay.active
106106
107107 MouseArea {
108+ objectName: "backgroundShadeMouseArea"
109+
108110 anchors.fill: parent
109111 enabled: overlay.edge == "none" && overlay.opacity == 1.0
110112 onClicked: overlay.doSkip()
Components/EdgeDragArea.qml+12−4
@@ -29,8 +29,16 @@ DirectionalDragArea {
2929 //
3030 // The idea here is that it's better having lax rules than false negatives.
3131 // False negatives are very frustrating to the user.
32− maxDeviation: units.gu(3)
33− wideningAngle: 50
34− distanceThreshold: units.gu(1.5)
35− minSpeed: units.gu(0) // some people were getting false negatives with it enabled.
32+ maxDeviation: defaultMaxDeviation
33+ wideningAngle: defaultWideningAngle
34+ distanceThreshold: defaultDistanceThreshold
35+ minSpeed: defaultMinSpeed
36+ maxSilenceTime: defaultMaxSilenceTime
37+
38+ readonly property real defaultMaxDeviation: units.gu(3)
39+ readonly property real defaultWideningAngle: 50
40+ readonly property real defaultDistanceThreshold: units.gu(1.5)
41+ // some people were getting false negatives with it enabled.
42+ readonly property real defaultMinSpeed: units.gu(0)
43+ readonly property int defaultMaxSilenceTime: 200
3644}
Components/FilterGrid.qml+3
@@ -54,6 +54,7 @@ Item {
5454 property alias delegateWidth: iconTileGrid.delegateWidth
5555 property alias delegateHeight: iconTileGrid.delegateHeight
5656 property alias verticalSpacing: iconTileGrid.verticalSpacing
57+ readonly property alias margins: iconTileGrid.margins
5758 property alias delegate: iconTileGrid.delegate
5859 property alias cellWidth: iconTileGrid.cellWidth
5960 property alias cellHeight: iconTileGrid.cellHeight
@@ -62,6 +63,8 @@ Item {
6263 readonly property alias flicking: iconTileGrid.flicking
6364 readonly property alias moving: iconTileGrid.moving
6465 readonly property alias pressDelay: iconTileGrid.pressDelay
66+ property alias highlightIndex: iconTileGrid.highlightIndex
67+ readonly property alias currentItem: iconTileGrid.currentItem
6568
6669 height: !filterAnimation.running ? childrenRect.height : height
6770 clip: filterAnimation.running
Components/LazyImage.qml+2−2
@@ -100,10 +100,10 @@ Item {
100100 verticalAlignment: Image.AlignVCenter
101101 sourceSize.width: root.scaleTo == "width" ? root.width
102102 : root.scaleTo == "fit" && root.width < root.height ? root.width
103− : undefined
103+ : 0
104104 sourceSize.height: root.scaleTo == "height" ? root.height
105105 : root.scale == "fit" && root.height < root.width ? root.height
106− : undefined
106+ : 0
107107 }
108108 }
109109
Components/PageHeader.qml+4
@@ -48,6 +48,10 @@ Item {
4848 searchField.focus = false;
4949 }
5050
51+ function unfocus() {
52+ searchField.focus = false;
53+ }
54+
5155 Connections {
5256 target: greeter
5357 onShownChanged: if (shown) resetSearch()
Components/PinPadButton.qml+40−13
@@ -21,24 +21,51 @@ Item {
2121 id: root
2222
2323 property alias text: label.text
24− property alias iconName: icon.name
24+ property alias subText: subTextLabel.text
25+ property string iconName
2526
2627 signal clicked()
2728
28− Label {
29− id: label
29+ Column {
3030 anchors.centerIn: parent
31− color: "#f3f3e7"
32− fontSize: "large"
33− font.weight: Font.DemiBold
34− }
31+ width: parent.width
32+ height: childrenRect.height
3533
36− Icon {
37− id: icon
38− height: units.gu(3)
39− width: height
40− anchors.centerIn: parent
41− color: "#f3f3e7"
34+ Item {
35+ anchors {
36+ left: parent.left
37+ right: parent.right
38+ }
39+ height: label.visible || icon.visible ? Math.max(label.height, icon.height) : 0
40+
41+ Label {
42+ id: label
43+ anchors.centerIn: parent
44+ width: parent.width
45+ horizontalAlignment: Text.AlignHCenter
46+ color: "#f3f3e7"
47+ fontSize: "large"
48+ font.weight: Font.DemiBold
49+ visible: text.length > 0
50+ }
51+
52+ Icon {
53+ id: icon
54+ height: units.gu(3)
55+ width: height
56+ anchors.centerIn: parent
57+ name: root.iconName
58+ color: "#f3f3e7"
59+ visible: name.length > 0
60+ }
61+ }
62+ Label {
63+ id: subTextLabel
64+ fontSize: "small"
65+ color: "grey"
66+ anchors.horizontalCenter: parent.horizontalCenter
67+ visible: text.length > 0
68+ }
4269 }
4370
4471 MouseArea {
Components/ResponsiveGridView.qml+10
@@ -26,6 +26,7 @@ Item {
2626 property int maximumNumberOfColumns: 6
2727 readonly property int columns: gridView.columns
2828 property alias verticalSpacing: gridView.verticalSpacing
29+ readonly property alias margins: gridView.margin
2930 property int delegateWidth
3031 property int delegateHeight
3132 property alias model: gridView.model
@@ -41,6 +42,8 @@ Item {
4142 readonly property alias pressDelay: gridView.pressDelay
4243 property alias delegateCreationBegin: gridView.delegateCreationBegin
4344 property alias delegateCreationEnd: gridView.delegateCreationEnd
45+ property alias highlightIndex: gridView.highlightIndex
46+ readonly property alias currentItem: gridView.currentItem
4447
4548 function contentHeightForRows(rows) {
4649 return rows * cellHeight + verticalSpacing
@@ -77,8 +80,15 @@ Item {
7780 property real horizontalSpacing: spacingForColumns(columns)
7881 property real verticalSpacing: horizontalSpacing
7982 property int margin: allocatableHorizontalSpace - columns * horizontalSpacing
83+ property int highlightIndex: -1
8084
8185 cellWidth: delegateWidth + horizontalSpacing
8286 cellHeight: delegateHeight + verticalSpacing
87+
88+ onHighlightIndexChanged: {
89+ if (highlightIndex != -1) {
90+ currentIndex = highlightIndex
91+ }
92+ }
8393 }
8494}
Components/Revealer.qml+1−2
@@ -16,7 +16,6 @@
1616
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
19−import "Math.js" as MathLocal
2019
2120Item {
2221 id: revealer
@@ -95,7 +94,7 @@ Item {
9594 onClosedValueChanged: if (!__opened && !dragging) target[boundProperty] = closedValue
9695
9796 function __computeValue(dragPosition) {
98− return MathLocal.clamp(dragToValue(dragPosition), __hintValue, openedValue)
97+ return MathUtils.clamp(dragToValue(dragPosition), __hintValue, openedValue)
9998 }
10099
101100 function __open() {
Components/Stage.qml+8−7
@@ -482,19 +482,20 @@ Showable {
482482 switchToApplicationAnimationController.progress += 0.4 * delta
483483 }
484484
485− property bool gotRejected: false
485+ property int lastStatus: -1
486486
487487 onStatusChanged: {
488488 if (status == DirectionalDragArea.Undecided) {
489− gotRejected = false
490489 onUndecided();
491− } else if (status == DirectionalDragArea.Rejected) {
492− switchToApplicationAnimationController.completeToBeginningWithSignal();
493− gotRejected = true;
494490 } else if (status == DirectionalDragArea.WaitingForTouch) {
495− if (!gotRejected)
496− onGestureEnded()
491+ if (lastStatus == DirectionalDragArea.Undecided) {
492+ // got rejected
493+ switchToApplicationAnimationController.completeToBeginningWithSignal();
494+ } else {
495+ onGestureEnded();
496+ }
497497 }
498+ lastStatus = status;
498499 }
499500
500501 function onUndecided() {
Components/Tile.qml+9
@@ -26,5 +26,14 @@ AbstractButton {
2626 property int imageHeight
2727 property int maximumLineCount: 1
2828
29+ readonly property int center: (index % GridView.view.columns * width) + (width / 2)
30+
31+ opacity: GridView.view.highlightIndex === -1 ? 1 :
32+ GridView.view.highlightIndex === index ? 0.6 : 0.2
33+
34+ Behavior on opacity {
35+ UbuntuNumberAnimation {}
36+ }
37+
2938 style: TileStyle {}
3039}
Components/Time.js+9−9
@@ -16,15 +16,15 @@
1616
1717.pragma library
1818
19−var factors = {
20− "second": [ 1000, "a second ago", "in a second", "%1 seconds ago", "in %1 seconds" ],
21− "minute": [ 60, "a minute ago", "in a minute", "%1 minutes ago", "in %1 minutes" ],
22− "hour": [ 60, "an hour ago", "in an hour", "%1 hours ago", "in %1 hours" ],
23− "day": [ 24, "yesterday", "tomorrow", "%1 days ago", "in %1 days" ],
24− "week": [ 7, "a week ago", "in a week", "about %1 weeks ago", "in about %1 weeks" ],
25− "month": [ 4.35, "a month ago", "in a month", "%1 months ago", "in %1 months" ], // == 365.25 days / 12 months / 7 days
26− "year": [ 12, "a year ago", "in a year", "%1 years ago", "in %1 years" ]
27−}
19+var factors = [
20+ [ 1000, "a second ago", "in a second", "%1 seconds ago", "in %1 seconds" ],
21+ [ 60, "a minute ago", "in a minute", "%1 minutes ago", "in %1 minutes" ],
22+ [ 60, "an hour ago", "in an hour", "%1 hours ago", "in %1 hours" ],
23+ [ 24, "yesterday", "tomorrow", "%1 days ago", "in %1 days" ],
24+ [ 7, "a week ago", "in a week", "about %1 weeks ago", "in about %1 weeks" ],
25+ [ 4.35, "a month ago", "in a month", "%1 months ago", "in %1 months" ], // == 365.25 days / 12 months / 7 days
26+ [ 12, "a year ago", "in a year", "%1 years ago", "in %1 years" ]
27+]
2828
2929function readableFromNow(date, now) {
3030 var then = new Date(date);
Components/UbuntuShapeForItem.qml+1
@@ -31,6 +31,7 @@ Item {
3131
3232 ShaderEffectSource {
3333 id: source
34+ anchors.centerIn: parent // Placed under shape, so it's hidden
3435 width: 1
3536 height: 1
3637 hideSource: true
Dash/Apps/AppPreview.qml+9
@@ -44,6 +44,15 @@ GenericPreview {
4444 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
4545 cacheBuffer: width * 3
4646
47+ // FIXME: Because of ListViews inside ListViews inside Flickables inside ListViews (and some more)
48+ // we finally reached the point where this ListView doesn't correctly get swipe input any more but
49+ // instead the parent ListView is the one that is swiped. This MouseArea sort of creates a blocking
50+ // layer to make sure this ListView can be swiped, regardless of what's behind it.
51+ MouseArea {
52+ anchors.fill: parent
53+ enabled: parent.contentWidth > parent.width
54+ }
55+
4756 LazyImage {
4857 objectName: "placeholderScreenshot"
4958 anchors {
Dash/Apps/ApplicationsFilterGrid.qml+14−5
@@ -19,7 +19,7 @@ import "../../Components"
1919import "../../Applications"
2020
2121FilterGrid {
22− id: filterGrid
22+ id: filtergrid
2323
2424 filter: false
2525 minimumHorizontalSpacing: units.gu(0.5)
@@ -28,9 +28,11 @@ FilterGrid {
2828 delegateHeight: units.gu(9.5)
2929 verticalSpacing: units.gu(2)
3030
31− signal clicked(int index, variant data)
31+ signal clicked(int index, real itemY)
32+ signal pressAndHold(int index, real itemY)
3233
3334 delegate: Tile {
35+ id: tile
3436 objectName: "delegate" + index
3537 Application {
3638 id: application
@@ -44,12 +46,19 @@ FilterGrid {
4446
4547 property string icon: model.icon ? model.icon : "../../graphics/applicationIcons/" + application.icon + ".png" // FIXME: this is temporary
4648
47− width: filterGrid.cellWidth
48− height: filterGrid.cellHeight
49+ width: filtergrid.cellWidth
50+ height: filtergrid.cellHeight
4951 text: model.title ? model.title : application.name // FIXME: this is temporary
5052 imageWidth: units.gu(8)
5153 imageHeight: units.gu(7.5)
5254 source: icon
53− onClicked: filterGrid.clicked(index, application.desktopFile);
55+
56+ onClicked: {
57+ filtergrid.clicked(index, tile.y)
58+ }
59+
60+ onPressAndHold: {
61+ filtergrid.pressAndHold(index, tile.y)
62+ }
5463 }
5564}
Dash/Apps/DashPluginFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
3737 }
3838
3939 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, tile.y)
4241 }
4342
4443 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, tile.y)
4745 }
4846 }
4947}
Dash/Apps/RunningApplicationTile.qml−2
@@ -105,8 +105,6 @@ AbstractButton {
105105 baseline: parent.bottom
106106 left: parent.left
107107 right: parent.right
108− leftMargin: units.gu(1)
109− rightMargin: units.gu(1)
110108 }
111109 text: (application) ? application.name : ""
112110
Dash/DashBar.qml+2−2
@@ -35,7 +35,7 @@ Item {
3535
3636 function startNavigation() {
3737 timeout.stop()
38− panel.opened = true
38+ panel.open()
3939 }
4040
4141 function stopNavigation() {
@@ -43,7 +43,7 @@ Item {
4343 }
4444
4545 function finishNavigation() {
46− panel.opened = false
46+ panel.close()
4747 }
4848
4949 Timer {
Dash/DashContent.qml+2−2
@@ -116,8 +116,8 @@ Item {
116116 source: scopeMapper.map(scope.id)
117117 objectName: scope.id + " loader"
118118
119− readonly property bool previewShown: item.previewShown
120− readonly property bool moving: item.moving
119+ readonly property bool previewShown: item ? item.previewShown : false
120+ readonly property bool moving: item ? item.moving : false
121121
122122 // these are needed for autopilot tests
123123 readonly property string scopeId: scope.id
Dash/DashPreview.qml+1−3
@@ -17,7 +17,7 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919
20−Rectangle {
20+Item {
2121 id: root
2222
2323 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
@@ -36,7 +36,6 @@ Rectangle {
3636 readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
3737 readonly property int contentSpacing: units.gu(3)
3838
39− color: Qt.rgba(0, 0, 0, .3)
4039 clip: true
4140
4241 function ensureVisible(item) {
@@ -61,7 +60,6 @@ Rectangle {
6160 anchors.fill: parent
6261 }
6362
64−
6563 Row {
6664 id: contentRow
6765 anchors {
tests/mocks/QMenuModel/UnityMenuModel.qml → Dash/DashPreviewPlaceholder.qmlrenamed+6−7
@@ -1,9 +1,6 @@
11/*
22 * Copyright (C) 2013 Canonical, Ltd.
33 *
4− * Authors:
5− * Mirco Müller <mirco.mueller@canonical.com>
6− *
74 * This program is free software; you can redistribute it and/or modify
85 * it under the terms of the GNU General Public License as published by
96 * the Free Software Foundation; version 3.
@@ -18,9 +15,11 @@
1815 */
1916
2017import QtQuick 2.0
18+import Ubuntu.Components 0.1
2119
22−QtObject {
23− property variant busName
24− property variant actions
25− property variant menuObjectPath
20+DashPreview {
21+ ActivityIndicator {
22+ anchors.centerIn: parent
23+ running: true
24+ }
2625}
Dash/Generic/GenericCarouselLoader.qmlnew 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+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+
20+Loader {
21+ property var model: null
22+ property url carouselUrl: "GenericCarousel.qml"
23+ property url filtergridUrl: "GenericFilterGrid.qml"
24+
25+ source: model.count > 6 ? carouselUrl : filtergridUrl // FIXME do not hardcode 6, but retrieve correct minimum required count
26+
27+ onLoaded: {
28+ item.model = Qt.binding(function() { return model; })
29+ }
30+}
Dash/Generic/GenericFilterGrid.qml+4−6
@@ -28,8 +28,8 @@ FilterGrid {
2828 property int iconWidth: units.gu(8)
2929 property int iconHeight: units.gu(7.5)
3030
31− signal clicked(int index, var delegateItem, real itemY)
32− signal pressAndHold(int index, var delegateItem, real itemY)
31+ signal clicked(int index, real itemY)
32+ signal pressAndHold(int index, real itemY)
3333
3434 delegate: Tile {
3535 id: tile
@@ -42,13 +42,11 @@ FilterGrid {
4242 source: model.icon
4343
4444 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, tile.y)
4746 }
4847
4948 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, tile.y)
5250 }
5351 }
5452}
Dash/Generic/WeatherFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
3737 style: FlatTileStyle {}
3838
3939 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, tile.y)
4241 }
4342
4443 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, tile.y)
4745 }
4846 }
4947}
Dash/GenericScopeView.qml+229−67
@@ -21,14 +21,14 @@ import "../Components/ListItems" as ListItems
2121
2222ScopeView {
2323 id: scopeView
24− readonly property alias previewShown: previewLoader.onScreen
24+ readonly property alias previewShown: previewListView.onScreen
2525 property bool enableHeightBehaviorOnNextCreation: false
2626
2727 moving: categoryView.moving
2828
2929 onIsCurrentChanged: {
3030 pageHeader.resetSearch();
31− previewLoader.open = false;
31+ previewListView.open = false;
3232 }
3333
3434 onMovementStarted: categoryView.showHeader()
@@ -57,9 +57,8 @@ ScopeView {
5757
5858 Connections {
5959 target: scopeView.scope
60− onShowDash: previewLoader.open = false;
61− onHideDash: previewLoader.open = false;
62− onActivated: previewLoader.closePreviewSpinner();
60+ onShowDash: previewListView.open = false;
61+ onHideDash: previewListView.open = false;
6362 }
6463
6564 ScopeListView {
@@ -67,12 +66,18 @@ ScopeView {
6766 objectName: "categoryListView"
6867 anchors.fill: parent
6968 model: scopeView.categories
70− forceNoClip: previewLoader.onScreen
69+ forceNoClip: previewListView.onScreen
7170
7271 onAtYEndChanged: if (atYEnd) endReached()
7372 onMovingChanged: if (moving && atYEnd) endReached()
7473
7574 property string expandedCategoryId: ""
75+ signal correctExpandedCategory();
76+
77+ Behavior on contentY {
78+ enabled: previewListView.open
79+ UbuntuNumberAnimation {}
80+ }
7681
7782 delegate: ListItems.Base {
7883 id: baseItem
@@ -124,33 +129,50 @@ ScopeView {
124129 Connections {
125130 target: rendererLoader.item
126131 onClicked: {
132+ // Prepare the preview in case activate() triggers a preview only
127133 effect.positionPx = mapToItem(categoryView, 0, itemY).y
128− scopeView.scope.activate(delegateItem.model.uri,
129− delegateItem.model.icon,
130− delegateItem.model.category,
131− 0,
132− delegateItem.model.mimetype,
133− delegateItem.model.title,
134− delegateItem.model.comment,
135− delegateItem.model.dndUri,
136− delegateItem.model.metadata)
134+ previewListView.categoryId = categoryId
135+ previewListView.categoryDelegate = rendererLoader.item
136+ previewListView.model = target.model;
137+ previewListView.init = true;
138+ previewListView.currentIndex = index;
139+
140+ var item = target.model.get(index);
141+
142+ if ((scopeView.scope.id == "applications.scope" && categoryId == "installed")
143+ || (scopeView.scope.id == "home.scope" && categoryId == "applications.scope")) {
144+ scopeView.scope.activate(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
145+ item.comment, item.dndUri, item.metadata)
146+ } else {
147+ previewListView.open = true
148+
149+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
150+ item.comment, item.dndUri, item.metadata)
151+ }
137152 }
138153 onPressAndHold: {
139154 effect.positionPx = mapToItem(categoryView, 0, itemY).y
140− scopeView.scope.preview( delegateItem.model.uri,
141− delegateItem.model.icon,
142− delegateItem.model.category,
143− 0,
144− delegateItem.model.mimetype,
145− delegateItem.model.title,
146− delegateItem.model.comment,
147− delegateItem.model.dndUri,
148− delegateItem.model.metadata)
155+ previewListView.categoryId = categoryId
156+ previewListView.categoryDelegate = rendererLoader.item
157+ previewListView.model = target.model;
158+ previewListView.init = true;
159+ previewListView.currentIndex = index;
160+ previewListView.open = true
161+
162+ var item = target.model.get(index)
163+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
164+ item.comment, item.dndUri, item.metadata)
149165 }
150166 }
151167 Connections {
152168 target: categoryView
153169 onExpandedCategoryIdChanged: {
170+ collapseAllButExpandedCategory();
171+ }
172+ onCorrectExpandedCategory: {
173+ collapseAllButExpandedCategory();
174+ }
175+ function collapseAllButExpandedCategory() {
154176 var item = rendererLoader.item;
155177 if (item.expandable) {
156178 var shouldFilter = categoryId != categoryView.expandedCategoryId;
@@ -158,13 +180,15 @@ ScopeView {
158180 // If the filter animation will be seen start it, otherwise, just flip the switch
159181 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
160182 var growingVisible = !shouldFilter && y + height < categoryView.height;
161− if (shrinkingVisible || growingVisible) {
162− item.startFilterAnimation(shouldFilter)
163− } else {
164− item.filter = shouldFilter;
165− }
166− if (!shouldFilter) {
167− categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
183+ if (!previewListView.open || !shouldFilter) {
184+ if (shrinkingVisible || growingVisible) {
185+ item.startFilterAnimation(shouldFilter)
186+ } else {
187+ item.filter = shouldFilter;
188+ }
189+ if (!shouldFilter && !previewListView.open) {
190+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
191+ }
168192 }
169193 }
170194 }
@@ -238,26 +262,27 @@ ScopeView {
238262 rendererId = getDefaultRendererId(contentType);
239263 }
240264 switch (rendererId) {
265+ case "carousel": {
266+ switch (contentType) {
267+ case "music": return "Music/MusicCarouselLoader.qml";
268+ case "video": return "Video/VideoCarouselLoader.qml";
269+ default: return "Generic/GenericCarouselLoader.qml";
270+ }
271+ }
241272 case "grid": {
242273 switch (contentType) {
243− case "video": return "Video/VideoFilterGrid.qml";
244− case "music": return "Music/MusicFilterGrid.qml";
245274 case "apps": {
246275 if (rendererHint == "toggled")
247276 return "Apps/DashPluginFilterGrid.qml";
248277 else
249278 return "Generic/GenericFilterGrid.qml";
250279 }
280+ case "music": return "Music/MusicFilterGrid.qml";
281+ case "video": return "Video/VideoFilterGrid.qml";
251282 case "weather": return "Generic/WeatherFilterGrid.qml";
252283 default: return "Generic/GenericFilterGrid.qml";
253284 }
254285 }
255− case "carousel": {
256− switch (contentType) {
257− case "music": return "Music/MusicCarousel.qml";
258− default: return "Generic/GenericCarousel.qml";
259− }
260− }
261286 case "special": {
262287 switch (contentType) {
263288 case "apps": return "Apps/RunningApplicationsGrid.qml";
@@ -270,6 +295,7 @@ ScopeView {
270295
271296 OpenEffect {
272297 id: effect
298+ objectName: "openEffect"
273299 anchors {
274300 fill: parent
275301 bottomMargin: -bottomOverflow
@@ -282,29 +308,44 @@ ScopeView {
282308 topOpacity: (1 - gap * 1.2)
283309 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
284310 bottomOverflow: units.gu(20)
285− bottomOpacity: 1 - (gap * 0.8)
311+ live: !expansionAnimation.running
286312
287313 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
288− property real gap: previewLoader.open ? 1.0 : 0.0
314+ property real gap: previewListView.open ? 1.0 : 0.0
289315
290316 Behavior on gap {
291317 NumberAnimation {
318+ id: expansionAnimation
292319 duration: 200
293320 easing.type: Easing.InOutQuad
294321 onRunningChanged: {
295− if (!previewLoader.open && !running) {
296− previewLoader.onScreen = false
322+ if (!previewListView.open && !running) {
323+ previewListView.onScreen = false
297324 }
298325 }
299326 }
300327 }
328+ Behavior on positionPx {
329+ enabled: previewListView.open
330+ UbuntuNumberAnimation {}
331+ }
301332 }
302333
303334 Connections {
304335 target: scopeView.scope
305336 onPreviewReady: {
306− previewLoader.previewData = preview
307− previewLoader.open = true
337+ if (previewListView.init) {
338+ // Preview was triggered because of a click on the item. Need to expand now.
339+ if (!previewListView.open) {
340+ previewListView.open = true
341+ }
342+
343+ var index = previewListView.currentIndex
344+ previewListView.currentIndex = -1
345+ previewListView.currentIndex = index
346+ previewListView.init = false
347+ }
348+ previewListView.currentItem.previewData = preview
308349 }
309350 }
310351
@@ -312,18 +353,9 @@ ScopeView {
312353 id: previewDelegateMapper
313354 }
314355
315− Connections {
316− ignoreUnknownSignals: true
317− target: previewLoader.valid ? previewLoader.item : null
318− onClose: {
319− previewLoader.open = false
320− }
321− }
322−
323− Loader {
324− objectName: "previewLoader"
325− id: previewLoader
326− property var previewData
356+ ListView {
357+ id: previewListView
358+ objectName: "previewListView"
327359 height: effect.bottomGapPx - effect.topGapPx
328360 anchors {
329361 top: parent.top
@@ -331,38 +363,168 @@ ScopeView {
331363 left: parent.left
332364 right: parent.right
333365 }
334− source: onScreen ? previewDelegateMapper.map(previewLoader.previewData.rendererName) : ""
366+ orientation: ListView.Horizontal
367+ highlightRangeMode: ListView.StrictlyEnforceRange
368+ snapMode: ListView.SnapOneItem
369+ boundsBehavior: Flickable.DragAndOvershootBounds
370+ highlightMoveDuration: 250
371+ flickDeceleration: units.gu(625)
372+ maximumFlickVelocity: width * 5
373+ cacheBuffer: 0
374+
375+ // To be set before opening the preview
376+ property string categoryId: ""
377+ property var categoryDelegate
378+
379+ // because the ListView is built asynchronous, setting the
380+ // currentIndex directly won't work. We need to refresh it
381+ // when the first preview is ready to be displayed.
382+ property bool init: true
383+
384+ onCurrentIndexChanged: {
385+ var row = Math.floor(currentIndex / categoryDelegate.columns);
386+ if (categoryDelegate.collapsedRowCount <= row) {
387+ categoryView.expandedCategoryId = categoryId
388+ }
389+
390+ if (open) {
391+ categoryDelegate.highlightIndex = currentIndex
392+ }
393+
394+ if (!init && model !== undefined) {
395+ var item = model.get(currentIndex)
396+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title, item.comment, item.dndUri, item.metadata)
397+ }
398+
399+ var itemY = categoryView.contentItem.mapFromItem(categoryDelegate.currentItem).y;
400+ var newContentY = itemY - effect.positionPx - categoryDelegate.verticalSpacing;
401+ var effectAdjust = effect.positionPx;
402+ if (newContentY < 0) {
403+ effectAdjust += newContentY;
404+ newContentY = 0;
405+ }
406+ if (newContentY > Math.max(0, categoryView.contentHeight - categoryView.height)) {
407+ effectAdjust += -(categoryView.contentHeight - categoryView.height) + newContentY
408+ newContentY = categoryView.contentHeight - categoryView.height;
409+ }
410+
411+ effect.positionPx = effectAdjust;
412+ categoryView.contentY = newContentY - categoryView.originY;
413+ }
335414
336415 property bool open: false
337416 property bool onScreen: false
338− property bool valid: item !== null
339417
340418 onOpenChanged: {
341419 if (open) {
342− onScreen = true
420+ onScreen = true;
421+ categoryDelegate.highlightIndex = currentIndex;
422+ pageHeader.unfocus();
423+ } else {
424+ // Cancel any pending preview requests or actions
425+ if (previewListView.currentItem.previewData !== undefined) {
426+ previewListView.currentItem.previewData.cancelAction();
427+ }
428+ scopeView.scope.cancelActivation();
429+ model = undefined;
430+ categoryView.correctExpandedCategory();
431+ categoryDelegate.highlightIndex = -1;
343432 }
344433 }
345434
346− onLoaded: {
347− item.previewData = Qt.binding(function() { return previewLoader.previewData })
435+ Rectangle {
436+ anchors.fill: parent
437+ color: Qt.rgba(0, 0, 0, .3)
438+ z: -1
439+ }
440+
441+ delegate: Loader {
442+ id: previewLoader
443+ objectName: "previewLoader" + index
444+ height: previewListView.height
445+ width: previewListView.width
446+ asynchronous: true
447+ source: previewListView.onScreen ?
448+ (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
449+
450+ onPreviewDataChanged: {
451+ if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
452+ previewLoader.opacity = 0;
453+ }
454+ }
455+
456+ onSourceChanged: {
457+ if (previewData !== undefined) {
458+ fadeIn.start()
459+ }
460+ }
461+
462+ PropertyAnimation {
463+ id: fadeIn
464+ target: previewLoader
465+ property: "opacity"
466+ from: 0.0
467+ to: 1.0
468+ duration: UbuntuAnimation.BriskDuration
469+ }
470+
471+ property var previewData
472+ property bool valid: item !== null
473+
474+ onLoaded: {
475+ if (previewListView.onScreen && previewData !== undefined) {
476+ item.previewData = Qt.binding(function() { return previewData })
477+ }
478+ }
479+
480+ Connections {
481+ ignoreUnknownSignals: true
482+ target: item
483+ onClose: {
484+ previewListView.open = false
485+ }
486+ }
487+
488+ function closePreviewSpinner() {
489+ if(item) {
490+ item.showProcessingAction = false;
491+ }
492+ }
348493 }
494+ }
495+
496+ Image {
497+ objectName: "pointerArrow"
498+ anchors {
499+ top: previewListView.bottom
500+ left: parent.left
501+ leftMargin: previewListView.categoryDelegate !== undefined && previewListView.categoryDelegate.currentItem ?
502+ previewListView.categoryDelegate.currentItem.center + (-width + margins) / 2 : 0
349503
350− function closePreviewSpinner() {
351− if(item) {
352− item.showProcessingAction = false;
504+ Behavior on leftMargin {
505+ SmoothedAnimation {
506+ duration: UbuntuAnimation.FastDuration
507+ }
353508 }
354509 }
510+ height: units.gu(1)
511+ width: units.gu(2)
512+ property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
513+ opacity: previewListView.open ? .5 : 0
514+
515+ source: "graphics/tooltip_arrow.png"
355516 }
356517
357518 // TODO: Move as InverseMouseArea to DashPreview
358519 MouseArea {
359− enabled: previewLoader.onScreen
520+ objectName: "closePreviewMouseArea"
521+ enabled: previewListView.onScreen
360522 anchors {
361523 fill: parent
362524 topMargin: effect.bottomGapPx
363525 }
364526 onClicked: {
365− previewLoader.open = false;
527+ previewListView.open = false;
366528 }
367529 }
368530}
Dash/Music/AlbumTile.qmldeleted−116
@@ -1,116 +0,0 @@
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−AbstractButton {
21− id: root
22− property url source
23− property alias album: albumLabel.text
24− property alias artist: artistLabel.text
25− property alias iconWidth: icon.width
26− property alias iconHeight: icon.height
27−
28− UbuntuShape {
29− id: icon
30− anchors {
31− top: parent.top
32− horizontalCenter: parent.horizontalCenter
33− }
34− radius: "medium"
35− image: Image {
36− source: root.source
37− sourceSize { width: icon.width; height: icon.height }
38− asynchronous: true
39− cache: false
40− }
41− }
42−
43− UbuntuShape {
44− id: borderPressed
45−
46− anchors.fill: icon
47− radius: "medium"
48− borderSource: "radius_pressed.sci"
49− opacity: root.pressed ? 1.0 : 0.0
50− Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
51− }
52−
53− Item {
54− id: artistContainer
55− anchors {
56− left: parent.left
57− right: parent.right
58− top: icon.bottom
59− }
60− height: units.gu(2)
61−
62− Label {
63− id: artistLabel
64− anchors {
65− baseline: parent.bottom
66− left: parent.left
67− right: parent.right
68− leftMargin: units.gu(1)
69− rightMargin: units.gu(1)
70− }
71−
72− color: Theme.palette.selected.backgroundText
73− opacity: 0.9
74− style: Text.Raised
75− styleColor: "black"
76− fontSize: "small"
77− elide: Text.ElideMiddle
78− horizontalAlignment: Text.AlignHCenter
79− }
80− }
81−
82− Item {
83− anchors {
84− left: parent.left
85− right: parent.right
86− top: artistContainer.bottom
87− }
88− height: units.gu(1.5)
89−
90− Label {
91− id: albumLabel
92− anchors {
93− baseline: parent.bottom
94− left: parent.left
95− right: parent.right
96− leftMargin: units.gu(1)
97− rightMargin: units.gu(1)
98− }
99−
100− color: Theme.palette.selected.backgroundText
101− opacity: 0.9
102− style: Text.Raised
103− styleColor: "black"
104− fontSize: "small"
105− font.weight: Font.Light
106− elide: Text.ElideMiddle
107− horizontalAlignment: Text.AlignHCenter
108− }
109− }
110−
111− GridView.onRemove: SequentialAnimation {
112− PropertyAction { target: root; property: "GridView.delayRemove"; value: true }
113− NumberAnimation { target: root; property: "opacity"; from: 1; to: 0; duration: 200; easing.type: Easing.InOutQuad }
114− PropertyAction { target: root; property: "GridView.delayRemove"; value: false }
115− }
116−}
Launcher/QuicklistForegroundStyle.qml → Dash/Music/MusicCarouselLoader.qmlrenamed+6−5
@@ -14,10 +14,11 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import Ubuntu.Components.Themes.Ambiance 0.1
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import "../Generic"
1820
19−PopoverForegroundStyle {
20− bubbleColor: "#221e1c"
21− bubbleOpacity: 0.9
22− arrowSource: "graphics/quicklist_tooltip.png"
21+GenericCarouselLoader {
22+ carouselUrl: "MusicCarousel.qml"
23+ filtergridUrl: "MusicFilterGrid.qml"
2324}
Dash/Music/MusicFilterGrid.qml+2−4
@@ -44,13 +44,11 @@ GenericFilterGrid {
4444 }
4545
4646 onClicked: {
47− var data = { model: model }
48− filtergrid.clicked(index, data, tile.y)
47+ filtergrid.clicked(index, tile.y)
4948 }
5049
5150 onPressAndHold: {
52− var data = { model: model }
53− filtergrid.pressAndHold(index, data, tile.y)
51+ filtergrid.pressAndHold(index, tile.y)
5452 }
5553 }
5654}
Dash/SearchableResultModel.qml+4
@@ -25,6 +25,10 @@ SortFilterProxyModel {
2525
2626 filterRole: CategoryResults.RoleTitle
2727
28+ function get(index) {
29+ return model.get(mapToSource(index))
30+ }
31+
2832 onSearchQueryChanged: {
2933 if (searchQuery.length == 0) {
3034 filterRegExp = RegExp("");
Dash/Video/VideosCarousel.qml → Dash/Video/VideoCarousel.qmlrenamed

The content is the same.

Components/Math.js → Dash/Video/VideoCarouselLoader.qmlrenamed+6−8
@@ -14,13 +14,11 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−.pragma library
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import "../Generic"
1820
19−function clamp(value, min, max) {
20− if (min <= max) {
21− return Math.max(min, Math.min(max, value))
22− } else {
23− // swap min/max if min > max
24− return clamp(value, max, min)
25− }
21+GenericCarouselLoader {
22+ carouselUrl: "VideoCarousel.qml"
23+ filtergridUrl: "VideoFilterGrid.qml"
2624}
Dash/Video/VideoFilterGrid.qml+2−4
@@ -42,13 +42,11 @@ GenericFilterGrid {
4242 style: VideoTileStyle {}
4343
4444 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, tile.y)
4746 }
4847
4948 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, tile.y)
5250 }
5351 }
5452}
Dash/graphics/tooltip_arrow@27.pngnew file

Binary file not shown.

Greeter/Greeter.qml+3−3
@@ -19,7 +19,6 @@ import Ubuntu.Components 0.1
1919import Ubuntu.Gestures 0.1
2020import LightDM 0.1 as LightDM
2121import "../Components"
22−import "../Components/Math.js" as MathLocal
2322
2423Showable {
2524 id: greeter
@@ -29,7 +28,7 @@ Showable {
2928 property url defaultBackground
3029
3130 // 1 when fully shown and 0 when fully hidden
32− property real showProgress: MathLocal.clamp((width + x) / width, 0, 1)
31+ property real showProgress: MathUtils.clamp((width + x) / width, 0, 1)
3332
3433 showAnimation: StandardAnimation { property: "x"; to: 0 }
3534 hideAnimation: StandardAnimation { property: "x"; to: -width }
@@ -52,7 +51,7 @@ Showable {
5251 signal unlocked(int uid)
5352
5453 onRightTeaserPressedChanged: {
55− if (rightTeaserPressed && !locked) {
54+ if (rightTeaserPressed && !locked && x == 0) {
5655 teasingTimer.start();
5756 }
5857 }
@@ -64,6 +63,7 @@ Showable {
6463 property var model: LightDM.Users
6564 property int currentIndex: 0
6665 property var infographicModel: LightDM.Infographic
66+ readonly property int backgroundTopMargin: -greeter.y
6767
6868 source: required ? "GreeterContent.qml" : ""
6969
Greeter/GreeterContent.qml+23−20
@@ -24,7 +24,7 @@ MouseArea {
2424 id: root
2525 anchors.fill: parent
2626
27− property bool ready: wallpaper.source == "" || wallpaper.status == Image.Ready || wallpaper.status == Image.Error
27+ property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
2828 property bool leftTeaserPressed: teasingMouseArea.pressed &&
2929 teasingMouseArea.mouseX < teasingMouseArea.width / 2
3030 property bool rightTeaserPressed: teasingMouseArea.pressed &&
@@ -34,33 +34,36 @@ MouseArea {
3434 signal unlocked(int uid)
3535
3636 Rectangle {
37− // In case wallpaper fails to load
38− id: wallpaperBackup
37+ // In case background fails to load
38+ id: backgroundBackup
3939 anchors.fill: parent
4040 color: "black"
4141 }
4242
4343 property url backgroundValue: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : greeter.defaultBackground
44− onBackgroundValueChanged: wallpaper.source = backgroundValue
44+ onBackgroundValueChanged: background.source = backgroundValue
4545
4646 CrossFadeImage {
47− id: wallpaper
48− objectName: "wallpaper"
49− anchors.fill: parent
47+ id: background
48+ objectName: "greeterBackground"
49+ anchors {
50+ fill: parent
51+ topMargin: backgroundTopMargin
52+ }
5053 fillMode: Image.PreserveAspectCrop
5154 }
5255
5356 // See Shell.qml's backgroundSettings treatment for why we need a separate
5457 // Image, but it boils down to avoiding binding loop detection.
5558 Image {
56− source: wallpaper.source
59+ source: background.source
5760 height: 0
5861 width: 0
5962 sourceSize.height: 0
6063 sourceSize.width: 0
6164 onStatusChanged: {
6265 if (status == Image.Error && source != greeter.defaultBackground) {
63− wallpaper.source = greeter.defaultBackground
66+ background.source = greeter.defaultBackground
6467 }
6568 }
6669 }
@@ -71,17 +74,6 @@ MouseArea {
7174 opacity: 0.4
7275 }
7376
74− Clock {
75− id: clock
76− visible: narrowMode
77−
78− anchors {
79− top: parent.top
80− topMargin: units.gu(2)
81− horizontalCenter: parent.horizontalCenter
82− }
83− }
84−
8577 MouseArea {
8678 id: teasingMouseArea
8779 anchors.fill: parent
@@ -160,4 +152,15 @@ MouseArea {
160152 right: root.right
161153 }
162154 }
155+
156+ Clock {
157+ id: clock
158+ visible: narrowMode
159+
160+ anchors {
161+ top: parent.top
162+ topMargin: units.gu(2)
163+ horizontalCenter: parent.horizontalCenter
164+ }
165+ }
163166}
Greeter/Lockscreen.qml+8−4
@@ -57,15 +57,19 @@ Showable {
5757 }
5858
5959 Rectangle {
60− // In case wallpaper fails to load or is undefined
61− id: wallpaperBackup
60+ // In case background fails to load or is undefined
61+ id: backgroundBackup
6262 anchors.fill: parent
6363 color: "black"
6464 }
6565
6666 Image {
67− id: wallpaper
68− anchors.fill: parent
67+ id: backgroundImage
68+ objectName: "lockscreenBackground"
69+ anchors {
70+ fill: parent
71+ topMargin: backgroundTopMargin
72+ }
6973 source: root.required ? root.background : ""
7074 fillMode: Image.PreserveAspectCrop
7175 }
Greeter/PinLockscreen.qml+34−4
@@ -27,7 +27,7 @@ Column {
2727 property alias placeholderText: pinentryField.placeholderText
2828 property int padWidth: units.gu(34)
2929 property int padHeight: units.gu(28)
30− property int pinLength: 4
30+ property int pinLength: -1
3131
3232 signal entered(string passphrase)
3333 signal cancel()
@@ -65,6 +65,9 @@ Column {
6565 Label {
6666 id: pinentryFieldLabel
6767 anchors.centerIn: parent
68+ width: parent.width - (backspaceIcon.width + backspaceIcon.anchors.rightMargin) * 2
69+ elide: Text.ElideMiddle
70+ horizontalAlignment: Text.AlignHCenter
6871 font.pixelSize: units.dp(44)
6972 color: "#f3f3e7"
7073 opacity: 0.6
@@ -76,6 +79,26 @@ Column {
7679 text: parent.placeholderText
7780 visible: pinentryFieldLabel.text.length == 0
7881 }
82+
83+ Icon {
84+ id: backspaceIcon
85+ objectName: "backspaceIcon"
86+ anchors {
87+ top: parent.top
88+ topMargin: units.gu(1)
89+ right: parent.right
90+ rightMargin: units.gu(2)
91+ bottom: parent.bottom
92+ bottomMargin: units.gu(1)
93+ }
94+ visible: root.pinLength == -1
95+ width: height
96+ name: "erase"
97+ MouseArea {
98+ anchors.fill: parent
99+ onClicked: pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
100+ }
101+ }
79102 }
80103
81104 UbuntuShape {
@@ -154,7 +177,7 @@ Column {
154177 objectName: "pinPadButtonBack"
155178 width: root.padWidth / 3
156179 height: root.padHeight / 4
157− iconName: "back"
180+ subText: "CANCEL"
158181 onClicked: root.cancel();
159182 }
160183
@@ -171,8 +194,15 @@ Column {
171194 objectName: "pinPadButtonErase"
172195 width: root.padWidth / 3
173196 height: root.padHeight / 4
174− iconName: "erase"
175− onClicked: pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1)
197+ iconName: root.pinLength == -1 ? "" : "erase"
198+ subText: root.pinLength == -1 ? "DONE" : ""
199+ onClicked: {
200+ if (root.pinLength !== -1) {
201+ pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
202+ } else {
203+ root.entered(pinentryField.text);
204+ }
205+ }
176206 enabled: entryEnabled
177207 }
178208 }
Launcher/Launcher.qml+2−2
@@ -28,8 +28,8 @@ Item {
2828
2929 property int panelWidth: units.gu(8)
3030 property int dragAreaWidth: units.gu(1)
31− property int minimizeDistance: panelWidth * 2.5
32− property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
31+ property int minimizeDistance: units.gu(26)
32+ property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
3333 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
3434
3535 readonly property bool shown: panel.x > -panel.width
Launcher/LauncherPanel.qml+87−46
@@ -21,6 +21,7 @@ import Unity 0.1
2121import Unity.Launcher 0.1
2222import Ubuntu.Components.Popups 0.1
2323import "../Components/ListItems"
24+import "../Components/"
2425
2526Item {
2627 id: root
@@ -31,7 +32,7 @@ Item {
3132 property bool inverted: true
3233 property bool dragging: false
3334 property bool moving: launcherListView.moving || launcherListView.flicking
34− property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
35+ property bool preventHiding: moving || dndArea.draggedIndex >= 0 || quickList.state === "open" || dndArea.pressed
3536 property int highlightIndex: -1
3637
3738 signal applicationSelected(string appId)
@@ -278,7 +279,6 @@ Item {
278279 property bool postDragging: false
279280 property int startX
280281 property int startY
281− property var quickListPopover: null
282282
283283 onPressed: {
284284 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
@@ -357,11 +357,10 @@ Item {
357357 draggedIndex = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
358358
359359 // Opening QuickList
360− var quickListModel = launcherListView.model.get(draggedIndex).quickList
361− var quickListAppId = launcherListView.model.get(draggedIndex).appId
362−
363− quickListPopover = PopupUtils.open(popoverComponent, selectedItem,
364− {model: quickListModel, appId: quickListAppId})
360+ quickList.item = selectedItem;
361+ quickList.model = launcherListView.model.get(draggedIndex).quickList;
362+ quickList.appId = launcherListView.model.get(draggedIndex).appId;
363+ quickList.state = "open";
365364
366365 launcherListView.interactive = false
367366
@@ -387,7 +386,7 @@ Item {
387386 var distance = Math.max(Math.abs(mouseX - startX), Math.abs(mouseY - startY))
388387 if (!preDragging && distance > units.gu(1.5)) {
389388 preDragging = true;
390− PopupUtils.close(quickListPopover)
389+ quickList.state = "";
391390 }
392391 if (distance > launcherListView.itemHeight) {
393392 selectedItem.dragging = true
@@ -482,53 +481,95 @@ Item {
482481 }
483482 }
484483
485− Component {
486− id: popoverComponent
484+ UbuntuShapeForItem {
485+ id: quickListShape
486+ objectName: "quickListShape"
487+ anchors.fill: quickList
488+ opacity: quickList.state === "open" ? 0.8 : 0
489+ visible: opacity > 0
490+ rotation: root.rotation
491+
492+ Behavior on opacity {
493+ UbuntuNumberAnimation {}
494+ }
495+
496+ image: quickList
497+
498+ Image {
499+ anchors {
500+ right: parent.left
501+ rightMargin: -units.dp(4)
502+ verticalCenter: parent.verticalCenter
503+ verticalCenterOffset: -quickList.offset
504+ }
505+ height: units.gu(1)
506+ width: units.gu(2)
507+ source: "graphics/quicklist_tooltip.png"
508+ rotation: 90
509+ }
510+
511+ InverseMouseArea {
512+ anchors.fill: parent
513+ enabled: quickList.state == "open"
514+ onClicked: {
515+ quickList.state = ""
516+ }
517+ }
518+
519+ }
520+
521+ Rectangle {
522+ id: quickList
523+ objectName: "quickList"
524+ color: "#221e1c"
525+ width: units.gu(30)
526+ height: quickListColumn.height
527+ anchors {
528+ left: root.inverted ? undefined : parent.right
529+ right: root.inverted ? parent.left : undefined
530+ margins: units.gu(1)
487531
488− Popover {
489− id: popover
490− property var model
491− property string appId
492− contentWidth: quickListColumn.width
493− foregroundStyle: QuicklistForegroundStyle {}
532+ }
533+ y: itemCenter - (height / 2) + offset
534+ rotation: root.rotation
494535
495− // FIXME: There's a bug in the Popover positioning that it covers the item in case it is rotated.
496− // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1204470
497− // For now, let's move the Popover around with callerMargin.
498− // Remove popupMargin once the bug is fixed.
499− property int popupMargin: root.inverted ? launcherListView.itemHeight : 0;
536+ property var model
537+ property string appId
538+ property var item
500539
501− callerMargin: units.gu(1) + popupMargin
540+ // internal
541+ property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
542+ property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
543+ itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
502544
503− Column {
504− id: quickListColumn
505− width: units.gu(30)
506− height: childrenRect.height
545+ Column {
546+ id: quickListColumn
547+ width: parent.width
548+ height: childrenRect.height
507549
508− Repeater {
509− id: popoverRepeater
510− model: popover.model
550+ Repeater {
551+ id: popoverRepeater
552+ model: quickList.model
511553
512− ListItems.Standard {
513− objectName: "quickListEntry" + index
514− text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
515− highlightWhenPressed: model.clickable
554+ ListItems.Standard {
555+ objectName: "quickListEntry" + index
556+ text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
557+ highlightWhenPressed: model.clickable
516558
517− // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
518− // ListItems don't know that they are sitting in a themed Popover where the color
519− // needs to be inverted.
520− __foregroundColor: Theme.palette.selected.backgroundText
559+ // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
560+ // ListItems don't know that they are sitting in a themed Popover where the color
561+ // needs to be inverted.
562+ __foregroundColor: Theme.palette.selected.backgroundText
521563
522− onClicked: {
523− if (!model.clickable) {
524− return;
525− }
526− PopupUtils.close(popover);
527− // Unsetting model to prevent showing changing entries during fading out
528− // that may happen because of triggering an action.
529− LauncherModel.quickListActionInvoked(appId, index);
530− popoverRepeater.model = undefined;
564+ onClicked: {
565+ if (!model.clickable) {
566+ return;
531567 }
568+ quickList.state = "";
569+ // Unsetting model to prevent showing changing entries during fading out
570+ // that may happen because of triggering an action.
571+ LauncherModel.quickListActionInvoked(quickList.appId, index);
572+ quickList.model = undefined;
532573 }
533574 }
534575 }
Notifications/NotificationMenuItemFactory.qml+20−6
@@ -54,7 +54,8 @@ Loader {
5454
5555 Component.onCompleted: {
5656 menuModel.loadExtendedAttributes(menuIndex, {"x-echo-mode-password": "bool"});
57− textfield.echoMode = menuData.ext.xEchoModePassword ? TextInput.Password : TextInput.Normal
57+ checkBox.checked = menuData.ext.xEchoModePassword ? false : true
58+ checkBoxRow.visible = menuData.ext.xEchoModePassword
5859 }
5960
6061 Label {
@@ -65,11 +66,29 @@ Loader {
6566 id: textfield
6667
6768 anchors.left: parent.left; anchors.right: parent.right
69+ echoMode: checkBox.checked ? TextInput.Normal : TextInput.Password
6870 height: units.gu(5)
6971 onTextChanged: {
7072 menuModel.changeState(menuIndex, text);
7173 }
7274 }
75+
76+ Row {
77+ id: checkBoxRow
78+
79+ spacing: units.gu(.5)
80+
81+ CheckBox {
82+ id: checkBox
83+
84+ checked: false
85+ }
86+
87+ Label {
88+ anchors.verticalCenter: checkBox.verticalCenter
89+ text: i18n.tr("Show password")
90+ }
91+ }
7392 }
7493 }
7594
@@ -78,11 +97,6 @@ Loader {
7897 PinLockscreen {
7998 anchors.left: parent.left; anchors.right: parent.right
8099
81− Component.onCompleted: {
82− menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-pin-length': 'int'});
83− pinLength = menuData.ext.xCanonicalPinLength;
84− }
85−
86100 onEntered: {
87101 menuModel.changeState(menuIndex, passphrase);
88102 entryEnabled = false;
Panel/Indicators.qml+1−2
@@ -21,7 +21,6 @@ import Unity.Indicators 0.1 as Indicators
2121
2222import "../Components"
2323import "../Components/ListItems"
24−import "../Components/Math.js" as MathLocal
2524
2625Showable {
2726 id: indicators
@@ -100,7 +99,7 @@ Showable {
10099 Otherwise it contains the bar's location as a fraction of the distance between hintValue (is 0) and lockThreshold (is 1).
101100 */
102101 var verticalProgress =
103− MathLocal.clamp((indicators.height - handle.height - hintValue) /
102+ MathUtils.clamp((indicators.height - handle.height - hintValue) /
104103 (lockThreshold - hintValue), 0, 1);
105104
106105 /*
Panel/Indicators/DefaultIndicatorWidget.qml+32−10
@@ -24,7 +24,7 @@ import Unity.Indicators 0.1 as Indicators
2424Indicators.IndicatorWidget {
2525 id: indicatorWidget
2626
27− width: itemRow.width + units.gu(0.5)
27+ width: itemRow.width
2828
2929 property alias leftLabel: itemLeftLabel.text
3030 property alias rightLabel: itemRightLabel.text
@@ -32,16 +32,17 @@ Indicators.IndicatorWidget {
3232
3333 Row {
3434 id: itemRow
35+ width: childrenRect.width
3536 objectName: "itemRow"
3637 anchors {
3738 top: parent.top
3839 bottom: parent.bottom
3940 horizontalCenter: parent.horizontalCenter
4041 }
41− spacing: units.gu(0.5)
4242
4343 Label {
4444 id: itemLeftLabel
45+ width: guRoundUp(implicitWidth)
4546 objectName: "leftLabel"
4647 color: Theme.palette.selected.backgroundText
4748 opacity: 0.8
@@ -57,25 +58,35 @@ Indicators.IndicatorWidget {
5758 top: parent.top
5859 bottom: parent.bottom
5960 }
60− spacing: units.gu(0.5)
6161
6262 Repeater {
6363 model: indicatorWidget.icons
6464
65− Image {
66− id: itemImage
67− objectName: "itemImage"
68− visible: source != ""
69− source: modelData
65+ Item {
66+ width: guRoundUp(itemImage.width)
7067 height: indicatorWidget.iconSize
71− width: indicatorWidget.iconSize
72− anchors.verticalCenter: parent.verticalCenter
68+
69+ Image {
70+ id: itemImage
71+ objectName: "itemImage"
72+ visible: source != ""
73+ source: modelData
74+ height: parent.height
75+ anchors.horizontalCenter: parent.horizontalCenter
76+ fillMode: Image.PreserveAspectFit
77+
78+ sourceSize {
79+ width: indicatorWidget.iconSize
80+ height: indicatorWidget.iconSize
81+ }
82+ }
7383 }
7484 }
7585 }
7686
7787 Label {
7888 id: itemRightLabel
89+ width: guRoundUp(implicitWidth)
7990 objectName: "rightLabel"
8091 color: Theme.palette.selected.backgroundText
8192 opacity: 0.8
@@ -86,6 +97,17 @@ Indicators.IndicatorWidget {
8697 }
8798 }
8899
100+ // TODO: Use toolkit function https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1242575
101+ function guRoundUp(width) {
102+ if (width == 0) {
103+ return 0;
104+ }
105+ var gu1 = units.gu(1.0);
106+ var mod = (width % gu1);
107+
108+ return mod == 0 ? width : width + (gu1 - mod);
109+ }
110+
89111 onRootActionStateChanged: {
90112 if (rootActionState == undefined) {
91113 leftLabel = "";
Shell.qml+9−3
@@ -30,7 +30,6 @@ import "Launcher"
3030import "Panel"
3131import "Hud"
3232import "Components"
33−import "Components/Math.js" as MathLocal
3433import "Bottombar"
3534import "SideStage"
3635import "Notifications"
@@ -237,7 +236,7 @@ FocusScope {
237236 Behavior on x {SmoothedAnimation{velocity: 600}}
238237
239238 property real showProgress:
240− MathLocal.clamp(1 - (x + stages.x) / shell.width, 0, 1)
239+ MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1)
241240
242241 Showable {
243242 id: stages
@@ -402,6 +401,10 @@ FocusScope {
402401
403402 Lockscreen {
404403 id: lockscreen
404+ objectName: "lockscreen"
405+
406+ readonly property int backgroundTopMargin: -panel.panelHeight
407+
405408 hides: [launcher, panel.indicators, hud]
406409 shown: false
407410 enabled: true
@@ -658,19 +661,22 @@ FocusScope {
658661 Launcher {
659662 id: launcher
660663
664+ readonly property bool dashSwipe: progress > 0
665+
661666 anchors.top: parent.top
662667 anchors.bottom: parent.bottom
663668 width: parent.width
664669 dragAreaWidth: shell.edgeSize
665670 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
671+
666672 onDashItemSelected: showHome()
667673 onDash: {
668674 if (stages.shown) {
669− dash.setCurrentScope("applications.scope", true /* animate */, true /* reset */)
670675 stages.hide();
671676 launcher.hide();
672677 }
673678 }
679+ onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("applications.scope", false, true)
674680 onLauncherApplicationSelected:{
675681 greeter.hide()
676682 shell.activateApplication(appId)
data/unity8.conf+20
@@ -6,4 +6,24 @@ stop on desktop-end
66
77respawn
88
9+pre-start script
10+ if [ -n "$MIR_SOCKET" ]; then
11+ if [ -z "$UNITY_MIR_SOCKET" ]; then
12+ # Save original value of MIR_SOCKET in case we are restarted,
13+ # as we modify the variable for future jobs, including ourself.
14+ UNITY_MIR_SOCKET=$MIR_SOCKET
15+ initctl set-env --global UNITY_MIR_SOCKET=$UNITY_MIR_SOCKET
16+ fi
17+
18+ # Point unity8 at unity-system-compositor
19+ MIR_SERVER_FILE=$XDG_RUNTIME_DIR/mir_socket
20+ initctl set-env MIR_SERVER_FILE=$MIR_SERVER_FILE
21+ initctl set-env MIR_SERVER_HOST_SOCKET=$UNITY_MIR_SOCKET
22+
23+ # Point all future jobs in this session to our Mir socket instead of
24+ # unity-system-compositor's socket.
25+ initctl set-env --global MIR_SOCKET=$MIR_SERVER_FILE
26+ fi
27+end script
28+
929exec ${BINARY:-unity8} $ARGS
debian/changelog+102
@@ -1,3 +1,105 @@
1+unity8 (7.83+14.04.20131106-0ubuntu1) trusty; urgency=low
2+
3+ [ Andrea Cimitan ]
4+ * Place ShaderEffectSource of UbuntuShapeForItem under the Shape Item.
5+ (LP: #1239317)
6+
7+ [ Omer Akram ]
8+ * test: make sure the search indicator hides when an app has focus.
9+
10+ [ Nick Dedekind ]
11+ * Fixed indicator slider menu item alignment to label field and icon.
12+ (LP: #1240756)
13+
14+ [ chris.gagnon ]
15+ * Update to autopilot 1.4.
16+
17+ [ Ubuntu daily release ]
18+ * Automatic snapshot from revision 500
19+
20+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 06 Nov 2013 03:37:02 +0000
21+
22+unity8 (7.83+14.04.20131105.1-0ubuntu1) trusty; urgency=low
23+
24+ [ Michał Sawicz ]
25+ * Implement switching between previews by swiping. (LP: #1220651,
26+ #1088572)
27+
28+ [ Nick Dedekind ]
29+ * Faster loading of indicator menus. (LP: #1226650)
30+
31+ [ Albert Astals ]
32+ * Make sure we have ssh started when we need it .
33+ * Fix a few warnings in DashContent.qml on shutdown
34+ Dash/DashContent.qml:119: TypeError: Cannot read property
35+ 'previewShown' of null Dash/DashContent.qml:120: TypeError: Cannot
36+ read property 'moving' of null .
37+ * Don't use deprecated Panel methods.
38+
39+ [ Christopher Lee ]
40+ * Adds an easy to consume function that attempts to unlock the unity
41+ greeter. (LP: #1240261)
42+
43+ [ Lars Uebernickel ]
44+ * Indicators: add TimeFormatter and use it in the messaging menu This
45+ component can be used to turn a timestamp and a format string into a
46+ string with the corresponding time and format. The string will
47+ change whenever the timezone changes. (LP: #1236413)
48+
49+ [ Andrea Cimitan ]
50+ * Allow drag over bounds, but not overshoot. (LP: #1204300)
51+ * Threshold to activate the left edge swipe to reveal dash is now 26
52+ GU. (LP: #1236286)
53+
54+ [ Michael Zanetti ]
55+ * replace the launcher quicklist's Popover with an own quicklist
56+ implementation The Popover probably won't ever support what the
57+ launcher needs.
58+ * Implement switching between previews by swiping. (LP: #1220651,
59+ #1088572)
60+ * don't trigger the greeter teasing during a movement of the greeter.
61+
62+ [ Ubuntu daily release ]
63+ * Automatic snapshot from revision 495
64+
65+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 05 Nov 2013 12:22:04 +0000
66+
67+unity8 (7.83+14.04.20131031-0ubuntu1) trusty; urgency=low
68+
69+ [ Michał Sawicz ]
70+ * Use setenv as early as possible to avoid setenv and getenv clashing
71+ in multi-threaded situations. (LP: #1240866)
72+
73+ [ Ubuntu daily release ]
74+ * Automatic snapshot from revision 482
75+
76+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 31 Oct 2013 20:10:56 +0000
77+
78+unity8 (7.83+14.04.20131028.1-0ubuntu1) trusty; urgency=low
79+
80+ * New rebuild forced
81+ * Automatic snapshot from revision 480
82+
83+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 28 Oct 2013 22:42:07 +0000
84+
85+unity8 (7.83+14.04.20131028-0ubuntu1) trusty; urgency=low
86+
87+ [ Andrea Cimitan ]
88+ * Fix 1195349 by counting drawbuffer on the newContentX logic of the
89+ carousel When we changed carousel from repeater to listview, we
90+ added drawbuffer. this breaks the logic of newContentX, which was
91+ considered disabled and set to -1. The correct disabled value now
92+ has to take into account the drawbuffer. (LP: #1195349)
93+
94+ [ Christopher Lee ]
95+ * Checks both then env and upstart env for the currently set
96+ XDG_DATA_DIRS so they can be set correctly for the test.
97+
98+ [ Ubuntu daily release ]
99+ * Automatic snapshot from revision 479
100+
101+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 28 Oct 2013 03:08:59 +0000
102+
1103unity8 (7.83+13.10.20131016.2-0ubuntu1) saucy; urgency=low
2104
3105 [ Loïc Minier ]
debian/control+1−1
@@ -91,7 +91,7 @@ Description: Unity 8 shell
9191Package: unity8-autopilot
9292Architecture: all
9393Depends: gir1.2-glib-2.0,
94− libautopilot-qt,
94+ libautopilot-qt (>= 1.4),
9595 libqt5test5,
9696 libqt5widgets5,
9797 python-evdev,
main.cpp+8−8
@@ -78,14 +78,6 @@ void resolveIconTheme() {
7878
7979int startShell(int argc, const char** argv, void* server)
8080{
81− /* Workaround Qt platform integration plugin not advertising itself
82− as having the following capabilities:
83− - QPlatformIntegration::ThreadedOpenGL
84− - QPlatformIntegration::BufferQueueingOpenGL
85− */
86− setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
87− setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
88−
8981 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
9082
9183 QGuiApplication::setApplicationName("Unity 8");
@@ -186,6 +178,14 @@ int startShell(int argc, const char** argv, void* server)
186178
187179int main(int argc, const char *argv[])
188180{
181+ /* Workaround Qt platform integration plugin not advertising itself
182+ as having the following capabilities:
183+ - QPlatformIntegration::ThreadedOpenGL
184+ - QPlatformIntegration::BufferQueueingOpenGL
185+ */
186+ setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
187+ setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
188+
189189 // For ubuntumirserver/ubuntumirclient
190190 if (qgetenv("QT_QPA_PLATFORM").startsWith("ubuntumir")) {
191191 setenv("QT_QPA_PLATFORM", "ubuntumirserver", 1);
plugins/ListViewWithPageHeader/listviewwithpageheader.cpp+4−1
@@ -155,6 +155,7 @@ ListViewWithPageHeader::ListViewWithPageHeader()
155155 , m_topSectionItem(nullptr)
156156 , m_forceNoClip(false)
157157 , m_inLayout(false)
158+ , m_inContentHeightKeepHeaderShown(false)
158159{
159160 m_clipItem = new QQuickItem(contentItem());
160161// m_clipItem = new QQuickRectangle(contentItem());
@@ -494,7 +495,7 @@ void ListViewWithPageHeader::viewportMoved(Qt::Orientations orient)
494495 if (!scrolledUp && contentY() == -m_minYExtent) {
495496 m_headerItemShownHeight = 0;
496497 m_headerItem->setY(contentY());
497− } else if ((scrolledUp && notRebounding && notShownByItsOwn && !maximizeVisibleAreaRunning) || (m_headerItemShownHeight > 0)) {
498+ } else if ((scrolledUp && notRebounding && notShownByItsOwn && !maximizeVisibleAreaRunning) || (m_headerItemShownHeight > 0) || m_inContentHeightKeepHeaderShown) {
498499 if (maximizeVisibleAreaRunning && diff > 0) // If we are maximizing and the header was shown, make sure we hide it
499500 m_headerItemShownHeight -= diff;
500501 else
@@ -1267,7 +1268,9 @@ void ListViewWithPageHeader::updatePolish()
12671268
12681269 m_contentHeightDirty = false;
12691270 adjustMinYExtent();
1271+ m_inContentHeightKeepHeaderShown = m_headerItem && m_headerItem->y() == contentY();
12701272 setContentHeight(contentHeight);
1273+ m_inContentHeightKeepHeaderShown = false;
12711274 }
12721275}
12731276
plugins/ListViewWithPageHeader/listviewwithpageheader.h+1
@@ -202,6 +202,7 @@ private:
202202
203203 bool m_forceNoClip;
204204 bool m_inLayout;
205+ bool m_inContentHeightKeepHeaderShown;
205206
206207 // Qt 5.0 doesn't like releasing the items just after itemCreated
207208 // so we delay the releasing until the next updatePolish
plugins/Ubuntu/Gestures/AxisVelocityCalculator.cpp+9−18
@@ -21,25 +21,19 @@
2121#include "AxisVelocityCalculator.h"
2222#include <QtCore/QElapsedTimer>
2323
24−class RealTimeSource : public UbuntuGestures::TimeSource {
25−public:
26− RealTimeSource()
27− : UbuntuGestures::TimeSource() {
28− m_timer.start();
29− }
30− virtual qint64 msecsSinceReference() {
31− return m_timer.elapsed();
32− }
33−private:
34− QElapsedTimer m_timer;
35−};
24+using namespace UbuntuGestures;
3625
3726AxisVelocityCalculator::AxisVelocityCalculator(QObject *parent)
27+ : AxisVelocityCalculator(SharedTimeSource(new RealTimeSource), parent)
28+{
29+}
30+
31+AxisVelocityCalculator::AxisVelocityCalculator(const SharedTimeSource &timeSource,
32+ QObject *parent)
3833 : QObject(parent)
39− , m_timeSource(new RealTimeSource)
34+ , m_timeSource(timeSource)
4035 , m_trackedPosition(0.0)
4136{
42− m_timeSource->setParent(this);
4337 reset();
4438}
4539
@@ -142,11 +136,8 @@ int AxisVelocityCalculator::numSamples() const
142136 }
143137}
144138
145−void AxisVelocityCalculator::setTimeSource(UbuntuGestures::TimeSource *timeSource)
139+void AxisVelocityCalculator::setTimeSource(const SharedTimeSource &timeSource)
146140{
147− if (m_timeSource && m_timeSource->parent() == this)
148− delete m_timeSource;
149−
150141 m_timeSource = timeSource;
151142
152143 if (numSamples() > 0) {
plugins/Ubuntu/Gestures/AxisVelocityCalculator.h+9−16
@@ -24,19 +24,7 @@
2424#include "UbuntuGesturesGlobal.h"
2525#include <stdint.h>
2626#include <QtCore/QObject>
27−
28−namespace UbuntuGestures {
29−/*
30− Interface for a time source.
31− */
32−class UBUNTUGESTURES_EXPORT TimeSource : public QObject {
33− Q_OBJECT
34−public:
35− virtual ~TimeSource() {}
36− /* Returns the current time in milliseconds since some reference time in the past. */
37− virtual qint64 msecsSinceReference() = 0;
38−};
39−}
27+#include "TimeSource.h"
4028
4129/*
4230 Estimates the current velocity of a finger based on recent movement along an axis
@@ -73,10 +61,15 @@ class UBUNTUGESTURES_EXPORT AxisVelocityCalculator : public QObject
7361public:
7462
7563 /*
76− Regular constructor
64+ Regular, simple, constructor
7765 */
7866 AxisVelocityCalculator(QObject *parent = 0);
7967
68+ /*
69+ Constructor that takes a TimeSource
70+ */
71+ AxisVelocityCalculator(const UbuntuGestures::SharedTimeSource &timeSource, QObject *parent = 0);
72+
8073 virtual ~AxisVelocityCalculator();
8174
8275 qreal trackedPosition() const;
@@ -97,7 +90,7 @@ public:
9790 /*
9891 Replaces the TimeSource with the given one. Useful for testing purposes.
9992 */
100− void setTimeSource(UbuntuGestures::TimeSource *timeSource);
93+ void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
10194
10295 /*
10396 The minimum amount of samples needed for a velocity calculation.
@@ -145,7 +138,7 @@ private:
145138 int m_samplesRead; /* index of the oldest sample available. -1 if buffer is empty */
146139 int m_samplesWrite; /* index where the next sample will be written */
147140
148− UbuntuGestures::TimeSource *m_timeSource;
141+ UbuntuGestures::SharedTimeSource m_timeSource;
149142
150143 qreal m_trackedPosition;
151144};
plugins/Ubuntu/Gestures/CMakeLists.txt+1
@@ -6,6 +6,7 @@ set(UbuntuGestureQml_SOURCES
66 AxisVelocityCalculator.cpp
77 Direction.cpp
88 DirectionalDragArea.cpp
9+ TimeSource.cpp
910)
1011
1112add_definitions(-DUBUNTUGESTURES_LIBRARY)
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+307−71
@@ -18,6 +18,75 @@
1818
1919#include <QtCore/qmath.h>
2020#include <QtCore/QTimer>
21+#include <QDebug>
22+
23+using namespace UbuntuGestures;
24+
25+#define DIRECTIONALDRAGAREA_DEBUG 0
26+
27+#if DIRECTIONALDRAGAREA_DEBUG
28+#define DDA_DEBUG(msg) qDebug("[DDA] " msg)
29+namespace {
30+QString touchPointStateToString(Qt::TouchPointState state) {
31+ switch (state) {
32+ case Qt::TouchPointPressed:
33+ return QString("pressed");
34+ case Qt::TouchPointMoved:
35+ return QString("moved");
36+ case Qt::TouchPointStationary:
37+ return QString("stationary");
38+ default: // Qt::TouchPointReleased:
39+ return QString("released");
40+ }
41+}
42+QString touchEventToString(QTouchEvent *ev)
43+{
44+ QString message;
45+
46+ switch (ev->type()) {
47+ case QEvent::TouchBegin:
48+ message.append("TouchBegin ");
49+ break;
50+ case QEvent::TouchUpdate:
51+ message.append("TouchUpdate ");
52+ break;
53+ case QEvent::TouchEnd:
54+ message.append("TouchEnd ");
55+ break;
56+ default: //QEvent::TouchCancel
57+ message.append("TouchCancel ");
58+ }
59+
60+ for (int i=0; i < ev->touchPoints().size(); ++i) {
61+
62+ const QTouchEvent::TouchPoint& touchPoint = ev->touchPoints().at(i);
63+ message.append(
64+ QString("(id:%1, state:%2, scenePos:(%3,%4)) ")
65+ .arg(touchPoint.id())
66+ .arg(touchPointStateToString(touchPoint.state()))
67+ .arg(touchPoint.scenePos().x())
68+ .arg(touchPoint.scenePos().y())
69+ );
70+ }
71+
72+ return message;
73+}
74+
75+const char *statusToString(DirectionalDragArea::Status status)
76+{
77+ if (status == DirectionalDragArea::WaitingForTouch) {
78+ return "WaitingForTouch";
79+ } else if (status == DirectionalDragArea::Undecided) {
80+ return "Undecided";
81+ } else {
82+ return "Recognized";
83+ }
84+}
85+
86+} // namespace {
87+#else // DIRECTIONALDRAGAREA_DEBUG
88+#define DDA_DEBUG(msg) do{}while(0)
89+#endif // DIRECTIONALDRAGAREA_DEBUG
2190
2291// Essentially a QTimer wrapper
2392class RecognitionTimer : public UbuntuGestures::AbstractTimer
@@ -48,9 +117,12 @@ DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
48117 , m_minSpeed(0)
49118 , m_maxSilenceTime(200)
50119 , m_silenceTime(0)
120+ , m_compositionTime(60)
51121 , m_numSamplesOnLastSpeedCheck(0)
52122 , m_recognitionTimer(0)
53123 , m_velocityCalculator(0)
124+ , m_timeSource(new RealTimeSource)
125+ , m_activeTouches(m_timeSource)
54126{
55127 setRecognitionTimer(new RecognitionTimer(this));
56128 m_recognitionTimer->setInterval(60);
@@ -73,8 +145,8 @@ void DirectionalDragArea::setDirection(Direction::Type direction)
73145
74146void DirectionalDragArea::setMaxDeviation(qreal value)
75147{
76− if (m_dampedPos.maxDelta() != value) {
77− m_dampedPos.setMaxDelta(value);
148+ if (m_dampedScenePos.maxDelta() != value) {
149+ m_dampedScenePos.setMaxDelta(value);
78150 Q_EMIT maxDeviationChanged(value);
79151 }
80152}
@@ -118,6 +190,14 @@ void DirectionalDragArea::setMaxSilenceTime(int value)
118190 }
119191}
120192
193+void DirectionalDragArea::setCompositionTime(int value)
194+{
195+ if (m_compositionTime != value) {
196+ m_compositionTime = value;
197+ Q_EMIT compositionTimeChanged(value);
198+ }
199+}
200+
121201void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
122202{
123203 int interval = 0;
@@ -141,9 +221,11 @@ void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *tim
141221 }
142222}
143223
144−void DirectionalDragArea::setTimeSource(UbuntuGestures::TimeSource *timeSource)
224+void DirectionalDragArea::setTimeSource(const SharedTimeSource &timeSource)
145225{
226+ m_timeSource = timeSource;
146227 m_velocityCalculator->setTimeSource(timeSource);
228+ m_activeTouches.m_timeSource = timeSource;
147229}
148230
149231qreal DirectionalDragArea::distance() const
@@ -186,6 +268,12 @@ qreal DirectionalDragArea::touchSceneY() const
186268
187269void DirectionalDragArea::touchEvent(QTouchEvent *event)
188270{
271+ #if DIRECTIONALDRAGAREA_DEBUG
272+ // TODO Consider using qCDebug() when available (Qt 5.2)
273+ qDebug() << "[DDA]" << m_timeSource->msecsSinceReference()
274+ << qPrintable(touchEventToString(event));
275+ #endif
276+
189277 if (!isEnabled() || !isVisible()) {
190278 QQuickItem::touchEvent(event);
191279 return;
@@ -198,76 +286,124 @@ void DirectionalDragArea::touchEvent(QTouchEvent *event)
198286 case Undecided:
199287 touchEvent_undecided(event);
200288 break;
201− case Recognized:
289+ default: // Recognized:
202290 touchEvent_recognized(event);
203291 break;
204− default: // Rejected
205− touchEvent_rejected(event);
206− break;
207292 }
293+
294+ m_activeTouches.update(event);
208295}
209296
210297void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
211298{
212− if ((event->touchPointStates() && (Qt::TouchPointPressed || Qt::TouchPointMoved))
213− && event->touchPoints().count() == 1) {
214− const QTouchEvent::TouchPoint &touchPoint = event->touchPoints()[0];
215− m_startPos = touchPoint.pos();
216− m_startScenePos = touchPoint.scenePos();
217− m_touchId = touchPoint.id();
218− m_dampedPos.reset(m_startPos);
219− updateVelocityCalculator(m_startPos);
220− m_velocityCalculator->reset();
221− m_numSamplesOnLastSpeedCheck = 0;
222− m_silenceTime = 0;
223− setPreviousPos(m_startPos);
224− setPreviousScenePos(m_startScenePos);
299+ if (!event->touchPointStates().testFlag(Qt::TouchPointPressed)) {
300+ // Nothing to see here. No touch starting in this event.
301+ return;
302+ }
225303
226− if (m_distanceThreshold > 0)
227− setStatus(Undecided);
228− else
229− setStatus(Recognized);
304+ if (isWithinTouchCompositionWindow()) {
305+ // too close to the last touch start. So we consider them as starting roughly at the same time.
306+ // Can't be a single-touch gesture.
307+ #if DIRECTIONALDRAGAREA_DEBUG
308+ qDebug("[DDA] A new touch point came in but we're still within time composition window. Ignoring it.");
309+ #endif
310+ return;
230311 }
312+
313+ const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
314+
315+ const QTouchEvent::TouchPoint *newTouchPoint = nullptr;
316+ for (int i = 0; i < touchPoints.count(); ++i) {
317+ const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
318+ if (touchPoint.state() == Qt::TouchPointPressed) {
319+ if (newTouchPoint) {
320+ // more than one touch starting in this QTouchEvent. Can't be a single-touch gesture
321+ return;
322+ } else {
323+ // that's our candidate
324+ m_touchId = touchPoint.id();
325+ newTouchPoint = &touchPoint;
326+ }
327+ }
328+ }
329+
330+ Q_ASSERT(newTouchPoint);
331+
332+ // If we have made this far, we are good to go to the next status.
333+
334+ m_startPos = newTouchPoint->pos();
335+ m_startScenePos = newTouchPoint->scenePos();
336+ m_touchId = newTouchPoint->id();
337+ m_dampedScenePos.reset(m_startScenePos);
338+ updateVelocityCalculator(m_startScenePos);
339+ m_velocityCalculator->reset();
340+ m_numSamplesOnLastSpeedCheck = 0;
341+ m_silenceTime = 0;
342+ setPreviousPos(m_startPos);
343+ setPreviousScenePos(m_startScenePos);
344+
345+ setStatus(Undecided);
231346}
232347
233348void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
234349{
235350 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
236− const QPointF &touchPos = touchPoint->pos();
351+
352+ if (!touchPoint) {
353+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
354+ << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
355+ "Considering it as released.";
356+ setStatus(WaitingForTouch);
357+ return;
358+ }
359+
360+ const QPointF &touchScenePos = touchPoint->scenePos();
237361
238362 if (touchPoint->state() == Qt::TouchPointReleased) {
239363 // touch has ended before recognition concluded
364+ DDA_DEBUG("Touch has ended before recognition concluded");
240365 setStatus(WaitingForTouch);
241366 return;
242367 }
243368
244− if (event->touchPointStates().testFlag(Qt::TouchPointPressed)
245− || event->touchPoints().count() > 1) {
369+ if (event->touchPointStates().testFlag(Qt::TouchPointPressed) && isWithinTouchCompositionWindow()) {
246370 // multi-finger drags are not accepted
247− setStatus(Rejected);
371+ DDA_DEBUG("Multi-finger drags are not accepted");
372+ setStatus(WaitingForTouch);
248373 return;
249374 }
250375
251− m_previousDampedPos.setX(m_dampedPos.x());
252− m_previousDampedPos.setY(m_dampedPos.y());
253− m_dampedPos.update(touchPos);
254− updateVelocityCalculator(touchPos);
376+ m_previousDampedScenePos.setX(m_dampedScenePos.x());
377+ m_previousDampedScenePos.setY(m_dampedScenePos.y());
378+ m_dampedScenePos.update(touchScenePos);
379+ updateVelocityCalculator(touchScenePos);
255380
256381 if (!pointInsideAllowedArea()) {
257− setStatus(Rejected);
382+ DDA_DEBUG("Rejecting gesture because touch point is outside allowed area.");
383+ setStatus(WaitingForTouch);
258384 return;
259385 }
260386
261387 if (!movingInRightDirection()) {
262− setStatus(Rejected);
388+ DDA_DEBUG("Rejecting gesture becauuse touch point is moving in the wrong direction.");
389+ setStatus(WaitingForTouch);
263390 return;
264391 }
265392
266− setPreviousPos(touchPos);
267− setPreviousScenePos(touchPoint->scenePos());
393+ setPreviousPos(touchPoint->pos());
394+ setPreviousScenePos(touchScenePos);
268395
269− if (movedFarEnough(touchPos)) {
396+ if (isWithinTouchCompositionWindow()) {
397+ // There's still time for some new touch to appear and ruin our party as it would be combined
398+ // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
399+ DDA_DEBUG("Sill within composition window. Let's wait more.");
400+ return;
401+ }
402+
403+ if (movedFarEnough(touchScenePos)) {
270404 setStatus(Recognized);
405+ } else {
406+ DDA_DEBUG("Didn't move far enough yet. Let's wait more.");
271407 }
272408}
273409
@@ -275,20 +411,18 @@ void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
275411{
276412 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
277413
278− setPreviousPos(touchPoint->pos());
279− setPreviousScenePos(touchPoint->scenePos());
280−
281− if (touchPoint->state() == Qt::TouchPointReleased) {
414+ if (!touchPoint) {
415+ qCritical() << "DirectionalDragArea[status=Recognized]: touch " << m_touchId
416+ << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
417+ "Considering it as released.";
282418 setStatus(WaitingForTouch);
283− }
284−}
285−
286−void DirectionalDragArea::touchEvent_rejected(QTouchEvent *event)
287−{
288− const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
419+ } else {
420+ setPreviousPos(touchPoint->pos());
421+ setPreviousScenePos(touchPoint->scenePos());
289422
290− if (!touchPoint || touchPoint->state() == Qt::TouchPointReleased) {
291− setStatus(WaitingForTouch);
423+ if (touchPoint->state() == Qt::TouchPointReleased) {
424+ setStatus(WaitingForTouch);
425+ }
292426 }
293427}
294428
@@ -307,8 +441,8 @@ const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouch
307441
308442bool DirectionalDragArea::pointInsideAllowedArea() const
309443{
310− qreal dX = m_dampedPos.x() - m_startPos.x();
311− qreal dY = m_dampedPos.y() - m_startPos.y();
444+ qreal dX = m_dampedScenePos.x() - m_startScenePos.x();
445+ qreal dY = m_dampedScenePos.y() - m_startScenePos.y();
312446
313447 switch (m_direction) {
314448 case Direction::Upwards:
@@ -326,22 +460,26 @@ bool DirectionalDragArea::movingInRightDirection() const
326460{
327461 switch (m_direction) {
328462 case Direction::Upwards:
329− return m_dampedPos.y() <= m_previousDampedPos.y();
463+ return m_dampedScenePos.y() <= m_previousDampedScenePos.y();
330464 case Direction::Downwards:
331− return m_dampedPos.y() >= m_previousDampedPos.y();
465+ return m_dampedScenePos.y() >= m_previousDampedScenePos.y();
332466 case Direction::Leftwards:
333− return m_dampedPos.x() <= m_previousDampedPos.x();
467+ return m_dampedScenePos.x() <= m_previousDampedScenePos.x();
334468 default: // Direction::Rightwards:
335− return m_dampedPos.x() >= m_previousDampedPos.x();
469+ return m_dampedScenePos.x() >= m_previousDampedScenePos.x();
336470 }
337471}
338472
339473bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
340474{
341− if (Direction::isHorizontal(m_direction))
342− return qFabs(point.x() - m_startPos.x()) > m_distanceThreshold;
343− else
344− return qFabs(point.y() - m_startPos.y()) > m_distanceThreshold;
475+ if (m_distanceThreshold > 0) {
476+ if (Direction::isHorizontal(m_direction))
477+ return qFabs(point.x() - m_startScenePos.x()) > m_distanceThreshold;
478+ else
479+ return qFabs(point.y() - m_startScenePos.y()) > m_distanceThreshold;
480+ } else {
481+ return true;
482+ }
345483}
346484
347485void DirectionalDragArea::checkSpeed()
@@ -351,7 +489,8 @@ void DirectionalDragArea::checkSpeed()
351489 qreal minSpeedMsecs = m_minSpeed / 1000.0;
352490
353491 if (speed < minSpeedMsecs) {
354− setStatus(Rejected);
492+ DDA_DEBUG("Rejecting gesture because it's below minimum speed.");
493+ setStatus(WaitingForTouch);
355494 }
356495 }
357496
@@ -359,7 +498,8 @@ void DirectionalDragArea::checkSpeed()
359498 m_silenceTime += m_recognitionTimer->interval();
360499
361500 if (m_silenceTime > m_maxSilenceTime) {
362− setStatus(Rejected);
501+ DDA_DEBUG("Rejecting gesture because it's silence time has been exceeded.");
502+ setStatus(WaitingForTouch);
363503 }
364504 } else {
365505 m_silenceTime = 0;
@@ -381,11 +521,13 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
381521 m_status = newStatus;
382522 Q_EMIT statusChanged(m_status);
383523
524+ #if DIRECTIONALDRAGAREA_DEBUG
525+ qDebug() << "[DDA]" << statusToString(oldStatus) << "->" << statusToString(newStatus);
526+ #endif
527+
384528 switch (newStatus) {
385529 case WaitingForTouch:
386− if (oldStatus != DirectionalDragArea::Rejected) {
387− Q_EMIT draggingChanged(false);
388− }
530+ Q_EMIT draggingChanged(false);
389531 break;
390532 case Undecided:
391533 m_recognitionTimer->start();
@@ -395,9 +537,6 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
395537 if (oldStatus == WaitingForTouch)
396538 Q_EMIT draggingChanged(true);
397539 break;
398− case Rejected:
399− Q_EMIT draggingChanged(false);
400− break;
401540 default:
402541 // no-op
403542 break;
@@ -406,8 +545,6 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
406545
407546void DirectionalDragArea::setPreviousPos(const QPointF &point)
408547{
409− Q_ASSERT(m_status != Rejected);
410−
411548 bool xChanged = m_previousPos.x() != point.x();
412549 bool yChanged = m_previousPos.y() != point.y();
413550
@@ -428,8 +565,6 @@ void DirectionalDragArea::setPreviousPos(const QPointF &point)
428565
429566void DirectionalDragArea::setPreviousScenePos(const QPointF &point)
430567{
431− Q_ASSERT(m_status != Rejected);
432−
433568 bool xChanged = m_previousScenePos.x() != point.x();
434569 bool yChanged = m_previousScenePos.y() != point.y();
435570
@@ -457,5 +592,106 @@ void DirectionalDragArea::updateVelocityCalculator(const QPointF &point)
457592 }
458593}
459594
595+bool DirectionalDragArea::isWithinTouchCompositionWindow()
596+{
597+ return !m_activeTouches.isEmpty() &&
598+ m_timeSource->msecsSinceReference() <=
599+ m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
600+}
601+
602+//************************** ActiveTouchesInfo **************************
603+
604+DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
605+ : m_timeSource(timeSource), m_lastUsedIndex(-1)
606+{
607+ // Estimate of the maximum number of active touches we might reach.
608+ // Not a problem if it ends up being an underestimate as this is just
609+ // an optimization.
610+ m_vector.resize(3);
611+}
612+
613+void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
614+{
615+ if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
616+ // nothing to update
617+ return;
618+ }
619+
620+ const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
621+ for (int i = 0; i < touchPoints.count(); ++i) {
622+ const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
623+ if (touchPoint.state() == Qt::TouchPointPressed) {
624+ addTouchPoint(touchPoint);
625+ } else if (touchPoint.state() == Qt::TouchPointReleased) {
626+ removeTouchPoint(touchPoint);
627+ }
628+ }
629+}
630+
631+void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(const QTouchEvent::TouchPoint &touchPoint)
632+{
633+ ActiveTouchInfo &activeTouchInfo = getEmptySlot();
634+ activeTouchInfo.id = touchPoint.id();
635+ activeTouchInfo.startTime = m_timeSource->msecsSinceReference();
636+}
637+
638+void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(const QTouchEvent::TouchPoint &touchPoint)
639+{
640+ for (int i = 0; i <= m_lastUsedIndex; ++i) {
641+ if (touchPoint.id() == m_vector.at(i).id) {
642+ freeSlot(i);
643+ return;
644+ }
645+ }
646+ Q_ASSERT(false); // shouldn't reach this point
647+}
648+
649+DirectionalDragArea::ActiveTouchInfo &DirectionalDragArea::ActiveTouchesInfo::getEmptySlot()
650+{
651+ Q_ASSERT(m_lastUsedIndex < m_vector.size());
652+
653+ // Look for an in-between vacancy first
654+ for (int i = 0; i < m_lastUsedIndex; ++i) {
655+ ActiveTouchInfo &activeTouchInfo = m_vector[i];
656+ if (!activeTouchInfo.isValid()) {
657+ return activeTouchInfo;
658+ }
659+ }
660+
661+ ++m_lastUsedIndex;
662+ if (m_lastUsedIndex >= m_vector.size()) {
663+ m_vector.resize(m_lastUsedIndex + 1);
664+ }
665+
666+ return m_vector[m_lastUsedIndex];
667+}
668+
669+void DirectionalDragArea::ActiveTouchesInfo::freeSlot(int index)
670+{
671+ m_vector[index].reset();
672+ if (index == m_lastUsedIndex) {
673+ do {
674+ --m_lastUsedIndex;
675+ } while (m_lastUsedIndex >= 0 && !m_vector.at(m_lastUsedIndex).isValid());
676+ }
677+}
678+
679+qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
680+{
681+ Q_ASSERT(m_lastUsedIndex >= 0);
682+
683+ qint64 highestStartTime = m_vector.at(0).startTime;
684+ int i = 1;
685+ do {
686+ const ActiveTouchInfo &activeTouchInfo = m_vector.at(i);
687+ if (activeTouchInfo.isValid() && activeTouchInfo.startTime > highestStartTime) {
688+ highestStartTime = activeTouchInfo.startTime;
689+ }
690+ ++i;
691+ } while (i < m_lastUsedIndex);
692+
693+ return highestStartTime;
694+}
695+
460696// Because we are defining a new QObject-based class (RecognitionTimer) here.
461697#include "DirectionalDragArea.moc"
plugins/Ubuntu/Gestures/DirectionalDragArea.h+50−13
@@ -117,9 +117,17 @@ class UBUNTUGESTURES_EXPORT DirectionalDragArea : public QQuickItem {
117117 Q_PROPERTY(int maxSilenceTime READ maxSilenceTime
118118 WRITE setMaxSilenceTime
119119 NOTIFY maxSilenceTimeChanged)
120+
120121 //
121122 /////
122123
124+ // Maximum time (in milliseconds) after the start of a given touch point where
125+ // subsequent touch starts are grouped with the first one into an N-touches gesture
126+ // (e.g. a two-fingers tap or drag).
127+ Q_PROPERTY(int compositionTime READ compositionTime
128+ WRITE setCompositionTime
129+ NOTIFY compositionTimeChanged)
130+
123131 Q_ENUMS(Direction)
124132 Q_ENUMS(Status)
125133public:
@@ -130,26 +138,23 @@ public:
130138
131139 // Describes the state of the directional drag gesture.
132140 enum Status {
133− // There's no touch point over this area.
141+ // Waiting for a new touch point to land on this area. No gesture is being processed
142+ // or tracked.
134143 WaitingForTouch,
135144
136145 // A touch point has landed on this area but it's not know yet whether it is
137146 // performing a drag in the correct direction.
147+ // If it's decided that the touch point is not performing a directional drag gesture,
148+ // it will be rejected/ignored and status will return to WaitingForTouch.
138149 Undecided, //Recognizing,
139150
140151 // There's a touch point in this area and it performed a drag in the correct
141152 // direction.
142153 //
143− // Once recognized, the gesture state will move back to Absent only once
154+ // Once recognized, the gesture state will move back to WaitingForTouch only once
144155 // that touch point ends. The gesture will remain in the Recognized state even if
145156 // the touch point starts moving in other directions or halts.
146157 Recognized,
147−
148− // A gesture was performed but it wasn't a single-touch drag in the correct
149− // direction.
150− // It will remain in this state until there are no more touch points over this
151− // area, at which point it will move to Absent state.
152− Rejected
153158 };
154159 Status status() const { return m_status; }
155160
@@ -164,7 +169,7 @@ public:
164169
165170 bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }
166171
167− qreal maxDeviation() const { return m_dampedPos.maxDelta(); }
172+ qreal maxDeviation() const { return m_dampedScenePos.maxDelta(); }
168173 void setMaxDeviation(qreal value);
169174
170175 qreal wideningAngle() const;
@@ -179,13 +184,16 @@ public:
179184 int maxSilenceTime() const { return m_maxSilenceTime; }
180185 void setMaxSilenceTime(int value);
181186
187+ int compositionTime() const { return m_compositionTime; }
188+ void setCompositionTime(int value);
189+
182190 // Replaces the existing Timer with the given one.
183191 //
184192 // Useful for providing a fake timer when testing.
185193 void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
186194
187195 // Useful for testing, where a fake time source can be supplied
188− void setTimeSource(UbuntuGestures::TimeSource *timeSource);
196+ void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
189197
190198Q_SIGNALS:
191199 void directionChanged(Direction::Type direction);
@@ -198,6 +206,7 @@ Q_SIGNALS:
198206 void distanceThresholdChanged(qreal value);
199207 void minSpeedChanged(qreal value);
200208 void maxSilenceTimeChanged(int value);
209+ void compositionTimeChanged(int value);
201210 void touchXChanged(qreal value);
202211 void touchYChanged(qreal value);
203212 void touchSceneXChanged(qreal value);
@@ -213,7 +222,6 @@ private:
213222 void touchEvent_absent(QTouchEvent *event);
214223 void touchEvent_undecided(QTouchEvent *event);
215224 void touchEvent_recognized(QTouchEvent *event);
216− void touchEvent_rejected(QTouchEvent *event);
217225 bool pointInsideAllowedArea() const;
218226 bool movingInRightDirection() const;
219227 bool movedFarEnough(const QPointF &point) const;
@@ -222,6 +230,7 @@ private:
222230 void setPreviousPos(const QPointF &point);
223231 void setPreviousScenePos(const QPointF &point);
224232 void updateVelocityCalculator(const QPointF &point);
233+ bool isWithinTouchCompositionWindow();
225234
226235 Status m_status;
227236
@@ -233,8 +242,8 @@ private:
233242
234243 // A movement damper is used in some of the gesture recognition calculations
235244 // to get rid of noise or small oscillations in the touch position.
236− DampedPointF m_dampedPos;
237− QPointF m_previousDampedPos;
245+ DampedPointF m_dampedScenePos;
246+ QPointF m_previousDampedScenePos;
238247
239248 Direction::Type m_direction;
240249 qreal m_wideningAngle; // in degrees
@@ -243,9 +252,37 @@ private:
243252 qreal m_minSpeed;
244253 int m_maxSilenceTime; // in milliseconds
245254 int m_silenceTime; // in milliseconds
255+ int m_compositionTime; // in milliseconds
246256 int m_numSamplesOnLastSpeedCheck;
247257 UbuntuGestures::AbstractTimer *m_recognitionTimer;
248258 AxisVelocityCalculator *m_velocityCalculator;
259+
260+ UbuntuGestures::SharedTimeSource m_timeSource;
261+
262+ // Information about an active touch point
263+ struct ActiveTouchInfo {
264+ ActiveTouchInfo() : id(-1), startTime(-1) {}
265+ bool isValid() const { return id != -1; }
266+ void reset() { id = -1; }
267+ int id;
268+ qint64 startTime;
269+ };
270+ class ActiveTouchesInfo {
271+ public:
272+ ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
273+ void update(QTouchEvent *event);
274+ ActiveTouchInfo &touchInfo(int id);
275+ qint64 mostRecentStartTime();
276+ UbuntuGestures::SharedTimeSource m_timeSource;
277+ bool isEmpty() const { return m_lastUsedIndex == -1; }
278+ private:
279+ void addTouchPoint(const QTouchEvent::TouchPoint &touchPoint);
280+ ActiveTouchInfo &getEmptySlot();
281+ void freeSlot(int index);
282+ void removeTouchPoint(const QTouchEvent::TouchPoint &touchPoint);
283+ QVector<struct ActiveTouchInfo> m_vector;
284+ int m_lastUsedIndex;
285+ } m_activeTouches;
249286};
250287
251288#endif // DIRECTIONAL_DRAG_AREA_H
plugins/Ubuntu/Gestures/TimeSource.cppnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2013 - Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License, as
6+ * published by the Free Software Foundation; either version 2.1 or 3.0
7+ * of the License.
8+ *
9+ * This program is distributed in the hope that it will be useful, but
10+ * WITHOUT ANY WARRANTY; without even the implied warranties of
11+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
12+ * PURPOSE. See the applicable version of the GNU Lesser General Public
13+ * License for more details.
14+ *
15+ * You should have received a copy of both the GNU Lesser General Public
16+ * License along with this program. If not, see <http://www.gnu.org/licenses/>
17+ *
18+ * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com>
19+ */
20+
21+#include "TimeSource.h"
22+
23+#include <QElapsedTimer>
24+
25+namespace UbuntuGestures {
26+class RealTimeSourcePrivate {
27+public:
28+ QElapsedTimer timer;
29+};
30+}
31+
32+using namespace UbuntuGestures;
33+
34+RealTimeSource::RealTimeSource()
35+ : UbuntuGestures::TimeSource()
36+ , d(new RealTimeSourcePrivate)
37+{
38+ d->timer.start();
39+}
40+
41+RealTimeSource::~RealTimeSource()
42+{
43+ delete d;
44+}
45+
46+qint64 RealTimeSource::msecsSinceReference()
47+{
48+ return d->timer.elapsed();
49+}
plugins/Ubuntu/Gestures/TimeSource.hnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (C) 2013 - Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License, as
6+ * published by the Free Software Foundation; either version 2.1 or 3.0
7+ * of the License.
8+ *
9+ * This program is distributed in the hope that it will be useful, but
10+ * WITHOUT ANY WARRANTY; without even the implied warranties of
11+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
12+ * PURPOSE. See the applicable version of the GNU Lesser General Public
13+ * License for more details.
14+ *
15+ * You should have received a copy of both the GNU Lesser General Public
16+ * License along with this program. If not, see <http://www.gnu.org/licenses/>
17+ *
18+ * Authored by: Daniel d'Andrada <daniel.dandrada@canonical.com>
19+ */
20+
21+#ifndef UBUNTUGESTURES_TIMESOURCE_H
22+#define UBUNTUGESTURES_TIMESOURCE_H
23+
24+#include "UbuntuGesturesGlobal.h"
25+#include <QSharedPointer>
26+
27+namespace UbuntuGestures {
28+/*
29+ Interface for a time source.
30+ */
31+class UBUNTUGESTURES_EXPORT TimeSource {
32+public:
33+ virtual ~TimeSource() {}
34+ /* Returns the current time in milliseconds since some reference time in the past. */
35+ virtual qint64 msecsSinceReference() = 0;
36+};
37+typedef QSharedPointer<TimeSource> SharedTimeSource;
38+
39+/*
40+ Implementation of a time source
41+ */
42+class RealTimeSourcePrivate;
43+class RealTimeSource : public TimeSource {
44+public:
45+ RealTimeSource();
46+ virtual ~RealTimeSource();
47+ qint64 msecsSinceReference() override;
48+private:
49+ RealTimeSourcePrivate *d;
50+};
51+
52+} // namespace UbuntuGestures
53+
54+#endif // UBUNTUGESTURES_TIMESOURCE_H
plugins/Unity/CMakeLists.txt−1
@@ -34,7 +34,6 @@ set(QMLPLUGIN_SRC
3434 radiooptionfilter.cpp
3535 ratingsfilter.cpp
3636 ratingfilteroption.cpp
37− bottombarvisibilitycommunicatorshell.cpp
3837 applicationpreview.cpp
3938 genericpreview.cpp
4039 moviepreview.cpp
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -36,6 +36,7 @@ set(IndicatorsQML_SOURCES
3636 unitymenumodelcache.cpp
3737 unitymenumodelstack.cpp
3838 visibleindicatorsmodel.cpp
39+ timeformatter.cpp
3940)
4041add_definitions(-DUNITYINDICATORS_LIBRARY)
4142
plugins/Unity/Indicators/Messaging/qml/MessageMenuItemFactory.qml+3−4
@@ -22,7 +22,6 @@ import QtQuick 2.0
2222import Ubuntu.Components 0.1
2323import Unity.Indicators 0.1 as Indicators
2424import QMenuModel 0.1 as QMenuModel
25−import "utils.js" as Utils
2625
2726Indicators.BaseMenuItem {
2827 id: menuItem
@@ -65,7 +64,7 @@ Indicators.BaseMenuItem {
6564 SimpleTextMessage {
6665 // text
6766 title: menu && menu.label ? menu.label : ""
68− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
67+ time: menu ? menu.ext.xCanonicalTime : 0
6968 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
7069 // icons
7170 avatar: menu && menu.ext.icon !== undefined ? menu.ext.icon : "qrc:/indicators/artwork/messaging/default_contact.png"
@@ -90,7 +89,7 @@ Indicators.BaseMenuItem {
9089
9190 // text
9291 title: menu && menu.label ? menu.label : ""
93− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
92+ time: menu ? menu.ext.xCanonicalTime : 0
9493 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
9594 replyButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Send"
9695 // icons
@@ -126,7 +125,7 @@ Indicators.BaseMenuItem {
126125
127126 // text
128127 title: menu && menu.label ? menu.label : ""
129− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
128+ time: menu ? menu.ext.xCanonicalTime : ""
130129 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
131130 actionButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Call back"
132131 replyButtonText: actionsDescription && actionsDescription[1].label ? actionsDescription[1].label : "Send"
plugins/Unity/Indicators/Messaging/qml/SimpleTextMessage.qml+9−2
@@ -20,19 +20,20 @@
2020
2121import QtQuick 2.0
2222import Ubuntu.Components 0.1
23+import Unity.Indicators 0.1
2324
2425HeroMessage {
2526 id: __heroMessage
2627
2728 property string title: ""
28− property string time: ""
29+ property var time
2930 property string message: ""
3031
3132 property alias footer: footerLoader.sourceComponent
3233
3334 expandedHeight: fullMessage.y + fullMessage.height + units.gu(2)
3435 heroMessageHeader.titleText.text: title
35− heroMessageHeader.subtitleText.text: time
36+ heroMessageHeader.subtitleText.text: timeFormatter.timeString
3637 heroMessageHeader.bodyText.text: message
3738
3839 Item {
@@ -49,6 +50,12 @@ HeroMessage {
4950 opacity: 0.0
5051 enabled: false
5152
53+ TimeFormatter {
54+ id: timeFormatter
55+ time: __heroMessage.time
56+ format: "hh:mm - MMM dd"
57+ }
58+
5259 Label {
5360 id: bodyText
5461 anchors {
plugins/Unity/Indicators/Messaging/qml/SnapDecision.qml+9−3
@@ -21,13 +21,13 @@
2121import QtQuick 2.0
2222import Ubuntu.Components 0.1
2323import Unity.Indicators 0.1 as Indicators
24−import "utils.js" as Utils
24+import Unity.Indicators 0.1
2525
2626HeroMessage {
2727 id: snapDecision
2828
2929 property string title: ""
30− property string time: ""
30+ property var time
3131 property string message: ""
3232
3333 property bool activateEnabled: true
@@ -41,7 +41,7 @@ HeroMessage {
4141 heroMessageHeader.titleText.text: title
4242 heroMessageHeader.subtitleText.text: message
4343 heroMessageHeader.subtitleText.color: "#e8e1d0"
44− heroMessageHeader.bodyText.text: time
44+ heroMessageHeader.bodyText.text: timeFormatter.timeString
4545 heroMessageHeader.bodyText.color: "#8f8f88"
4646
4747 signal activate
@@ -59,6 +59,12 @@ HeroMessage {
5959 height: units.gu(4)
6060 opacity: 0.0
6161
62+ TimeFormatter {
63+ id: timeFormatter
64+ time: snapDecision.time
65+ format: "hh:mm - MMM dd"
66+ }
67+
6268 Button {
6369 text: "Message"
6470 color: "#bababa"
plugins/Unity/Indicators/plugin.cpp+2
@@ -33,6 +33,7 @@
3333#include "unitymenumodelcache.h"
3434#include "unitymenumodelstack.h"
3535#include "visibleindicatorsmodel.h"
36+#include "timeformatter.h"
3637
3738static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
3839 Q_UNUSED(engine);
@@ -51,6 +52,7 @@ void Indicators2Plugin::registerTypes(const char *uri)
5152 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
5253 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
5354 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
55+ qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
5456
5557 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
5658
plugins/Unity/Indicators/qml/IndicatorPage.qml+27−6
@@ -34,11 +34,36 @@ IndicatorBase {
3434 Indicators.UnityMenuModelStack {
3535 id: menuStack
3636 head: contentActive ? main.menuModel : null
37+
38+ property var rootMenu: null
39+
40+ onTailChanged: {
41+ if (!tail) {
42+ rootMenu = null;
43+ } else if (rootMenu != tail) {
44+ if (tail.get(0, "type") === rootMenuType) {
45+ rootMenu = menuStack.tail.submenu(0);
46+ push(rootMenu, 0);
47+ } else {
48+ rootMenu = null;
49+ }
50+ }
51+ }
52+ }
53+
54+ Connections {
55+ target: menuStack.tail
56+ onRowsInserted: {
57+ if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
58+ menuStack.rootMenu = menuStack.tail.submenu(0);
59+ menuStack.push(menuStack.rootMenu, 0);
60+ }
61+ }
3762 }
3863
3964 ListView {
4065 id: mainMenu
41− model: menuStack.tail ? menuStack.tail : null
66+ model: menuStack.rootMenu
4267
4368 anchors {
4469 fill: parent
@@ -102,7 +127,7 @@ IndicatorBase {
102127
103128 Loader {
104129 id: loader
105− asynchronous: true
130+ asynchronous: false
106131
107132 property int modelIndex: index
108133
@@ -114,10 +139,6 @@ IndicatorBase {
114139 sourceComponent: factory.load(model)
115140
116141 onLoaded: {
117− if (model.type === rootMenuType) {
118− menuStack.push(mainMenu.model.submenu(index));
119− }
120−
121142 if (item.hasOwnProperty("menuSelected")) {
122143 item.menuSelected = Qt.binding(function() { return mainMenu.selectedIndex == index; });
123144 item.selectMenu.connect(function() { mainMenu.selectedIndex = index; });
plugins/Unity/Indicators/qml/IndicatorWidget.qml+1−1
@@ -24,5 +24,5 @@ IndicatorBase {
2424 id: indicatorWidget
2525
2626 enabled: false
27− property int iconSize: height - units.gu(0.5)
27+ property int iconSize: height
2828}
plugins/Unity/Indicators/qml/SliderMenuItem.qml+3−2
@@ -51,12 +51,12 @@ FramedMenuItem {
5151
5252 control: Item {
5353 id: row
54− width: sliderMenu.text ? units.gu(20) : menuItem.width - (2 * menuItem.__contentsMargins)
54+ width: menuItem.text ? units.gu(20) : menuItem.width - (2 * menuItem.__contentsMargins)
5555 height: slider.height
5656
5757 Image {
5858 id: leftImage
59− visible: source != ""
59+ visible: status === Image.Ready
6060 anchors.left: row.left
6161 anchors.verticalCenter: row.verticalCenter
6262 height: units.gu(4)
@@ -96,6 +96,7 @@ FramedMenuItem {
9696
9797 Image {
9898 id: rightImage
99+ visible: status === Image.Ready
99100 anchors.right: row.right
100101 anchors.verticalCenter: row.verticalCenter
101102 height: units.gu(4)
plugins/Unity/Indicators/timeformatter.cppnew file+172
@@ -0,0 +1,172 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17+ */
18+
19+#include "timeformatter.h"
20+
21+#include <gio/gio.h>
22+#include <QDateTime>
23+
24+struct TimeFormatterPrivate
25+{
26+ TimeFormatter *formatter;
27+
28+ QString format;
29+ QString timeString;
30+ qint64 time;
31+
32+ GDBusConnection *system_bus;
33+ guint subscription_id;
34+ GCancellable *cancellable;
35+};
36+
37+static void
38+timedate1_properties_changed (GDBusConnection *connection,
39+ const gchar *sender_name,
40+ const gchar *object_path,
41+ const gchar *interface_name,
42+ const gchar *signal_name,
43+ GVariant *parameters,
44+ gpointer user_data)
45+{
46+ Q_UNUSED(connection);
47+ Q_UNUSED(sender_name);
48+ Q_UNUSED(object_path);
49+ Q_UNUSED(interface_name);
50+ Q_UNUSED(signal_name);
51+
52+ TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
53+ GVariant *changed;
54+ GVariantIter *iter;
55+ const gchar *name;
56+
57+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
58+ return;
59+
60+ g_variant_get (parameters, "(s@a{sv}as)", NULL, &changed, &iter);
61+
62+ if (g_variant_lookup (changed, "Timezone", "s", NULL)) {
63+ priv->formatter->update();
64+ }
65+ else {
66+ while (g_variant_iter_next (iter, "&s", &name)) {
67+ if (g_str_equal (name, "Timezone")) {
68+ priv->formatter->update();
69+ break;
70+ }
71+ }
72+ }
73+
74+ g_variant_unref (changed);
75+ g_variant_iter_free (iter);
76+}
77+
78+static void
79+got_bus(GObject *object, GAsyncResult *result, gpointer user_data)
80+{
81+ Q_UNUSED(object);
82+
83+ TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
84+ GError *error = NULL;
85+
86+ priv->system_bus = g_bus_get_finish (result, &error);
87+ if (priv->system_bus == NULL) {
88+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
89+ qWarning("TimeFormatter: cannot connect to the system bus: %s", error->message);
90+ g_error_free (error);
91+ return;
92+ }
93+
94+ /* Listen to the PropertiesChanged on the org.freedesktop.timedate1
95+ * interface from any sender. In practice, this signal will only be sent
96+ * from timedated (we can trust other processes on the system bus to behave
97+ * nicely). That way, we don't have to watch timedated's well-known name
98+ * and keep the process alive.
99+ */
100+ priv->subscription_id = g_dbus_connection_signal_subscribe (priv->system_bus,
101+ NULL, /* sender */
102+ "org.freedesktop.DBus.Properties",
103+ "PropertiesChanged",
104+ NULL,
105+ "org.freedesktop.timedate1",
106+ G_DBUS_SIGNAL_FLAGS_NONE,
107+ timedate1_properties_changed,
108+ priv, NULL);
109+}
110+
111+TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
112+{
113+ priv = new TimeFormatterPrivate;
114+ priv->formatter = this;
115+ priv->time = 0;
116+ priv->format = "yyyy-MM-dd hh:mm";
117+ priv->system_bus = NULL;
118+ priv->subscription_id = 0;
119+ priv->cancellable = g_cancellable_new ();
120+
121+ g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
122+}
123+
124+TimeFormatter::~TimeFormatter()
125+{
126+ if (priv->system_bus) {
127+ g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->subscription_id);
128+ g_object_unref (priv->system_bus);
129+ }
130+
131+ g_cancellable_cancel (priv->cancellable);
132+ g_object_unref (priv->cancellable);
133+}
134+
135+QString TimeFormatter::format() const
136+{
137+ return priv->format;
138+}
139+
140+QString TimeFormatter::timeString() const
141+{
142+ return priv->timeString;
143+}
144+
145+qint64 TimeFormatter::time() const
146+{
147+ return priv->time;
148+}
149+
150+void TimeFormatter::setFormat(const QString &format)
151+{
152+ if (priv->format != format) {
153+ priv->format = format;
154+ Q_EMIT formatChanged(priv->format);
155+ update();
156+ }
157+}
158+
159+void TimeFormatter::setTime(qint64 time)
160+{
161+ if (priv->time != time) {
162+ priv->time = time;
163+ Q_EMIT timeChanged(priv->time);
164+ update();
165+ }
166+}
167+
168+void TimeFormatter::update()
169+{
170+ priv->timeString = QDateTime::fromMSecsSinceEpoch(priv->time / 1000).toString(priv->format);
171+ Q_EMIT timeStringChanged(priv->timeString);
172+}
plugins/Unity/Indicators/timeformatter.hnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17+ */
18+
19+#ifndef TIME_FORMATTER_H
20+#define TIME_FORMATTER_H
21+
22+#include "unityindicatorsglobal.h"
23+#include <QObject>
24+
25+class UNITYINDICATORS_EXPORT TimeFormatter : public QObject
26+{
27+ Q_OBJECT
28+ Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged)
29+ Q_PROPERTY(QString timeString READ timeString NOTIFY timeStringChanged)
30+ Q_PROPERTY(qint64 time READ time WRITE setTime NOTIFY timeChanged)
31+
32+public:
33+ TimeFormatter(QObject *parent = 0);
34+ ~TimeFormatter();
35+
36+ QString format() const;
37+ QString timeString() const;
38+ qint64 time() const;
39+
40+ void setFormat(const QString &format);
41+ void setTime(qint64 time);
42+
43+ void update();
44+
45+Q_SIGNALS:
46+ void formatChanged(const QString &format);
47+ void timeStringChanged(const QString &timeString);
48+ void timeChanged(qint64 time);
49+
50+private:
51+ struct TimeFormatterPrivate *priv;
52+};
53+
54+#endif
plugins/Unity/Indicators/unitymenumodelstack.cpp+98−7
@@ -19,6 +19,64 @@
1919
2020#include "unitymenumodelstack.h"
2121
22+#include <QDebug>
23+#include <unitymenumodel.h>
24+
25+class UnityMenuModelEntry : public QObject {
26+ Q_OBJECT
27+public:
28+ UnityMenuModelEntry(UnityMenuModel* model, UnityMenuModel* parentModel, int index)
29+ : m_model(model),
30+ m_parentModel(parentModel),
31+ m_index(index)
32+ {
33+ if (m_parentModel) {
34+ QObject::connect(m_parentModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(onRowsInserted(QModelIndex, int, int)));
35+ QObject::connect(m_parentModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(onRowsRemoved(QModelIndex, int, int)));
36+ QObject::connect(m_parentModel, SIGNAL(modelReset()), this, SLOT(onModelReset()));
37+ }
38+ }
39+
40+ UnityMenuModel* model() const { return m_model; }
41+
42+private Q_SLOTS:
43+ void onRowsInserted(const QModelIndex&, int start, int end)
44+ {
45+ int delta = end-start + 1;
46+ if (start <= m_index) {
47+ m_index += delta;
48+ }
49+ }
50+
51+ void onRowsRemoved(const QModelIndex&, int start, int end)
52+ {
53+ int delta = end-start + 1;
54+ if (start <= m_index) {
55+ if (start + delta > m_index) {
56+ // in the range removed
57+ Q_EMIT remove();
58+ disconnect(m_parentModel, 0, this, 0);
59+ } else {
60+ m_index -= delta;
61+ }
62+ }
63+ }
64+
65+ void onModelReset()
66+ {
67+ Q_EMIT remove();
68+ disconnect(m_parentModel, 0, this, 0);
69+ }
70+
71+Q_SIGNALS:
72+ void remove();
73+
74+private:
75+ UnityMenuModel* m_model;
76+ UnityMenuModel* m_parentModel;
77+ int m_index;
78+};
79+
2280UnityMenuModelStack::UnityMenuModelStack(QObject* parent)
2381 : QObject(parent)
2482{
@@ -26,31 +84,44 @@ UnityMenuModelStack::UnityMenuModelStack(QObject* parent)
2684
2785UnityMenuModelStack::~UnityMenuModelStack()
2886{
87+ qDeleteAll(m_menuModels);
88+ m_menuModels.clear();
2989}
3090
3191UnityMenuModel* UnityMenuModelStack::head() const
3292{
33− return !m_menuModels.isEmpty() ? m_menuModels.first() : NULL;
93+ return !m_menuModels.isEmpty() ? m_menuModels.first()->model() : NULL;
3494}
3595
3696void UnityMenuModelStack::setHead(UnityMenuModel* model)
3797{
3898 if (head() != model) {
99+ qDeleteAll(m_menuModels);
39100 m_menuModels.clear();
40− push(model);
101+
102+ push(model, 0);
41103 Q_EMIT headChanged(model);
42104 }
43105}
44106
45107UnityMenuModel* UnityMenuModelStack::tail() const
46108{
47− return !m_menuModels.isEmpty() ? m_menuModels.last() : NULL;
109+ return !m_menuModels.isEmpty() ? m_menuModels.last()->model() : NULL;
110+}
111+
112+int UnityMenuModelStack::count() const
113+{
114+ return m_menuModels.count();
48115}
49116
50−void UnityMenuModelStack::push(UnityMenuModel* model)
117+void UnityMenuModelStack::push(UnityMenuModel* model, int index)
51118{
52− m_menuModels << model;
119+ UnityMenuModelEntry* entry = new UnityMenuModelEntry(model, tail(), index);
120+ QObject::connect(entry, SIGNAL(remove()), SLOT(onRemove()));
121+
122+ m_menuModels << entry;
53123 Q_EMIT tailChanged(model);
124+ Q_EMIT countChanged(m_menuModels.count());
54125}
55126
56127UnityMenuModel* UnityMenuModelStack::pop()
@@ -58,12 +129,32 @@ UnityMenuModel* UnityMenuModelStack::pop()
58129 if (m_menuModels.isEmpty()) {
59130 return NULL;
60131 }
61− UnityMenuModel* model = m_menuModels.takeLast();
132+ UnityMenuModelEntry* entry = m_menuModels.takeLast();
133+ UnityMenuModel* model = entry->model();
134+ entry->deleteLater();
62135
63136 Q_EMIT tailChanged(tail());
64137 if (m_menuModels.isEmpty()) {
65138 Q_EMIT headChanged(NULL);
66139 }
140+ Q_EMIT countChanged(m_menuModels.count());
67141
68142 return model;
69−}
143+}
144+
145+void UnityMenuModelStack::onRemove()
146+{
147+ UnityMenuModelEntry* removed = qobject_cast<UnityMenuModelEntry*>(sender());
148+ if (!m_menuModels.contains(removed))
149+ return;
150+
151+ for (int i = m_menuModels.count() -1; i >= 0; i--) {
152+ UnityMenuModelEntry* entry = m_menuModels[i];
153+ pop();
154+ if (entry == removed) {
155+ break;
156+ }
157+ }
158+}
159+
160+#include "unitymenumodelstack.moc"
plugins/Unity/Indicators/unitymenumodelstack.h+10−2
@@ -25,6 +25,7 @@
2525#include <QObject>
2626#include <QList>
2727
28+class UnityMenuModelEntry;
2829class UnityMenuModel;
2930
3031// A LIFO queue for storing the current submenu of a UnityMenuModel.
@@ -37,6 +38,7 @@ class UNITYINDICATORS_EXPORT UnityMenuModelStack : public QObject
3738 Q_OBJECT
3839 Q_PROPERTY(UnityMenuModel* head READ head WRITE setHead NOTIFY headChanged)
3940 Q_PROPERTY(UnityMenuModel* tail READ tail NOTIFY tailChanged)
41+ Q_PROPERTY(int count READ count NOTIFY countChanged)
4042public:
4143 UnityMenuModelStack(QObject*parent=NULL);
4244 ~UnityMenuModelStack();
@@ -46,15 +48,21 @@ public:
4648
4749 UnityMenuModel* tail() const;
4850
49− Q_INVOKABLE void push(UnityMenuModel* model);
51+ int count() const;
52+
53+ Q_INVOKABLE void push(UnityMenuModel* model, int menuIndex);
5054 Q_INVOKABLE UnityMenuModel* pop();
5155
5256Q_SIGNALS:
5357 void headChanged(UnityMenuModel* head);
5458 void tailChanged(UnityMenuModel* tail);
59+ void countChanged(int count);
60+
61+private Q_SLOTS:
62+ void onRemove();
5563
5664private:
57− QList<UnityMenuModel*> m_menuModels;
65+ QList<UnityMenuModelEntry*> m_menuModels;
5866};
5967
6068#endif // UNITYMENUMODELSTACK_H
plugins/Unity/Launcher/launcheritem.cpp+4−2
@@ -20,6 +20,8 @@
2020#include "launcheritem.h"
2121#include "quicklistmodel.h"
2222
23+#include <libintl.h>
24+
2325LauncherItem::LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent) :
2426 LauncherItemInterface(parent),
2527 m_appId(appId),
@@ -38,7 +40,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
3840 m_quickList->appendAction(nameAction);
3941 QuickListEntry pinningAction;
4042 pinningAction.setActionId("pin_item");
41− pinningAction.setText("Pin to Launcher");
43+ pinningAction.setText(gettext("Lock to Launcher"));
4244 m_quickList->appendAction(pinningAction);
4345}
4446
@@ -68,7 +70,7 @@ void LauncherItem::setPinned(bool pinned)
6870 m_pinned = pinned;
6971 QuickListEntry entry;
7072 entry.setActionId("pin_item");
71− entry.setText(pinned ? "Remove from Launcher": "Pin to Launcher");
73+ entry.setText(pinned ? gettext("Unlock from Launcher") : gettext("Lock to Launcher"));
7274 m_quickList->updateAction(entry);
7375 Q_EMIT pinnedChanged(pinned);
7476 }
plugins/Unity/plugin.cpp−12
@@ -17,8 +17,6 @@
1717 */
1818
1919// Qt
20−#include <QDBusConnection>
21−#include <QQmlContext>
2220#include <qqml.h>
2321
2422// self
@@ -33,16 +31,12 @@
3331#include "scopes.h"
3432#include "categories.h"
3533#include "categoryresults.h"
36−#include "bottombarvisibilitycommunicatorshell.h"
3734#include "genericoptionsmodel.h"
3835#include "result.h"
3936
4037// libqtdee
4138#include "deelistmodel.h"
4239
43−static const char* BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH = "/BottomBarVisibilityCommunicator";
44−static const char* DBUS_SERVICE = "com.canonical.Shell.BottomBarVisibilityCommunicator";
45−
4640void UnityPlugin::registerTypes(const char *uri)
4741{
4842 Q_ASSERT(uri == QLatin1String("Unity"));
@@ -57,7 +51,6 @@ void UnityPlugin::registerTypes(const char *uri)
5751 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
5852 qmlRegisterUncreatableType<CategoryResults>(uri, 0, 1, "CategoryResults", "Can't create new Category Results in QML. Get them from Categories instance.");
5953 qmlRegisterType<DeeListModel>(uri, 0, 1, "DeeListModel");
60− qmlRegisterUncreatableType<BottomBarVisibilityCommunicatorShell>(uri, 0, 1, "BottomBarVisibilityCommunicatorShell", "Can't create BottomBarVisibilityCommunicatorShell");
6154}
6255
6356void UnityPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
@@ -66,9 +59,4 @@ void UnityPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
6659#ifndef GLIB_VERSION_2_36
6760 g_type_init();
6861#endif
69−
70− QDBusConnection::sessionBus().registerService(DBUS_SERVICE);
71− BottomBarVisibilityCommunicatorShell *bottomBarVisibilityCommunicator = &BottomBarVisibilityCommunicatorShell::instance();
72− QDBusConnection::sessionBus().registerObject(BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH, bottomBarVisibilityCommunicator, QDBusConnection::ExportAllContents);
73− engine->rootContext()->setContextProperty("bottomBarVisibilityCommunicatorShell", bottomBarVisibilityCommunicator);
7462}
plugins/Utils/CMakeLists.txt+1
@@ -13,6 +13,7 @@ include_directories(
1313set(QMLPLUGIN_SRC
1414 albumartprovider.cpp
1515 applicationpaths.h
16+ bottombarvisibilitycommunicatorshell.cpp
1617 mediaartcache.cpp
1718 qlimitproxymodelqml.cpp
1819 qsortfilterproxymodelqml.cpp
plugins/Unity/bottombarvisibilitycommunicatorshell.cpp → plugins/Utils/bottombarvisibilitycommunicatorshell.cpprenamed

The content is the same.

plugins/Unity/bottombarvisibilitycommunicatorshell.h → plugins/Utils/bottombarvisibilitycommunicatorshell.hrenamed

The content is the same.

plugins/Utils/plugin.cpp+12
@@ -18,6 +18,8 @@
1818
1919// Qt
2020#include <QtQml/qqml.h>
21+#include <QDBusConnection>
22+#include <QQmlContext>
2123#include <QtQuick/QQuickWindow>
2224#include <QDebug>
2325// self
@@ -26,11 +28,15 @@
2628// local
2729#include "albumartprovider.h"
2830#include "applicationpaths.h"
31+#include "bottombarvisibilitycommunicatorshell.h"
2932#include "qlimitproxymodelqml.h"
3033#include "qsortfilterproxymodelqml.h"
3134#include "ubuntuwindow.h"
3235#include "unitymenumodelpaths.h"
3336
37+static const char* BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH = "/BottomBarVisibilityCommunicator";
38+static const char* DBUS_SERVICE = "com.canonical.Shell.BottomBarVisibilityCommunicator";
39+
3440static QObject* applicationsPathsSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
3541 Q_UNUSED(engine);
3642 Q_UNUSED(scriptEngine);
@@ -46,6 +52,7 @@ void UtilsPlugin::registerTypes(const char *uri)
4652 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
4753 qmlRegisterExtendedType<QQuickWindow, UbuntuWindow>(uri, 0, 1, "Window");
4854 qmlRegisterSingletonType<ApplicationPaths>(uri, 0, 1, "ApplicationPaths", applicationsPathsSingleton);
55+ qmlRegisterUncreatableType<BottomBarVisibilityCommunicatorShell>(uri, 0, 1, "BottomBarVisibilityCommunicatorShell", "Can't create BottomBarVisibilityCommunicatorShell");
4956}
5057
5158void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
@@ -64,4 +71,9 @@ void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
6471 {
6572 qWarning() << "Failed to register image provider for albumart (unknown error)";
6673 }
74+
75+ QDBusConnection::sessionBus().registerService(DBUS_SERVICE);
76+ BottomBarVisibilityCommunicatorShell *bottomBarVisibilityCommunicator = &BottomBarVisibilityCommunicatorShell::instance();
77+ QDBusConnection::sessionBus().registerObject(BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH, bottomBarVisibilityCommunicator, QDBusConnection::ExportAllContents);
78+ engine->rootContext()->setContextProperty("bottomBarVisibilityCommunicatorShell", bottomBarVisibilityCommunicator);
6779}
po/ar.po+9−5
@@ -7,8 +7,8 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11−"PO-Revision-Date: 2013-10-20 19:41+0000\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
11+"PO-Revision-Date: 2013-11-15 12:29+0000\n"
1212"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
1313"Language-Team: Arabic <ar@li.org>\n"
1414"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
1818"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2013-10-21 05:04+0000\n"
20−"X-Generator: Launchpad (build 16807)\n"
19+"X-Launchpad-Export-Date: 2013-11-16 06:27+0000\n"
20+"X-Generator: Launchpad (build 16831)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -213,11 +213,15 @@ msgstr "تكلم الآن..."
213213msgid "Speaking..."
214214msgstr "تتكلم الآن..."
215215
216+#: Notifications/NotificationMenuItemFactory.qml:89
217+msgid "Show password"
218+msgstr "عرض كلمة السر"
219+
216220#: Panel/SearchIndicator.qml:27
217221msgid "Search"
218222msgstr "ابحث"
219223
220−#: plugins/Unity/scope.cpp:398
224+#: plugins/Unity/scope.cpp:408
221225msgid "Sorry, there is nothing that matches your search."
222226msgstr "عذراً، لا يوجد ما يطابق بحثك."
223227
po/ast.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-03 14:02+0000\n"
1212"Last-Translator: ivarela <ivarela@ubuntu.com>\n"
1313"Language-Team: Asturian <ast@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -209,11 +209,15 @@ msgstr "Fala agora…"
209209msgid "Speaking..."
210210msgstr "Falando…"
211211
212+#: Notifications/NotificationMenuItemFactory.qml:89
213+msgid "Show password"
214+msgstr ""
215+
212216#: Panel/SearchIndicator.qml:27
213217msgid "Search"
214218msgstr "Guetar"
215219
216−#: plugins/Unity/scope.cpp:398
220+#: plugins/Unity/scope.cpp:408
217221msgid "Sorry, there is nothing that matches your search."
218222msgstr "Sentímoslo, nun hai res que concase cola gueta"
219223
@@ -225,24 +229,3 @@ msgstr "Too"
225229#, qt-format
226230msgid "Please enter %1"
227231msgstr "Introduz %1"
228−
229−#~ msgid "New Releases"
230−#~ msgstr "Llanzamientos nuevos"
231−
232−#~ msgid "Featured"
233−#~ msgstr "Destacaos"
234−
235−#~ msgid "Top Charting"
236−#~ msgstr "Meyor posicionaes"
237−
238−#~ msgid "Recent"
239−#~ msgstr "Recientes"
240−
241−#~ msgid "Videos"
242−#~ msgstr "Vídeos"
243−
244−#~ msgid "Music"
245−#~ msgstr "Música"
246−
247−#~ msgid "Popular Online"
248−#~ msgstr "Populares en llinia"
po/az.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-26 23:48+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Azerbaijani <az@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Söyləyin..."
205205msgid "Speaking..."
206206msgstr "Söylənilir..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Axtar"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Bağışlayın, amma axtarışınıza heç bir nəticə uyğun gəlmir."
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Musiqi"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Videolar"
po/bg.po+8−16
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 00:06+0000\n"
1212"Last-Translator: Miroslav E. Hadzhiev (Хаджиев) "
1313"<miroslav.hadzhiev@gmail.com>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
20+"X-Generator: Launchpad (build 16820)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -206,11 +206,15 @@ msgstr ""
206206msgid "Speaking..."
207207msgstr ""
208208
209+#: Notifications/NotificationMenuItemFactory.qml:89
210+msgid "Show password"
211+msgstr ""
212+
209213#: Panel/SearchIndicator.qml:27
210214msgid "Search"
211215msgstr "Търсене"
212216
213−#: plugins/Unity/scope.cpp:398
217+#: plugins/Unity/scope.cpp:408
214218msgid "Sorry, there is nothing that matches your search."
215219msgstr "Съжаляваме, няма нищо, което да отговаря на вашето търсене."
216220
@@ -222,15 +226,3 @@ msgstr ""
222226#, qt-format
223227msgid "Please enter %1"
224228msgstr ""
225−
226−#~ msgid "Featured"
227−#~ msgstr "Най-отличени"
228−
229−#~ msgid "Recent"
230−#~ msgstr "Най-нови"
231−
232−#~ msgid "Music"
233−#~ msgstr "Музика"
234−
235−#~ msgid "Videos"
236−#~ msgstr "Видеоклипове"
po/ca.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-09-28 07:38+0000\n"
1212"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
1313"Language-Team: Catalan <ca@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -211,11 +211,15 @@ msgstr "Parleu..."
211211msgid "Speaking..."
212212msgstr "S'està parlant..."
213213
214+#: Notifications/NotificationMenuItemFactory.qml:89
215+msgid "Show password"
216+msgstr ""
217+
214218#: Panel/SearchIndicator.qml:27
215219msgid "Search"
216220msgstr "Cerca"
217221
218−#: plugins/Unity/scope.cpp:398
222+#: plugins/Unity/scope.cpp:408
219223msgid "Sorry, there is nothing that matches your search."
220224msgstr "No hi ha res que coincideixi amb la cerca"
221225
@@ -227,24 +231,3 @@ msgstr ""
227231#, qt-format
228232msgid "Please enter %1"
229233msgstr "Introduïu %1"
230−
231−#~ msgid "Music"
232−#~ msgstr "Música"
233−
234−#~ msgid "Videos"
235−#~ msgstr "Vídeos"
236−
237−#~ msgid "Recent"
238−#~ msgstr "Recents"
239−
240−#~ msgid "Featured"
241−#~ msgstr "Destacats"
242−
243−#~ msgid "New Releases"
244−#~ msgstr "Novetats"
245−
246−#~ msgid "Top Charting"
247−#~ msgstr "El millor de les llistes"
248−
249−#~ msgid "Popular Online"
250−#~ msgstr "Popular a la xarxa"
po/cs.po+8−19
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-08 08:09+0000\n"
1212"Last-Translator: Tadeáš Pařík <tadeas.parik@gmail.com>\n"
1313"Language-Team: Czech <cs@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -206,11 +206,15 @@ msgstr "Mluvte..."
206206msgid "Speaking..."
207207msgstr "Mluvte..."
208208
209+#: Notifications/NotificationMenuItemFactory.qml:89
210+msgid "Show password"
211+msgstr ""
212+
209213#: Panel/SearchIndicator.qml:27
210214msgid "Search"
211215msgstr "Hledat"
212216
213−#: plugins/Unity/scope.cpp:398
217+#: plugins/Unity/scope.cpp:408
214218msgid "Sorry, there is nothing that matches your search."
215219msgstr "Je nám líto, ale nic neodpovídá zadání"
216220
@@ -222,18 +226,3 @@ msgstr "Vše"
222226#, qt-format
223227msgid "Please enter %1"
224228msgstr "Zadejte prosím %1"
225−
226−#~ msgid "Featured"
227−#~ msgstr "Oblíbené"
228−
229−#~ msgid "Recent"
230−#~ msgstr "Nedávné"
231−
232−#~ msgid "Videos"
233−#~ msgstr "Videa"
234−
235−#~ msgid "Music"
236−#~ msgstr "Hudba"
237−
238−#~ msgid "Popular Online"
239−#~ msgstr "Oblíbené on-line"
po/de.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-18 12:26+0000\n"
1212"Last-Translator: Phillip Sz <Unknown>\n"
1313"Language-Team: German <de@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-19 05:30+0000\n"
19−"X-Generator: Launchpad (build 16807)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -209,11 +209,15 @@ msgstr "Sprechen Sie jetzt …"
209209msgid "Speaking..."
210210msgstr "Sie sprechen …"
211211
212+#: Notifications/NotificationMenuItemFactory.qml:89
213+msgid "Show password"
214+msgstr ""
215+
212216#: Panel/SearchIndicator.qml:27
213217msgid "Search"
214218msgstr "Suchen"
215219
216−#: plugins/Unity/scope.cpp:398
220+#: plugins/Unity/scope.cpp:408
217221msgid "Sorry, there is nothing that matches your search."
218222msgstr "Entschuldigung, es wurden keine Treffer erzielt."
219223
po/el.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 00:53+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Greek <el@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Μιλήστε τώρα..."
205205msgid "Speaking..."
206206msgstr "Ομιλία..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Αναζήτηση"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Λυπούμαστε, δεν υπάρχει τίποτα που να ταιριάζει στην αναζήτησή σας."
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Μουσική"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Βίντεο"
po/en_AU.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-09-28 07:46+0000\n"
1212"Last-Translator: Jared Norris <jarednorris@ubuntu.com>\n"
1313"Language-Team: English (Australia) <en_AU@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Speak Now..."
205205msgid "Speaking..."
206206msgstr "Speaking..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Search"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Sorry, there is nothing that matches your search."
215219
@@ -221,24 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr "Please enter %1"
224−
225−#~ msgid "Featured"
226−#~ msgstr "Featured"
227−
228−#~ msgid "Recent"
229−#~ msgstr "Recent"
230−
231−#~ msgid "New Releases"
232−#~ msgstr "New Releases"
233−
234−#~ msgid "Top Charting"
235−#~ msgstr "Top Charting"
236−
237−#~ msgid "Music"
238−#~ msgstr "Music"
239−
240−#~ msgid "Popular Online"
241−#~ msgstr "Popular Online"
242−
243−#~ msgid "Videos"
244−#~ msgstr "Videos"
po/en_GB.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 08:53+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Speak Now..."
205205msgid "Speaking..."
206206msgstr "Speaking..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Search"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Sorry, there is nothing that matches your search."
215219
@@ -221,24 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr "Please enter %1"
224−
225−#~ msgid "Featured"
226−#~ msgstr "Featured"
227−
228−#~ msgid "Recent"
229−#~ msgstr "Recent"
230−
231−#~ msgid "New Releases"
232−#~ msgstr "New Releases"
233−
234−#~ msgid "Top Charting"
235−#~ msgstr "Top Charting"
236−
237−#~ msgid "Music"
238−#~ msgstr "Music"
239−
240−#~ msgid "Popular Online"
241−#~ msgstr "Popular Online"
242−
243−#~ msgid "Videos"
244−#~ msgstr "Videos"
po/eo.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-26 22:35+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Esperanto <eo@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
po/es.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-02 04:59+0000\n"
1212"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
1313"Language-Team: Spanish <es@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -209,11 +209,15 @@ msgstr "Hable ahora…"
209209msgid "Speaking..."
210210msgstr "Hablando…"
211211
212+#: Notifications/NotificationMenuItemFactory.qml:89
213+msgid "Show password"
214+msgstr ""
215+
212216#: Panel/SearchIndicator.qml:27
213217msgid "Search"
214218msgstr "Buscar"
215219
216−#: plugins/Unity/scope.cpp:398
220+#: plugins/Unity/scope.cpp:408
217221msgid "Sorry, there is nothing that matches your search."
218222msgstr "No hay nada que coincida con su búsqueda."
219223
@@ -225,24 +229,3 @@ msgstr "Todo"
225229#, qt-format
226230msgid "Please enter %1"
227231msgstr "Introduzca %1"
228−
229−#~ msgid "Featured"
230−#~ msgstr "Destacados"
231−
232−#~ msgid "Recent"
233−#~ msgstr "Recientes"
234−
235−#~ msgid "New Releases"
236−#~ msgstr "Lanzamientos nuevos"
237−
238−#~ msgid "Top Charting"
239−#~ msgstr "Mejor posicionadas"
240−
241−#~ msgid "Music"
242−#~ msgstr "Música"
243−
244−#~ msgid "Popular Online"
245−#~ msgstr "Populares en línea"
246−
247−#~ msgid "Videos"
248−#~ msgstr "Vídeos"
po/fa.ponew file+227
@@ -0,0 +1,227 @@
1+# Persian translation for unity8
2+# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
11+"PO-Revision-Date: 2013-10-31 19:02+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Persian <fa@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
20+
21+#: Applications/applications.js:23
22+msgid "Phone"
23+msgstr ""
24+
25+#: Applications/applications.js:29
26+msgid "Camera"
27+msgstr ""
28+
29+#: Applications/applications.js:35
30+msgid "Gallery"
31+msgstr ""
32+
33+#: Applications/applications.js:40
34+msgid "Facebook"
35+msgstr ""
36+
37+#: Applications/applications.js:46
38+msgid "Browser"
39+msgstr ""
40+
41+#: Applications/applications.js:51
42+msgid "Twitter"
43+msgstr ""
44+
45+#: Applications/applications.js:57
46+msgid "GMail"
47+msgstr ""
48+
49+#: Applications/applications.js:62
50+msgid "Weather"
51+msgstr ""
52+
53+#: Applications/applications.js:68
54+msgid "Notepad"
55+msgstr ""
56+
57+#: Applications/applications.js:74
58+msgid "Calendar"
59+msgstr ""
60+
61+#: Applications/applications.js:80
62+msgid "Media Player"
63+msgstr ""
64+
65+#: Applications/applications.js:86
66+msgid "Evernote"
67+msgstr ""
68+
69+#: Applications/applications.js:91
70+msgid "Map"
71+msgstr ""
72+
73+#: Applications/applications.js:96
74+msgid "Pinterest"
75+msgstr ""
76+
77+#: Applications/applications.js:101
78+msgid "SoundCloud"
79+msgstr ""
80+
81+#: Applications/applications.js:106
82+msgid "Wikipedia"
83+msgstr ""
84+
85+#: Applications/applications.js:111
86+msgid "YouTube"
87+msgstr ""
88+
89+#: Components/EdgeDemoOverlay.qml:149
90+msgid "Skip intro"
91+msgstr ""
92+
93+#: Components/EdgeDemo.qml:112
94+msgid "Right edge"
95+msgstr ""
96+
97+#: Components/EdgeDemo.qml:113
98+msgid "Try swiping from the right edge to unlock the phone"
99+msgstr ""
100+
101+#: Components/EdgeDemo.qml:144
102+msgid "Top edge"
103+msgstr ""
104+
105+#: Components/EdgeDemo.qml:145
106+msgid "Try swiping from the top edge to access the indicators"
107+msgstr ""
108+
109+#: Components/EdgeDemo.qml:170
110+msgid "Close"
111+msgstr ""
112+
113+#: Components/EdgeDemo.qml:171
114+msgid "Swipe up again to close the settings screen"
115+msgstr ""
116+
117+#: Components/EdgeDemo.qml:198
118+msgid "Left edge"
119+msgstr ""
120+
121+#: Components/EdgeDemo.qml:199
122+msgid "Swipe from the left to reveal the launcher for quick access to apps"
123+msgstr ""
124+
125+#: Components/EdgeDemo.qml:226
126+msgid "Well done"
127+msgstr ""
128+
129+#: Components/EdgeDemo.qml:227
130+msgid ""
131+"You have now mastered the edge gestures and can start using the "
132+"phone<br><br>Tap anywhere to start"
133+msgstr ""
134+
135+#: Components/PageHeader.qml:341
136+msgid "Recent searches"
137+msgstr ""
138+
139+#. TRANSLATORS: Number of persons who rated this app
140+#. TRANSLATORS: Number of persons who rated this app/video/whatever
141+#: Dash/Apps/AppInfo.qml:73 Dash/Previews/Header.qml:93
142+#, qt-format
143+msgid "(%1)"
144+msgstr ""
145+
146+#. TRANSLATORS: Number of persons who wrote reviews for this app
147+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
148+#: Dash/Apps/AppInfo.qml:85 Dash/Previews/Header.qml:105
149+#, qt-format
150+msgid "%1 review"
151+msgid_plural "%1 reviews"
152+msgstr[0] ""
153+msgstr[1] ""
154+
155+#: Dash/Apps/AppPreview.qml:126
156+msgid "Rate this"
157+msgstr ""
158+
159+#: Dash/Previews/Reviews.qml:74
160+msgid "Add a review"
161+msgstr ""
162+
163+#: Dash/Previews/Reviews.qml:86
164+msgid "Review"
165+msgstr ""
166+
167+#: Dash/Previews/Reviews.qml:121
168+msgid "Send"
169+msgstr ""
170+
171+#: Dash/Previews/Reviews.qml:139
172+msgid "Comments:"
173+msgstr ""
174+
175+#: Greeter/Lockscreen.qml:142
176+msgid "Emergency Call"
177+msgstr ""
178+
179+#: Greeter/PassphraseLockscreen.qml:59
180+#, qt-format
181+msgid "Hello %1"
182+msgstr ""
183+
184+#: Greeter/PassphraseLockscreen.qml:59
185+msgid "Hello"
186+msgstr ""
187+
188+#: Hud/HudParametrizedActionsPage.qml:132
189+msgid "Confirm"
190+msgstr ""
191+
192+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
193+msgid "Type or say a command"
194+msgstr ""
195+
196+#: Hud/Hud.qml:84
197+msgid "Loading. Please Wait..."
198+msgstr ""
199+
200+#: Hud/Hud.qml:89
201+msgid "Speak Now..."
202+msgstr ""
203+
204+#: Hud/Hud.qml:94
205+msgid "Speaking..."
206+msgstr ""
207+
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
212+#: Panel/SearchIndicator.qml:27
213+msgid "Search"
214+msgstr ""
215+
216+#: plugins/Unity/scope.cpp:408
217+msgid "Sorry, there is nothing that matches your search."
218+msgstr ""
219+
220+#: plugins/Unity/showallfilteroption.cpp:41
221+msgid "All"
222+msgstr ""
223+
224+#: Shell.qml:438
225+#, qt-format
226+msgid "Please enter %1"
227+msgstr ""
po/fi.po+9−26
@@ -7,16 +7,16 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11−"PO-Revision-Date: 2013-10-03 09:21+0000\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
11+"PO-Revision-Date: 2013-11-11 14:07+0000\n"
1212"Last-Translator: Jiri Grönroos <Unknown>\n"
1313"Language-Team: Finnish <fi@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-12 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -207,11 +207,15 @@ msgstr "Puhu nyt..."
207207msgid "Speaking..."
208208msgstr "Puhutaan..."
209209
210+#: Notifications/NotificationMenuItemFactory.qml:89
211+msgid "Show password"
212+msgstr "Näytä salasana"
213+
210214#: Panel/SearchIndicator.qml:27
211215msgid "Search"
212216msgstr "Hae"
213217
214−#: plugins/Unity/scope.cpp:398
218+#: plugins/Unity/scope.cpp:408
215219msgid "Sorry, there is nothing that matches your search."
216220msgstr "Hakuun täsmääviä tuloksia ei löydy."
217221
@@ -223,24 +227,3 @@ msgstr "Kaikki"
223227#, qt-format
224228msgid "Please enter %1"
225229msgstr "Kirjoita %1"
226−
227−#~ msgid "Featured"
228−#~ msgstr "Suositellut"
229−
230−#~ msgid "Recent"
231−#~ msgstr "Viimeisimmät"
232−
233−#~ msgid "New Releases"
234−#~ msgstr "Uudet julkaisut"
235−
236−#~ msgid "Top Charting"
237−#~ msgstr "Nousukiidossa"
238−
239−#~ msgid "Music"
240−#~ msgstr "Musiikki"
241−
242−#~ msgid "Popular Online"
243−#~ msgstr "Suosittua verkossa"
244−
245−#~ msgid "Videos"
246−#~ msgstr "Videot"
po/fo.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-01 15:13+0000\n"
1212"Last-Translator: Jógvan Olsen <jeggy@jebster.net>\n"
1313"Language-Team: Faroese <fo@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Snakka nú..."
205205msgid "Speaking..."
206206msgstr "Snakkar..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Leita"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Tíanverri er tað einki sum hóskar til tína leiting."
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Tónleikur"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Sjónbond"
po/fr.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 03:21+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: French <fr@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Musique"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Vidéos"
po/gd.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-09-30 10:34+0000\n"
1212"Last-Translator: Akerbeltz <fios@akerbeltz.org>\n"
1313"Language-Team: Gaelic; Scottish <gd@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
1818"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
20+"X-Generator: Launchpad (build 16820)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -214,11 +214,15 @@ msgstr "Can rud a-nis..."
214214msgid "Speaking..."
215215msgstr "A' bruidhinn..."
216216
217+#: Notifications/NotificationMenuItemFactory.qml:89
218+msgid "Show password"
219+msgstr ""
220+
217221#: Panel/SearchIndicator.qml:27
218222msgid "Search"
219223msgstr "Lorg"
220224
221−#: plugins/Unity/scope.cpp:398
225+#: plugins/Unity/scope.cpp:408
222226msgid "Sorry, there is nothing that matches your search."
223227msgstr "Tha sinn duilich ach chan eil dad a' freagairt ris na lorg thu."
224228
@@ -230,24 +234,3 @@ msgstr ""
230234#, qt-format
231235msgid "Please enter %1"
232236msgstr "Cuir a-steach %1"
233−
234−#~ msgid "Top Charting"
235−#~ msgstr "Gràinnean-mullaich nan cairt"
236−
237−#~ msgid "Featured"
238−#~ msgstr "Brosnaichte"
239−
240−#~ msgid "Recent"
241−#~ msgstr "O chionn goirid"
242−
243−#~ msgid "New Releases"
244−#~ msgstr "Air ùr-sgaoileadh"
245−
246−#~ msgid "Videos"
247−#~ msgstr "Videothan"
248−
249−#~ msgid "Music"
250−#~ msgstr "Ceòl"
251−
252−#~ msgid "Popular Online"
253−#~ msgstr "Fèill mhòr air air loidhne"
po/gl.po+8−22
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-03 18:49+0000\n"
1212"Last-Translator: Marcos Lans <Unknown>\n"
1313"Language-Team: Galician <gl@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -207,11 +207,15 @@ msgstr "Fale agora…"
207207msgid "Speaking..."
208208msgstr "Falando…"
209209
210+#: Notifications/NotificationMenuItemFactory.qml:89
211+msgid "Show password"
212+msgstr ""
213+
210214#: Panel/SearchIndicator.qml:27
211215msgid "Search"
212216msgstr "Buscar"
213217
214−#: plugins/Unity/scope.cpp:398
218+#: plugins/Unity/scope.cpp:408
215219msgid "Sorry, there is nothing that matches your search."
216220msgstr "Sentímolo, non hai nada que coincida con esta busca."
217221
@@ -223,21 +227,3 @@ msgstr "Todo"
223227#, qt-format
224228msgid "Please enter %1"
225229msgstr "Introduza %1"
226−
227−#~ msgid "Music"
228−#~ msgstr "Música"
229−
230−#~ msgid "Videos"
231−#~ msgstr "Vídeos"
232−
233−#~ msgid "Featured"
234−#~ msgstr "Destacado"
235−
236−#~ msgid "Recent"
237−#~ msgstr "Recentes"
238−
239−#~ msgid "New Releases"
240−#~ msgstr "Novas versións"
241−
242−#~ msgid "Popular Online"
243−#~ msgstr "Populares en liña"
po/he.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-01 09:19+0000\n"
1212"Last-Translator: Yaron <sh.yaron@gmail.com>\n"
1313"Language-Team: Hebrew <he@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "יש לדבר כעת..."
205205msgid "Speaking..."
206206msgstr "הדיבור מושמע…"
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "חיפוש"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "שום דבר אינו תואם לחיפוש שלך, עמך הסליחה."
215219
@@ -221,24 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr "נא להזין %1"
224−
225−#~ msgid "Music"
226−#~ msgstr "מוזיקה"
227−
228−#~ msgid "Videos"
229−#~ msgstr "סרטונים"
230−
231−#~ msgid "Top Charting"
232−#~ msgstr "בראש הטבלה"
233−
234−#~ msgid "Featured"
235−#~ msgstr "מומלצים"
236−
237−#~ msgid "Recent"
238−#~ msgstr "אחרונים"
239−
240−#~ msgid "New Releases"
241−#~ msgstr "מהדורות חדשות"
242−
243−#~ msgid "Popular Online"
244−#~ msgstr "פופולריים ברחבי הרשת"
po/hr.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 04:14+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Croatian <hr@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
20+"X-Generator: Launchpad (build 16820)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -206,11 +206,15 @@ msgstr "Recite..."
206206msgid "Speaking..."
207207msgstr "Govor..."
208208
209+#: Notifications/NotificationMenuItemFactory.qml:89
210+msgid "Show password"
211+msgstr ""
212+
209213#: Panel/SearchIndicator.qml:27
210214msgid "Search"
211215msgstr "Pretraživanje"
212216
213−#: plugins/Unity/scope.cpp:398
217+#: plugins/Unity/scope.cpp:408
214218msgid "Sorry, there is nothing that matches your search."
215219msgstr "Nažalost, ne postoji ništa što odgovara vašoj pretrazi."
216220
@@ -222,9 +226,3 @@ msgstr ""
222226#, qt-format
223227msgid "Please enter %1"
224228msgstr ""
225−
226−#~ msgid "Music"
227−#~ msgstr "Glazba"
228−
229−#~ msgid "Videos"
230−#~ msgstr "Video"
po/hu.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 04:28+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Beszéljen most…"
205205msgid "Speaking..."
206206msgstr "Beszéd…"
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Keresés"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Elnézést, nincs találat a keresésre."
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Zene"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Videók"
po/hy.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 04:43+0000\n"
1212"Last-Translator: iAbaS <sosabazyan@yandex.ru>\n"
1313"Language-Team: Armenian <hy@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Խոսի՛ր"
205205msgid "Speaking..."
206206msgstr "Խոսում եմ..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Որոնում"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Կներերք, բայց Ձեր հարցումով ոչինչ չկար"
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Երգեր"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Վիդեոներ"
po/id.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-26 22:35+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Indonesian <id@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
po/is.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-09-18 16:22+0000\n"
1212"Last-Translator: Andri Thorlacius <Unknown>\n"
1313"Language-Team: Icelandic <is@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -206,11 +206,15 @@ msgstr "Talaðu Núna..."
206206msgid "Speaking..."
207207msgstr "Tala..."
208208
209+#: Notifications/NotificationMenuItemFactory.qml:89
210+msgid "Show password"
211+msgstr ""
212+
209213#: Panel/SearchIndicator.qml:27
210214msgid "Search"
211215msgstr "Leita"
212216
213−#: plugins/Unity/scope.cpp:398
217+#: plugins/Unity/scope.cpp:408
214218msgid "Sorry, there is nothing that matches your search."
215219msgstr "Því miður, það var ekkert sem passaði við leitarorðið þitt."
216220
@@ -222,24 +226,3 @@ msgstr ""
222226#, qt-format
223227msgid "Please enter %1"
224228msgstr ""
225−
226−#~ msgid "Top Charting"
227−#~ msgstr "Topp Listi"
228−
229−#~ msgid "Featured"
230−#~ msgstr "Efst á baugi"
231−
232−#~ msgid "Recent"
233−#~ msgstr "Nýlegt"
234−
235−#~ msgid "New Releases"
236−#~ msgstr "Nýjar Útgáfur"
237−
238−#~ msgid "Videos"
239−#~ msgstr "Myndbönd"
240−
241−#~ msgid "Music"
242−#~ msgstr "Tónlist"
243−
244−#~ msgid "Popular Online"
245−#~ msgstr "Vinsælt á Netinu"
po/it.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-07 09:34+0000\n"
1212"Last-Translator: Claudio Arseni <claudio.arseni@gmail.com>\n"
1313"Language-Team: Italian <it@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -212,11 +212,15 @@ msgstr "Parla ora..."
212212msgid "Speaking..."
213213msgstr "Sto parlando..."
214214
215+#: Notifications/NotificationMenuItemFactory.qml:89
216+msgid "Show password"
217+msgstr ""
218+
215219#: Panel/SearchIndicator.qml:27
216220msgid "Search"
217221msgstr "Cerca"
218222
219−#: plugins/Unity/scope.cpp:398
223+#: plugins/Unity/scope.cpp:408
220224msgid "Sorry, there is nothing that matches your search."
221225msgstr "Nessun risultato corrispondente alla ricerca."
222226
@@ -228,24 +232,3 @@ msgstr "Tutto"
228232#, qt-format
229233msgid "Please enter %1"
230234msgstr "Inserire %1"
231−
232−#~ msgid "Music"
233−#~ msgstr "Musica"
234−
235−#~ msgid "Videos"
236−#~ msgstr "Video"
237−
238−#~ msgid "Recent"
239−#~ msgstr "Recenti"
240−
241−#~ msgid "New Releases"
242−#~ msgstr "Nuovi rilasci"
243−
244−#~ msgid "Featured"
245−#~ msgstr "In evidenza"
246−
247−#~ msgid "Popular Online"
248−#~ msgstr "Popolari online"
249−
250−#~ msgid "Top Charting"
251−#~ msgstr "Più votate"
po/ja.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-26 22:36+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Japanese <ja@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
po/km.po+10−27
@@ -7,16 +7,16 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11−"PO-Revision-Date: 2013-10-11 08:49+0000\n"
12−"Last-Translator: Khoem Sokhem <khoemsokhem@khmeros.info>\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
11+"PO-Revision-Date: 2013-11-14 03:02+0000\n"
12+"Last-Translator: Seng Sutha <sutha@khmeros.info>\n"
1313"Language-Team: Khmer <km@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-15 06:44+0000\n"
19+"X-Generator: Launchpad (build 16831)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -209,11 +209,15 @@ msgstr "និយាយ​ឥឡូវ..."
209209msgid "Speaking..."
210210msgstr "កំពុង​និយាយ..."
211211
212+#: Notifications/NotificationMenuItemFactory.qml:89
213+msgid "Show password"
214+msgstr "បង្ហាញ​ពាក្យ​សម្ងាត់"
215+
212216#: Panel/SearchIndicator.qml:27
213217msgid "Search"
214218msgstr "ស្វែង​រក"
215219
216−#: plugins/Unity/scope.cpp:398
220+#: plugins/Unity/scope.cpp:408
217221msgid "Sorry, there is nothing that matches your search."
218222msgstr "សូម​ទោស មិនមាន​ពាក្យ​ដែល​អ្នក​ស្វែងរក​ទេ​។"
219223
@@ -225,24 +229,3 @@ msgstr "ទាំង​អស់"
225229#, qt-format
226230msgid "Please enter %1"
227231msgstr "សូម​បញ្ចូល %1"
228−
229−#~ msgid "Popular Online"
230−#~ msgstr "លើ​បណ្ដាញ​ប្រជាប្រិយ"
231−
232−#~ msgid "Music"
233−#~ msgstr "តន្ត្រី"
234−
235−#~ msgid "New Releases"
236−#~ msgstr "ការ​ចេញ​ផ្សាយ​ថ្មី"
237−
238−#~ msgid "Top Charting"
239−#~ msgstr "តារាង​ខាង​លើ"
240−
241−#~ msgid "Featured"
242−#~ msgstr "ដែល​ល្បី"
243−
244−#~ msgid "Recent"
245−#~ msgstr "ថ្មីៗ"
246−
247−#~ msgid "Videos"
248−#~ msgstr "វីដេអូ"
po/kn.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 05:43+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Kannada <kn@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "ಈಗ ಮಾತನಾಡಿ ..."
205205msgid "Speaking..."
206206msgstr "ಮಾತನಾಡುತಿದ್ದೀರಿ"
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "ಹುಡುಕು"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "ಕ್ಷಮಿಸಿ ನಿಮ್ಮ ಶೋಧನೆಗೆ ಸರಿಯಾದ ಹೊಂದಾಣಿಕೆಗಳು ದೊರಕುತ್ತಿಲ್ಲ"
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "ಸಂಗೀತ"
227−
228−#~ msgid "Videos"
229−#~ msgstr "ವೀಡಿಯೋಗಳು"
po/ko.po+8−7
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-10 07:04+0000\n"
1212"Last-Translator: Kim Boram <boramism@gmail.com>\n"
1313"Language-Team: Korean <ko@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "검색"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
@@ -221,6 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "음악"
po/ky.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 06:17+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Kirghiz <ky@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Азыр сүйлөңүз..."
205205msgid "Speaking..."
206206msgstr "Сүйлөнүүдө..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Издөө"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Тилекке каршы, сиздин издөөңүзгө эч нерсе дал келген жок."
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Музыка"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Видеолор"
po/lo.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-02 10:00+0000\n"
1212"Last-Translator: Anousak <anousak@gmail.com>\n"
1313"Language-Team: Lao <lo@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "ເວົ້າໂລດ"
205205msgid "Speaking..."
206206msgstr "ກໍາລັງເວົ້າ"
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "ຊອກຫາ"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
@@ -221,9 +225,3 @@ msgstr "ທັງໝົດ"
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr "ກະລູນາປ້ອນ %1"
224−
225−#~ msgid "Videos"
226−#~ msgstr "ວິດີໂອ"
227−
228−#~ msgid "Music"
229−#~ msgstr "ດົນຕີ"
po/lv.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-09-28 16:29+0000\n"
1212"Last-Translator: Jānis-Marks Gailis <jeanmarc.gailis@gmail.com>\n"
1313"Language-Team: Latvian <lv@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Runajiet Tagad"
205205msgid "Speaking..."
206206msgstr "Runa..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Meklēt"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Diemžēl nekas neatbilst jūsu meklētajam."
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Mūzika"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Video"
po/my.po+8−19
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-09 11:35+0000\n"
1212"Last-Translator: Pyae Sone <Unknown>\n"
1313"Language-Team: Burmese <my@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "ယခုပြောပါ"
205205msgid "Speaking..."
206206msgstr "ပြောနေသည်"
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "ရှာဖွေ"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "ဆောရီး၊ သင်၏ရှာဖွေမူနှင့်တူညီတာဘာမှမရှိပါ။"
215219
@@ -221,18 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "တေးဂီတ"
227−
228−#~ msgid "Videos"
229−#~ msgstr "ဗီဒီယိုများ"
230−
231−#~ msgid "New Releases"
232−#~ msgstr "အသစ်ထွက်ရှိသော"
233−
234−#~ msgid "Recent"
235−#~ msgstr "မကြာသေးခင်က"
236−
237−#~ msgid "Popular Online"
238−#~ msgstr "အွန်လိုင်းတွင်ပေါ်ပြူလာဖြစ်သော"
po/nb.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 07:02+0000\n"
1212"Last-Translator: Joakim Langvand <jlangvand@gmail.com>\n"
1313"Language-Team: Norwegian Bokmal <nb@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Snakk nå …"
205205msgid "Speaking..."
206206msgstr "Snakker …"
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Søk"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Søket ga ingen treff."
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Musikk"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Videoer"
po/nl.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 07:19+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Dutch <nl@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:37+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "Nu inspreken…"
205205msgid "Speaking..."
206206msgstr "Inspreken…"
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "Zoeken"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "Er is helaas niets dat met uw zoekopdracht overeenkomt."
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "Muziek"
227−
228−#~ msgid "Videos"
229−#~ msgstr "Video's"
po/pl.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-13 13:44+0000\n"
1212"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
1313"Language-Team: Polish <pl@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
1818"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-10-14 05:47+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
20+"X-Generator: Launchpad (build 16820)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -211,11 +211,15 @@ msgstr "Mów teraz..."
211211msgid "Speaking..."
212212msgstr "Rozpoznanie..."
213213
214+#: Notifications/NotificationMenuItemFactory.qml:89
215+msgid "Show password"
216+msgstr ""
217+
214218#: Panel/SearchIndicator.qml:27
215219msgid "Search"
216220msgstr "Szukaj"
217221
218−#: plugins/Unity/scope.cpp:398
222+#: plugins/Unity/scope.cpp:408
219223msgid "Sorry, there is nothing that matches your search."
220224msgstr "Nie znaleziono pasujących wyników."
221225
po/pt_BR.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-01 13:05+0000\n"
1212"Last-Translator: Rafael Neri <Unknown>\n"
1313"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -207,11 +207,15 @@ msgstr "Fale agora..."
207207msgid "Speaking..."
208208msgstr "Falando..."
209209
210+#: Notifications/NotificationMenuItemFactory.qml:89
211+msgid "Show password"
212+msgstr ""
213+
210214#: Panel/SearchIndicator.qml:27
211215msgid "Search"
212216msgstr "Pesquisar"
213217
214−#: plugins/Unity/scope.cpp:398
218+#: plugins/Unity/scope.cpp:408
215219msgid "Sorry, there is nothing that matches your search."
216220msgstr "Desculpe, não há nada que corresponda à sua pesquisa."
217221
@@ -223,24 +227,3 @@ msgstr ""
223227#, qt-format
224228msgid "Please enter %1"
225229msgstr "Por favor, digite %1"
226−
227−#~ msgid "Music"
228−#~ msgstr "Músicas"
229−
230−#~ msgid "Videos"
231−#~ msgstr "Vídeos"
232−
233−#~ msgid "New Releases"
234−#~ msgstr "Lançamentos"
235−
236−#~ msgid "Recent"
237−#~ msgstr "Recente"
238−
239−#~ msgid "Featured"
240−#~ msgstr "Em destaque"
241−
242−#~ msgid "Top Charting"
243−#~ msgstr "Top gráficos"
244−
245−#~ msgid "Popular Online"
246−#~ msgstr "Popular Online"
po/ro.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 07:53+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Romanian <ro@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
1818"== 0) && (n != 0))) ? 2: 1));\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
20+"X-Generator: Launchpad (build 16820)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -206,11 +206,15 @@ msgstr "Vorbiți acum..."
206206msgid "Speaking..."
207207msgstr "Vorbire..."
208208
209+#: Notifications/NotificationMenuItemFactory.qml:89
210+msgid "Show password"
211+msgstr ""
212+
209213#: Panel/SearchIndicator.qml:27
210214msgid "Search"
211215msgstr "Căutare"
212216
213−#: plugins/Unity/scope.cpp:398
217+#: plugins/Unity/scope.cpp:408
214218msgid "Sorry, there is nothing that matches your search."
215219msgstr ""
216220"Ne pare rău, nu este nimic care se potrivește căutării dumneavoastră."
@@ -223,9 +227,3 @@ msgstr ""
223227#, qt-format
224228msgid "Please enter %1"
225229msgstr ""
226−
227−#~ msgid "Music"
228−#~ msgstr "Muzică"
229−
230−#~ msgid "Videos"
231−#~ msgstr "Video"
po/ru.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-01 05:55+0000\n"
1212"Last-Translator: Zonov Roman <Unknown>\n"
1313"Language-Team: Russian <ru@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
20+"X-Generator: Launchpad (build 16820)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -210,11 +210,15 @@ msgstr "Говорите..."
210210msgid "Speaking..."
211211msgstr "Говорите..."
212212
213+#: Notifications/NotificationMenuItemFactory.qml:89
214+msgid "Show password"
215+msgstr ""
216+
213217#: Panel/SearchIndicator.qml:27
214218msgid "Search"
215219msgstr "Поиск"
216220
217−#: plugins/Unity/scope.cpp:398
221+#: plugins/Unity/scope.cpp:408
218222msgid "Sorry, there is nothing that matches your search."
219223msgstr "Извините, поиск не дал результатов."
220224
@@ -226,24 +230,3 @@ msgstr ""
226230#, qt-format
227231msgid "Please enter %1"
228232msgstr "Пожалуйста, введите %1"
229−
230−#~ msgid "New Releases"
231−#~ msgstr "Новые релизы"
232−
233−#~ msgid "Music"
234−#~ msgstr "Музыка"
235−
236−#~ msgid "Videos"
237−#~ msgstr "Видео"
238−
239−#~ msgid "Recent"
240−#~ msgstr "Недавние"
241−
242−#~ msgid "Top Charting"
243−#~ msgstr "Наиболее популярное"
244−
245−#~ msgid "Popular Online"
246−#~ msgstr "Популярное в Интернете"
247−
248−#~ msgid "Featured"
249−#~ msgstr "Рекомендованное"
po/shn.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-07 11:46+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Shan <shn@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=(n == 1) ? 0 : 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
po/si.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-26 22:36+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Sinhalese <si@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
po/sl.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-02 10:55+0000\n"
1212"Last-Translator: Damir Jerovšek <Unknown>\n"
1313"Language-Team: Slovenian <sl@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
1818"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
20+"X-Generator: Launchpad (build 16820)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -212,11 +212,15 @@ msgstr "Govorite zdaj ..."
212212msgid "Speaking..."
213213msgstr "Govorjenje ..."
214214
215+#: Notifications/NotificationMenuItemFactory.qml:89
216+msgid "Show password"
217+msgstr ""
218+
215219#: Panel/SearchIndicator.qml:27
216220msgid "Search"
217221msgstr "Iskanje"
218222
219−#: plugins/Unity/scope.cpp:398
223+#: plugins/Unity/scope.cpp:408
220224msgid "Sorry, there is nothing that matches your search."
221225msgstr "Nič se ne ujema z vašim iskalnim nizom."
222226
@@ -228,24 +232,3 @@ msgstr "Vse"
228232#, qt-format
229233msgid "Please enter %1"
230234msgstr "Vnesite %1"
231−
232−#~ msgid "Music"
233−#~ msgstr "Glasba"
234−
235−#~ msgid "Videos"
236−#~ msgstr "Videi"
237−
238−#~ msgid "New Releases"
239−#~ msgstr "Nove izdaje"
240−
241−#~ msgid "Top Charting"
242−#~ msgstr "Najvišje na lestvici"
243−
244−#~ msgid "Featured"
245−#~ msgstr "Predstavljeno"
246−
247−#~ msgid "Recent"
248−#~ msgstr "Nedavno"
249−
250−#~ msgid "Popular Online"
251−#~ msgstr "Priljubljeno na spletu"
po/sr.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 09:19+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Serbian <sr@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
20+"X-Generator: Launchpad (build 16820)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -206,11 +206,15 @@ msgstr "Изговорите сада..."
206206msgid "Speaking..."
207207msgstr "Говор..."
208208
209+#: Notifications/NotificationMenuItemFactory.qml:89
210+msgid "Show password"
211+msgstr ""
212+
209213#: Panel/SearchIndicator.qml:27
210214msgid "Search"
211215msgstr "Претрага"
212216
213−#: plugins/Unity/scope.cpp:398
217+#: plugins/Unity/scope.cpp:408
214218msgid "Sorry, there is nothing that matches your search."
215219msgstr "Нажалост, претрага није наишла на икакво подударање."
216220
@@ -222,9 +226,3 @@ msgstr ""
222226#, qt-format
223227msgid "Please enter %1"
224228msgstr ""
225−
226−#~ msgid "Music"
227−#~ msgstr "Музика"
228−
229−#~ msgid "Videos"
230−#~ msgstr "Видео"
po/sv.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-26 22:36+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Swedish <sv@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
po/ta.ponew file+233
@@ -0,0 +1,233 @@
1+# Tamil translation for unity8
2+# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
5+# துளசி <Thangamani Arun<thangam.arunx@gmail.com>>, 2013.
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
11+"PO-Revision-Date: 2013-10-24 11:36+0000\n"
12+"Last-Translator: Thangamani Arun - தங்கமணி அருண் <thangam.arunx@gmail.com>\n"
13+"Language-Team: Ubuntu Tamil Team/Free Tamil Computing/Thamizha\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
20+"Language: ta\n"
21+
22+#: Applications/applications.js:23
23+msgid "Phone"
24+msgstr "தொலைபேசி"
25+
26+#: Applications/applications.js:29
27+msgid "Camera"
28+msgstr "புகைப்பட கருவி"
29+
30+#: Applications/applications.js:35
31+msgid "Gallery"
32+msgstr "படத்தொகுப்பு"
33+
34+#: Applications/applications.js:40
35+msgid "Facebook"
36+msgstr "முகநுல்"
37+
38+#: Applications/applications.js:46
39+msgid "Browser"
40+msgstr "உலாவி"
41+
42+#: Applications/applications.js:51
43+msgid "Twitter"
44+msgstr "ட்விட்டர்"
45+
46+#: Applications/applications.js:57
47+msgid "GMail"
48+msgstr "ஜிமெயில்"
49+
50+#: Applications/applications.js:62
51+msgid "Weather"
52+msgstr "வானிலை"
53+
54+#: Applications/applications.js:68
55+msgid "Notepad"
56+msgstr "குறிப்புபலகை"
57+
58+#: Applications/applications.js:74
59+msgid "Calendar"
60+msgstr "நாள்காட்டி"
61+
62+#: Applications/applications.js:80
63+msgid "Media Player"
64+msgstr "ஊடக இயக்கி"
65+
66+#: Applications/applications.js:86
67+msgid "Evernote"
68+msgstr "எப்போதும்குறிப்பெடு"
69+
70+#: Applications/applications.js:91
71+msgid "Map"
72+msgstr "வரைபடம்"
73+
74+#: Applications/applications.js:96
75+msgid "Pinterest"
76+msgstr "விசைத்தட்டு"
77+
78+#: Applications/applications.js:101
79+msgid "SoundCloud"
80+msgstr "ஒலி கிளவுட்"
81+
82+#: Applications/applications.js:106
83+msgid "Wikipedia"
84+msgstr "விக்கிப்பீடியா"
85+
86+#: Applications/applications.js:111
87+msgid "YouTube"
88+msgstr "யூட்யுப்"
89+
90+#: Components/EdgeDemoOverlay.qml:149
91+msgid "Skip intro"
92+msgstr "அறிமுகத்தை தவிர்"
93+
94+#: Components/EdgeDemo.qml:112
95+msgid "Right edge"
96+msgstr "வலது விளிம்பு"
97+
98+#: Components/EdgeDemo.qml:113
99+msgid "Try swiping from the right edge to unlock the phone"
100+msgstr "தொலைபேசியை திறக்க வலது முனையிலிருந்து தேய்த்து முயற்சி செய்"
101+
102+#: Components/EdgeDemo.qml:144
103+msgid "Top edge"
104+msgstr "மேல் விளிம்பு"
105+
106+#: Components/EdgeDemo.qml:145
107+msgid "Try swiping from the top edge to access the indicators"
108+msgstr "காட்டிகளை அனுக மேல் விளிம்பிலிருந்து தேய்த்து முயற்சி செய்"
109+
110+#: Components/EdgeDemo.qml:170
111+msgid "Close"
112+msgstr "மூடு"
113+
114+#: Components/EdgeDemo.qml:171
115+msgid "Swipe up again to close the settings screen"
116+msgstr "அமைவுகளின் திரையை மூட மறுபடியும் மேலே தேய்க்கவும்"
117+
118+#: Components/EdgeDemo.qml:198
119+msgid "Left edge"
120+msgstr "இடது விளிம்பு"
121+
122+#: Components/EdgeDemo.qml:199
123+msgid "Swipe from the left to reveal the launcher for quick access to apps"
124+msgstr ""
125+"பயன்பாடுகளின் விரைவான அணுகலை பெறத்தேவையான தொடக்கியை இடமிருந்து தேய்த்தால் "
126+"வெளிப்படும்"
127+
128+#: Components/EdgeDemo.qml:226
129+msgid "Well done"
130+msgstr "நன்றாக செய்தீர்கள்"
131+
132+#: Components/EdgeDemo.qml:227
133+msgid ""
134+"You have now mastered the edge gestures and can start using the "
135+"phone<br><br>Tap anywhere to start"
136+msgstr ""
137+"நீங்கள் இப்போது விளிம்பு சைகைகளில் கைதேர்ந்தவர் மற்றும் தொலைபேசி "
138+"பயன்படுத்த தொடங்கவும் <br> <br>தட்டுவதன் மூலம் எங்க வேண்டுமானாலும் "
139+"தொடங்கலாம்."
140+
141+#: Components/PageHeader.qml:341
142+msgid "Recent searches"
143+msgstr "கடையாக தேடப்பட்டவைகள்"
144+
145+#. TRANSLATORS: Number of persons who rated this app
146+#. TRANSLATORS: Number of persons who rated this app/video/whatever
147+#: Dash/Apps/AppInfo.qml:73 Dash/Previews/Header.qml:93
148+#, qt-format
149+msgid "(%1)"
150+msgstr "(%1)"
151+
152+#. TRANSLATORS: Number of persons who wrote reviews for this app
153+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
154+#: Dash/Apps/AppInfo.qml:85 Dash/Previews/Header.qml:105
155+#, qt-format
156+msgid "%1 review"
157+msgid_plural "%1 reviews"
158+msgstr[0] "%1 பரிசீலனை"
159+msgstr[1] "%1 பரிசீலனைகள்"
160+
161+#: Dash/Apps/AppPreview.qml:126
162+msgid "Rate this"
163+msgstr "இதை மதிப்பிடு"
164+
165+#: Dash/Previews/Reviews.qml:74
166+msgid "Add a review"
167+msgstr "ஒரு பரிசீலனையை சேர்"
168+
169+#: Dash/Previews/Reviews.qml:86
170+msgid "Review"
171+msgstr "மறுஆய்வு"
172+
173+#: Dash/Previews/Reviews.qml:121
174+msgid "Send"
175+msgstr "அனுப்பு"
176+
177+#: Dash/Previews/Reviews.qml:139
178+msgid "Comments:"
179+msgstr "கருத்துகள்:"
180+
181+#: Greeter/Lockscreen.qml:142
182+msgid "Emergency Call"
183+msgstr "அவசர அழைப்பு"
184+
185+#: Greeter/PassphraseLockscreen.qml:59
186+#, qt-format
187+msgid "Hello %1"
188+msgstr "வணக்கம் %1"
189+
190+#: Greeter/PassphraseLockscreen.qml:59
191+msgid "Hello"
192+msgstr "வணக்கம்"
193+
194+#: Hud/HudParametrizedActionsPage.qml:132
195+msgid "Confirm"
196+msgstr "உறுதிப்படுத்து"
197+
198+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
199+msgid "Type or say a command"
200+msgstr "ஒரு கட்டளையை உச்சரி அல்லது தட்டச்சுசெய்"
201+
202+#: Hud/Hud.qml:84
203+msgid "Loading. Please Wait..."
204+msgstr "ஏற்றுகிறது. தயைசெய்து காத்திருக்கவும்..."
205+
206+#: Hud/Hud.qml:89
207+msgid "Speak Now..."
208+msgstr "இப்பொது பேசு..."
209+
210+#: Hud/Hud.qml:94
211+msgid "Speaking..."
212+msgstr "பேசுகிறது..."
213+
214+#: Notifications/NotificationMenuItemFactory.qml:89
215+msgid "Show password"
216+msgstr ""
217+
218+#: Panel/SearchIndicator.qml:27
219+msgid "Search"
220+msgstr "தெடு"
221+
222+#: plugins/Unity/scope.cpp:408
223+msgid "Sorry, there is nothing that matches your search."
224+msgstr "மன்னிக்கவும், உங்களுடைய தேடலுக்கு தகுந்த பொருத்தம் கிடைக்கவில்லை."
225+
226+#: plugins/Unity/showallfilteroption.cpp:41
227+msgid "All"
228+msgstr "அனைத்தும்"
229+
230+#: Shell.qml:438
231+#, qt-format
232+msgid "Please enter %1"
233+msgstr "தயவுச்செய்து %1 உள்ளிடு"
po/te.po+8−7
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 09:57+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Telugu <te@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
@@ -221,6 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "సంగీతం"
po/tr.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-01 13:21+0000\n"
1212"Last-Translator: Volkan Gezer <Unknown>\n"
1313"Language-Team: Turkish <tr@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -206,11 +206,15 @@ msgstr "Şimdi Konuşun..."
206206msgid "Speaking..."
207207msgstr "Konuşuyor..."
208208
209+#: Notifications/NotificationMenuItemFactory.qml:89
210+msgid "Show password"
211+msgstr ""
212+
209213#: Panel/SearchIndicator.qml:27
210214msgid "Search"
211215msgstr "Ara"
212216
213−#: plugins/Unity/scope.cpp:398
217+#: plugins/Unity/scope.cpp:408
214218msgid "Sorry, there is nothing that matches your search."
215219msgstr "Üzgünüz, aramanızla eşleşen hiçbir şey yok."
216220
@@ -222,24 +226,3 @@ msgstr ""
222226#, qt-format
223227msgid "Please enter %1"
224228msgstr "Lütfen %1 girin"
225−
226−#~ msgid "Featured"
227−#~ msgstr "Öne Çıkanlar"
228−
229−#~ msgid "Recent"
230−#~ msgstr "En Son"
231−
232−#~ msgid "Music"
233−#~ msgstr "Müzik"
234−
235−#~ msgid "Popular Online"
236−#~ msgstr "Genellikle Çevrimiçi"
237−
238−#~ msgid "Videos"
239−#~ msgstr "Videolar"
240−
241−#~ msgid "Top Charting"
242−#~ msgstr "En Çok Beğenilenler"
243−
244−#~ msgid "New Releases"
245−#~ msgstr "Yeni Sürümler"
po/ug.po+8−19
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-09 22:38+0000\n"
1212"Last-Translator: Gheyret T.Kenji <Unknown>\n"
1313"Language-Team: Uyghur <ug@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -204,11 +204,15 @@ msgstr "ئەمدى سۆزلەڭ…"
204204msgid "Speaking..."
205205msgstr "سۆزلەۋاتىسىز…"
206206
207+#: Notifications/NotificationMenuItemFactory.qml:89
208+msgid "Show password"
209+msgstr ""
210+
207211#: Panel/SearchIndicator.qml:27
208212msgid "Search"
209213msgstr "ئىزدە"
210214
211−#: plugins/Unity/scope.cpp:398
215+#: plugins/Unity/scope.cpp:408
212216msgid "Sorry, there is nothing that matches your search."
213217msgstr "كەچۈرۈڭ، ئىزدىگىنىڭىزگە ماس نەتىجە تېپىلمىدى."
214218
@@ -220,18 +224,3 @@ msgstr "ھەممىسى"
220224#, qt-format
221225msgid "Please enter %1"
222226msgstr "%1 نى كىرگۈزۈڭ"
223−
224−#~ msgid "Featured"
225−#~ msgstr "داڭلىق"
226−
227−#~ msgid "Recent"
228−#~ msgstr "يېقىنقى"
229−
230−#~ msgid "New Releases"
231−#~ msgstr "يېڭى ئېلان قىلىنغانلىرى"
232−
233−#~ msgid "Music"
234−#~ msgstr "مۇزىكا"
235−
236−#~ msgid "Videos"
237−#~ msgstr "سىنلار"
po/uk.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-19 16:44+0000\n"
1212"Last-Translator: Yuri Chornoivan <yurchor@gmail.com>\n"
1313"Language-Team: Ukrainian <uk@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-10-20 05:20+0000\n"
20−"X-Generator: Launchpad (build 16807)\n"
19+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
20+"X-Generator: Launchpad (build 16820)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -212,11 +212,15 @@ msgstr "Можна говорити…"
212212msgid "Speaking..."
213213msgstr "Мовлення…"
214214
215+#: Notifications/NotificationMenuItemFactory.qml:89
216+msgid "Show password"
217+msgstr ""
218+
215219#: Panel/SearchIndicator.qml:27
216220msgid "Search"
217221msgstr "Пошук"
218222
219−#: plugins/Unity/scope.cpp:398
223+#: plugins/Unity/scope.cpp:408
220224msgid "Sorry, there is nothing that matches your search."
221225msgstr "Вибачте, для вказаних вами ключових слів нічого не знайдено."
222226
po/unity8.pot+20−7
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11+"POT-Creation-Date: 2013-11-12 16:10+0100\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -86,7 +86,7 @@ msgstr ""
8686msgid "YouTube"
8787msgstr ""
8888
89−#: Components/EdgeDemoOverlay.qml:149
89+#: Components/EdgeDemoOverlay.qml:151
9090msgid "Skip intro"
9191msgstr ""
9292
@@ -132,7 +132,7 @@ msgid ""
132132"phone<br><br>Tap anywhere to start"
133133msgstr ""
134134
135−#: Components/PageHeader.qml:341
135+#: Components/PageHeader.qml:345
136136msgid "Recent searches"
137137msgstr ""
138138
@@ -152,7 +152,7 @@ msgid_plural "%1 reviews"
152152msgstr[0] ""
153153msgstr[1] ""
154154
155−#: Dash/Apps/AppPreview.qml:126
155+#: Dash/Apps/AppPreview.qml:135
156156msgid "Rate this"
157157msgstr ""
158158
@@ -172,7 +172,7 @@ msgstr ""
172172msgid "Comments:"
173173msgstr ""
174174
175−#: Greeter/Lockscreen.qml:142
175+#: Greeter/Lockscreen.qml:146
176176msgid "Emergency Call"
177177msgstr ""
178178
@@ -205,11 +205,24 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/Launcher/launcheritem.cpp:43
217+#: plugins/Unity/Launcher/launcheritem.cpp:73
218+msgid "Lock to Launcher"
219+msgstr ""
220+
221+#: plugins/Unity/Launcher/launcheritem.cpp:73
222+msgid "Unlock from Launcher"
223+msgstr ""
224+
225+#: plugins/Unity/scope.cpp:408
213226msgid "Sorry, there is nothing that matches your search."
214227msgstr ""
215228
@@ -217,7 +230,7 @@ msgstr ""
217230msgid "All"
218231msgstr ""
219232
220−#: Shell.qml:438
233+#: Shell.qml:441
221234#, qt-format
222235msgid "Please enter %1"
223236msgstr ""
po/xh.po+8−4
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-09-28 13:20+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Xhosa <xh@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr ""
205205msgid "Speaking..."
206206msgstr ""
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr ""
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr ""
215219
po/zh_CN.po+8−25
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-10-09 13:51+0000\n"
1212"Last-Translator: Aron Xu <Unknown>\n"
1313"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "现在请讲..."
205205msgid "Speaking..."
206206msgstr "讲话中..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "搜索"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "对不起,没有找到您想要的结果。"
215219
@@ -221,24 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Featured"
226−#~ msgstr "推荐"
227−
228−#~ msgid "New Releases"
229−#~ msgstr "新版本"
230−
231−#~ msgid "Top Charting"
232−#~ msgstr "排行榜图"
233−
234−#~ msgid "Music"
235−#~ msgstr "音乐"
236−
237−#~ msgid "Popular Online"
238−#~ msgstr "经常在线"
239−
240−#~ msgid "Videos"
241−#~ msgstr "视频"
242−
243−#~ msgid "Recent"
244−#~ msgstr "最近使用"
po/zh_TW.po+8−10
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-10-11 12:12+0200\n"
10+"POT-Creation-Date: 2013-11-05 17:53+0100\n"
1111"PO-Revision-Date: 2013-06-27 10:24+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Chinese (Traditional) <zh_TW@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-11-08 06:38+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -205,11 +205,15 @@ msgstr "請說..."
205205msgid "Speaking..."
206206msgstr "說話中..."
207207
208+#: Notifications/NotificationMenuItemFactory.qml:89
209+msgid "Show password"
210+msgstr ""
211+
208212#: Panel/SearchIndicator.qml:27
209213msgid "Search"
210214msgstr "搜尋"
211215
212−#: plugins/Unity/scope.cpp:398
216+#: plugins/Unity/scope.cpp:408
213217msgid "Sorry, there is nothing that matches your search."
214218msgstr "很抱歉,找不到符合您想搜尋的結果。"
215219
@@ -221,9 +225,3 @@ msgstr ""
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr ""
224−
225−#~ msgid "Music"
226−#~ msgstr "音樂"
227−
228−#~ msgid "Videos"
229−#~ msgstr "影片"
run_on_device+24−4
@@ -1,4 +1,4 @@
1−#!/bin/sh
1+#!/bin/bash
22CODE_DIR=shell
33USER=phablet
44USER_ID=32011
@@ -17,6 +17,8 @@ SUDO="echo $PASSWORD | sudo -S"
1717NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
1818FLIPPED=false
1919CHROOT_PREFIX="/data/ubuntu"
20+SSH_WAS_STARTED=0
21+SSH_STARTED=0
2022
2123usage() {
2224 echo "usage: run_on_device [OPTIONS]\n"
@@ -32,7 +34,15 @@ usage() {
3234 exit 1
3335}
3436
37+start_ssh() {
38+ if [ $SSH_STARTED -eq 0 ]; then
39+ exec_with_adb initctl start ssh
40+ SSH_STARTED=1
41+ fi
42+}
43+
3544exec_with_ssh() {
45+ start_ssh
3646 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -t $USER@$TARGET_IP -p $TARGET_SSH_PORT sudo -u $USER -i bash -ic \"$@\"
3747}
3848
@@ -52,7 +62,7 @@ adb_root() {
5262install_ssh_key() {
5363 ssh-keygen -R $TARGET_IP
5464 HOME_DIR="${CHROOT_PREFIX}/home/phablet"
55− adb push ~/.ssh/phone_rsa.pub $HOME_DIR/.ssh/authorized_keys
65+ adb push ~/.ssh/id_rsa.pub $HOME_DIR/.ssh/authorized_keys
5666 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh
5767 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh/authorized_keys
5868 adb shell chmod 700 $HOME_DIR/.ssh
@@ -65,8 +75,6 @@ setup_adb_forwarding() {
6575}
6676
6777install_dependencies() {
68− exec_with_adb rm /etc/init/ssh.override
69− exec_with_adb service ssh start
7078 exec_with_adb apt-get update
7179 exec_with_ssh $SUDO apt-get -y install build-essential rsync bzr ccache gdb ninja-build devscripts equivs
7280}
@@ -74,6 +82,7 @@ install_dependencies() {
7482sync_code() {
7583 [ -e .bzr ] && bzr export --uncommitted --format=dir /tmp/$CODE_DIR
7684 [ -e .git ] && git checkout-index -a -f --prefix=/tmp/$CODE_DIR/
85+ start_ssh
7786 rsync -crlOzv --delete --exclude builddir -e "ssh -p $TARGET_SSH_PORT -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" /tmp/$CODE_DIR/ $USER@$TARGET_IP:$CODE_DIR/
7887 rm -rf /tmp/$CODE_DIR
7988}
@@ -96,7 +105,9 @@ run() {
96105 fi
97106
98107 exec_with_ssh "stop unity8"
108+ exec_with_ssh "start maliit-server"
99109 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
110+ exec_with_ssh "stop maliit-server"
100111 exec_with_ssh "start unity8"
101112}
102113
@@ -130,6 +141,12 @@ if [ "`adb shell 'grep -q Ubuntu /etc/lsb-release 2> /dev/null && echo -n FLIPPE
130141 fi
131142fi
132143
144+status_output=$(exec_with_adb initctl status ssh)
145+if [[ $status_output == "ssh start/running, process "* ]]; then
146+ SSH_WAS_STARTED=1
147+fi
148+SSH_STARTED=$SSH_WAS_STARTED
149+
133150if $SETUP; then
134151 echo "Setting up environment for building shell.."
135152 install_ssh_key
@@ -144,3 +161,6 @@ else
144161 run
145162fi
146163
164+if [ $SSH_WAS_STARTED -eq 0 ]; then
165+ exec_with_adb initctl stop ssh
166+fi
tests/autopilot/unity8/process_helpers.pynew file+116
@@ -0,0 +1,116 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Utilities
4+# Copyright (C) 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+from autopilot.introspection import (
21+ get_proxy_object_for_existing_process,
22+ ProcessSearchError,
23+)
24+import subprocess
25+from unity8.shell.emulators import UnityEmulatorBase
26+from unity8.shell.emulators.main_window import MainWindow
27+
28+
29+class CannotAccessUnity(Exception):
30+ pass
31+
32+
33+def unlock_unity():
34+ """Helper function that attempts to unlock the unity greeter.
35+
36+ :raises RuntimeError: if the greeter attempts and fails to be unlocked.
37+
38+ :raises RuntimeWarning: when the greeter cannot be found because it is
39+ already unlocked.
40+ :raises CannotAccessUnity: if unity is not introspectable or cannot be
41+ found on dbus.
42+ :raises CannotAccessUnity: if unity's upstart status is not "start" or the
43+ upstart job cannot be found at all.
44+
45+ """
46+ pid = _get_unity_pid()
47+ try:
48+ unity = get_proxy_object_for_existing_process(
49+ pid=pid,
50+ emulator_base=UnityEmulatorBase,
51+ )
52+ main_window = MainWindow(unity)
53+
54+ greeter = main_window.get_greeter()
55+ if greeter is None:
56+ raise RuntimeWarning("Greeter appears to be already unlocked.")
57+ greeter.swipe()
58+ except ProcessSearchError as e:
59+ raise CannotAccessUnity(
60+ "Cannot introspect unity, make sure that it has been started "
61+ "with testability. Perhaps use the function "
62+ "'restart_unity_with_testability' this module provides."
63+ "(%s)"
64+ % str(e)
65+ )
66+
67+
68+def restart_unity_with_testability():
69+ """Restarts (or just starts) unity8 with the testability driver loaded
70+
71+ :raises subprocess.CalledProcessError: if unable to stop or start the
72+ unity8 upstart job.
73+
74+ """
75+ status = _get_unity_status()
76+ if "start/" in status:
77+ try:
78+ print("Stopping unity.")
79+ subprocess.check_call([
80+ 'initctl',
81+ 'stop',
82+ 'unity8',
83+ ])
84+ except subprocess.CalledProcessError as e:
85+ e.args += ("Failed to stop running instance of unity8", )
86+ raise
87+
88+ try:
89+ print("Starting unity with testability.")
90+ subprocess.check_call([
91+ 'initctl',
92+ 'start',
93+ 'unity8',
94+ 'QT_LOAD_TESTABILITY=1',
95+ ])
96+ except subprocess.CalledProcessError as e:
97+ e.args += ("Failed to start unity8 with testability ", )
98+ raise
99+
100+
101+def _get_unity_status():
102+ try:
103+ return subprocess.check_output([
104+ 'initctl',
105+ 'status',
106+ 'unity8'
107+ ])
108+ except subprocess.CalledProcessError as e:
109+ raise CannotAccessUnity("Unable to get unity's status: %s" % str(e))
110+
111+
112+def _get_unity_pid():
113+ status = _get_unity_status()
114+ if not "start/" in status:
115+ raise CannotAccessUnity("Unity is not in the running state.")
116+ return int(status.split()[-1])
tests/autopilot/unity8/shell/emulators/create_interactive_notification.pymode 100755 → 100644

Only the mode changed.

tests/autopilot/unity8/shell/emulators/dash.py+7−17
@@ -18,42 +18,32 @@
1818#
1919
2020from unity8.shell.emulators import UnityEmulatorBase
21−from time import sleep
21+
2222
2323class Dash(UnityEmulatorBase):
2424
2525 """An emulator that understands the Dash."""
2626
2727 def get_home_applications_grid(self):
28− for i in range(1,10):
29− get_grid = self.get_scope('home').select_single(
30− "GenericFilterGrid",
31− objectName="applications.scope"
32− )
33− if get_grid != None:
34− break
35− sleep(1)
28+ get_grid = self.get_scope('home').wait_select_single(
29+ "GenericFilterGrid",
30+ objectName="applications.scope"
31+ )
3632 return get_grid
3733
3834 def get_application_icon(self, text):
3935 """Returns a 'Tile' icon that has the text 'text' from the application
4036 grid.
4137
42− Will return None if the icon isn't found.
43−
4438 :param text: String containing the text of the icon to search for.
4539
4640 """
4741 app_grid = self.get_home_applications_grid()
48− for i in range(1,10):
49− resp_grid = app_grid.select_single('ResponsiveGridView')
50− if resp_grid != None:
51− break
52− sleep(1)
42+ resp_grid = app_grid.wait_select_single('ResponsiveGridView')
5343 return resp_grid.select_single('Tile', text=text)
5444
5545 def get_scope(self, scope_name='home'):
56− dash_content = self.select_single(
46+ dash_content = self.wait_select_single(
5747 'QQuickListView',
5848 objectName='dashContentList'
5949 )
tests/autopilot/unity8/shell/emulators/hud.py+12−4
@@ -26,6 +26,7 @@ from autopilot.input import Touch
2626
2727SwipeCoords = namedtuple('SwipeCoords', 'start_x end_x start_y end_y')
2828
29+
2930class Hud(UnityEmulatorBase, DragMixin):
3031
3132 """An emulator that understands the Hud."""
@@ -34,13 +35,18 @@ class Hud(UnityEmulatorBase, DragMixin):
3435 """Swipes open the Hud."""
3536 self.touch = Touch.create()
3637
37− window = self.get_root_instance().select_single('QQuickView')
38− hud_show_button = window.select_single("HudButton")
38+ window = self.get_root_instance().wait_select_single('QQuickView')
39+ hud_show_button = window.wait_select_single("HudButton")
3940
4041 swipe_coords = self.get_button_swipe_coords(window, hud_show_button)
4142
4243 self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
43− self._drag(swipe_coords.start_x, swipe_coords.start_y, swipe_coords.start_x, swipe_coords.end_y)
44+ self._drag(
45+ swipe_coords.start_x,
46+ swipe_coords.start_y,
47+ swipe_coords.start_x,
48+ swipe_coords.end_y
49+ )
4450 try:
4551 hud_show_button.opacity.wait_for(1.0)
4652 self.touch.release()
@@ -74,6 +80,8 @@ class Hud(UnityEmulatorBase, DragMixin):
7480 start_x = int(main_view.x + (main_view.width / 2))
7581 end_x = start_x
7682 start_y = main_view.y + (main_view.height - 3)
77− end_y = main_view.y + int(hud_show_button.y + (hud_show_button.height/2))
83+ end_y = main_view.y + int(
84+ hud_show_button.y + (hud_show_button.height/2)
85+ )
7886
7987 return SwipeCoords(start_x, end_x, start_y, end_y)
tests/autopilot/unity8/shell/emulators/main_window.py+7−1
@@ -38,11 +38,14 @@ class MainWindow(object):
3838 return self.app.select_single(Greeter)
3939
4040 def get_greeter_content_loader(self):
41− return self.app.select_single(
41+ return self.app.wait_select_single(
4242 "QQuickLoader",
4343 objectName="greeterContentLoader"
4444 )
4545
46+ def get_greeter_background(self):
47+ return self.app.select_single("CrossFadeImage", objectName="greeterBackground")
48+
4649 def get_login_loader(self):
4750 return self.app.select_single("QQuickLoader", objectName="loginLoader")
4851
@@ -93,3 +96,6 @@ class MainWindow(object):
9396
9497 def get_pinentryField(self):
9598 return self.app.select_single(objectName="pinentryField")
99+
100+ def get_shell_background(self):
101+ return self.app.select_single("CrossFadeImage", objectName="backgroundImage")
tests/autopilot/unity8/shell/tests/__init__.py+58−27
@@ -34,7 +34,7 @@ import logging
3434import os.path
3535import subprocess
3636import sys
37−from testtools.matchers import Equals, NotEquals
37+from testtools.matchers import Equals
3838
3939from unity8 import (
4040 get_lib_path,
@@ -53,7 +53,7 @@ logger = logging.getLogger(__name__)
5353UNITYSHELL_GSETTINGS_SCHEMA = "org.compiz.unityshell"
5454UNITYSHELL_GSETTINGS_PATH = "/org/compiz/profiles/unity/plugins/unityshell/"
5555UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
56−UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
56+UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
5757
5858
5959def _get_device_emulation_scenarios(devices='All'):
@@ -93,17 +93,25 @@ class UnityTestCase(AutopilotTestCase):
9393 ], stderr=subprocess.STDOUT)
9494 except subprocess.CalledProcessError, e:
9595 sys.stderr.write(
96− "Error: `initctl status unity8` failed, most probably the unity8 session could not be found:\n\n"
96+ "Error: `initctl status unity8` failed, most probably the "
97+ "unity8 session could not be found:\n\n"
9798 "{0}\n"
98− "Please install unity8 or copy data/unity8.conf to {1}\n".format(
99+ "Please install unity8 or copy data/unity8.conf to "
100+ "{1}\n".format(
99101 e.output,
100− os.path.join(os.getenv("XDG_CONFIG_HOME", os.path.join(os.getenv("HOME"), ".config")), "upstart")
101− ))
102+ os.path.join(os.getenv("XDG_CONFIG_HOME",
103+ os.path.join(os.getenv("HOME"),
104+ ".config")
105+ ),
106+ "upstart")
107+ )
108+ )
102109 sys.exit(1)
103110
104111 if "start/" in output:
105112 sys.stderr.write(
106− "Error: Unity is currently running, these tests require it to be 'stopped'.\n"
113+ "Error: Unity is currently running, these tests require it to "
114+ "be 'stopped'.\n"
107115 "Please run this command before running these tests: \n"
108116 "initctl stop unity8\n"
109117 )
@@ -111,11 +119,22 @@ class UnityTestCase(AutopilotTestCase):
111119
112120 def setUp(self):
113121 super(UnityTestCase, self).setUp()
114− if Gio is not None and UNITYSHELL_GSETTINGS_SCHEMA in Gio.Settings.list_relocatable_schemas():
122+ if (Gio is not None and
123+ UNITYSHELL_GSETTINGS_SCHEMA in
124+ Gio.Settings.list_relocatable_schemas()):
125+
115126 # Hide Unity launcher
116− self._unityshell_schema = Gio.Settings.new_with_path(UNITYSHELL_GSETTINGS_SCHEMA, UNITYSHELL_GSETTINGS_PATH)
117− self._launcher_hide_mode = self._unityshell_schema.get_int(UNITYSHELL_LAUNCHER_KEY)
118− self._unityshell_schema.set_int(UNITYSHELL_LAUNCHER_KEY, UNITYSHELL_LAUNCHER_MODE)
127+ self._unityshell_schema = Gio.Settings.new_with_path(
128+ UNITYSHELL_GSETTINGS_SCHEMA,
129+ UNITYSHELL_GSETTINGS_PATH,
130+ )
131+ self._launcher_hide_mode = self._unityshell_schema.get_int(
132+ UNITYSHELL_LAUNCHER_KEY,
133+ )
134+ self._unityshell_schema.set_int(
135+ UNITYSHELL_LAUNCHER_KEY,
136+ UNITYSHELL_LAUNCHER_MODE,
137+ )
119138 self.addCleanup(self._reset_launcher)
120139
121140 self._proxy = None
@@ -136,7 +155,10 @@ class UnityTestCase(AutopilotTestCase):
136155
137156 def _reset_launcher(self):
138157 """Reset Unity launcher hide mode"""
139− self._unityshell_schema.set_int(UNITYSHELL_LAUNCHER_KEY, self._launcher_hide_mode)
158+ self._unityshell_schema.set_int(
159+ UNITYSHELL_LAUNCHER_KEY,
160+ self._launcher_hide_mode,
161+ )
140162
141163 def _setup_display_details(self):
142164 scale_divisor = self._determine_geometry()
@@ -217,7 +239,10 @@ class UnityTestCase(AutopilotTestCase):
217239 def _upstart_reset_env(self, key, value):
218240 logger.info("Resetting upstart env %s to %s", key, value)
219241 if value is None:
220− subprocess.call(["/sbin/initctl", "unset-env", key], stderr=subprocess.STDOUT)
242+ subprocess.call(
243+ ["/sbin/initctl", "unset-env", key],
244+ stderr=subprocess.STDOUT,
245+ )
221246 else:
222247 subprocess.call([
223248 "/sbin/initctl",
@@ -244,9 +269,13 @@ class UnityTestCase(AutopilotTestCase):
244269 self._setup_extra_mock_environment_patch()
245270
246271 # FIXME: we shouldn't be doing this
247− # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and /tmp/mir_socket as last resort
272+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
273+ # /tmp/mir_socket as last resort
248274 try:
249− os.unlink(os.getenv('MIR_SOCKET', os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"), "mir_socket")))
275+ os.unlink(
276+ os.getenv('MIR_SOCKET',
277+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
278+ "mir_socket")))
250279 except OSError:
251280 pass
252281 try:
@@ -256,7 +285,7 @@ class UnityTestCase(AutopilotTestCase):
256285
257286 app_proxy = self._launch_unity_with_upstart(
258287 binary_path,
259− self.unity_geometry_args
288+ self.unity_geometry_args,
260289 )
261290
262291 self._set_proxy(app_proxy)
@@ -275,14 +304,16 @@ class UnityTestCase(AutopilotTestCase):
275304 binary_arg = "BINARY=%s" % binary_path
276305 extra_args = "ARGS=%s" % " ".join(args)
277306
278− output = subprocess.check_output([
279− "/sbin/initctl",
280− "start",
281− "unity8",
282− binary_arg,
283− extra_args
284− ] + ["%s=%s" % (k,v) for k,v in self._environment.iteritems()],
285− stderr=subprocess.STDOUT)
307+ output = subprocess.check_output(
308+ [
309+ "/sbin/initctl",
310+ "start",
311+ "unity8",
312+ binary_arg,
313+ extra_args,
314+ ] + ["%s=%s" % (k, v) for k, v in self._environment.iteritems()],
315+ stderr=subprocess.STDOUT,
316+ )
286317
287318 self.addCleanup(self._cleanup_launching_upstart_unity)
288319
@@ -296,7 +327,8 @@ class UnityTestCase(AutopilotTestCase):
296327 def _cleanup_launching_upstart_unity(self):
297328 logger.info("Stopping unity")
298329 try:
299− subprocess.check_output(["/sbin/initctl", "stop", "unity8"], stderr=subprocess.STDOUT)
330+ subprocess.check_output(
331+ ["/sbin/initctl", "stop", "unity8"], stderr=subprocess.STDOUT)
300332 except subprocess.CalledProcessError:
301333 logger.warning("Appears unity was already stopped!")
302334
@@ -366,8 +398,7 @@ class UnityTestCase(AutopilotTestCase):
366398 self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
367399
368400 def get_dash(self):
369− dash = self._proxy.select_single(Dash)
370− self.assertThat(dash, NotEquals(None))
401+ dash = self._proxy.wait_select_single(Dash)
371402 return dash
372403
373404 @property
tests/autopilot/unity8/shell/tests/test_greeter.pynew file+38
@@ -0,0 +1,38 @@
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+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
21+
22+from testtools.matchers import Equals
23+
24+class TestGreeter(UnityTestCase):
25+
26+ """Tests the Shell Greeter."""
27+
28+ scenarios = _get_device_emulation_scenarios()
29+
30+ def test_greeter_background(self):
31+ """Test that the background of the greeter and the background of the shell are placed correctly
32+
33+ """
34+ self.launch_unity()
35+ self.main_window.get_greeter()
36+ greeter_background = self.main_window.get_greeter_background()
37+ shell_background = self.main_window.get_shell_background()
38+ self.assertThat(greeter_background.globalRect, Equals(shell_background.globalRect))
tests/autopilot/unity8/shell/tests/test_notifications.py+49−42
@@ -45,7 +45,7 @@ class NotificationsBase(UnityTestCase):
4545 """Given an icons file name returns the full path (either system or
4646 source tree.
4747
48− Consider the graphics directory as root so for example (runnign tests
48+ Consider the graphics directory as root so for example (running tests
4949 from installed unity8-autopilot package):
5050 >>> self.get_icon_path('clock.png')
5151 /usr/share/unity8/graphics/clock.png
@@ -145,8 +145,8 @@ class InteractiveNotificationBase(NotificationsBase):
145145 hints,
146146 )
147147
148− get_notification = lambda: notify_list.select_single('Notification', objectName='notification1')
149− self.assertThat(get_notification, Eventually(NotEquals(None)))
148+ get_notification = lambda: notify_list.wait_select_single(
149+ 'Notification', objectName='notification1')
150150 notification = get_notification()
151151
152152 self.touch.tap_object(
@@ -156,7 +156,8 @@ class InteractiveNotificationBase(NotificationsBase):
156156 self.assert_notification_action_id_was_called('action_id')
157157
158158 def test_sd_incoming_call(self):
159− """Rejecting a call should make notification expand and offer more options."""
159+ """Rejecting a call should make notification expand and
160+ offer more options."""
160161 self.launch_unity()
161162 greeter = self.main_window.get_greeter()
162163 greeter.swipe()
@@ -191,8 +192,8 @@ class InteractiveNotificationBase(NotificationsBase):
191192 hints
192193 )
193194
194− get_notification = lambda: notify_list.select_single('Notification', objectName='notification1')
195− self.assertThat(get_notification, Eventually(NotEquals(None)))
195+ get_notification = lambda: notify_list.wait_select_single(
196+ 'Notification', objectName='notification1')
196197 notification = get_notification()
197198 self._assert_notification(notification, None, None, True, True, 1.0)
198199 initial_height = notification.height
@@ -200,8 +201,10 @@ class InteractiveNotificationBase(NotificationsBase):
200201 self.assertThat(
201202 notification.height,
202203 Eventually(Equals(initial_height +
203− 3 * notification.select_single(objectName="buttonColumn").spacing +
204− 3 * notification.select_single(objectName="button4").height)))
204+ 3 * notification.select_single(
205+ objectName="buttonColumn").spacing +
206+ 3 * notification.select_single(
207+ objectName="button4").height)))
205208 self.touch.tap_object(notification.select_single(objectName="button4"))
206209 self.assert_notification_action_id_was_called("action_decline_4")
207210
@@ -360,13 +363,20 @@ class EphemeralNotificationsTests(NotificationsBase):
360363
361364 notification.show()
362365
363− get_notification = lambda: notify_list.select_single('Notification', objectName='notification1')
364− self.assertThat(get_notification, Eventually(NotEquals(None)))
365− notification = get_notification()
366− self._assert_notification(notification, summary, body, True, True, 1.0)
366+ notification = lambda: notify_list.wait_select_single(
367+ 'Notification', objectName='notification1')
368+ self._assert_notification(
369+ notification(),
370+ summary,
371+ body,
372+ True,
373+ True,
374+ 1.0,
375+ )
367376
368377 def test_icon_summary(self):
369− """Notification must display the expected summary and secondary icon."""
378+ """Notification must display the expected summary and secondary
379+ icon."""
370380 self.launch_unity()
371381 greeter = self.main_window.get_greeter()
372382 greeter.swipe()
@@ -391,11 +401,11 @@ class EphemeralNotificationsTests(NotificationsBase):
391401
392402 notification.show()
393403
394− get_notification = lambda: notify_list.select_single('Notification', objectName='notification1')
395− self.assertThat(get_notification, Eventually(NotEquals(None)))
396− notification = get_notification()
404+ notification = lambda: notify_list.wait_select_single(
405+ 'Notification', objectName='notification1')
406+
397407 self._assert_notification(
398− notification,
408+ notification(),
399409 summary,
400410 None,
401411 False,
@@ -404,7 +414,8 @@ class EphemeralNotificationsTests(NotificationsBase):
404414 )
405415
406416 def test_urgency_order(self):
407− """Notifications must be displayed in order according to their urgency."""
417+ """Notifications must be displayed in order according to their
418+ urgency."""
408419 self.launch_unity()
409420 greeter = self.main_window.get_greeter()
410421 greeter.swipe()
@@ -448,11 +459,10 @@ class EphemeralNotificationsTests(NotificationsBase):
448459 )
449460 notification_critical.show()
450461
451− get_notification = lambda: notify_list.select_single(
462+ get_notification = lambda: notify_list.wait_select_single(
452463 'Notification',
453464 summary=summary_critical
454465 )
455− self.assertThat(get_notification, Eventually(NotEquals(None)))
456466
457467 notification = get_notification()
458468 self._assert_notification(
@@ -464,11 +474,10 @@ class EphemeralNotificationsTests(NotificationsBase):
464474 1.0
465475 )
466476
467− get_normal_notification = lambda: notify_list.select_single(
477+ get_normal_notification = lambda: notify_list.wait_select_single(
468478 'Notification',
469479 summary=summary_normal
470480 )
471− self.assertThat(get_normal_notification, Eventually(NotEquals(None)))
472481 notification = get_normal_notification()
473482 self._assert_notification(
474483 notification,
@@ -479,11 +488,10 @@ class EphemeralNotificationsTests(NotificationsBase):
479488 1.0
480489 )
481490
482− get_low_notification = lambda: notify_list.select_single(
491+ get_low_notification = lambda: notify_list.wait_select_single(
483492 'Notification',
484493 summary=summary_low
485494 )
486− self.assertThat(get_low_notification, Eventually(NotEquals(None)))
487495 notification = get_low_notification()
488496 self._assert_notification(
489497 notification,
@@ -508,9 +516,8 @@ class EphemeralNotificationsTests(NotificationsBase):
508516 notification = self._create_ephemeral_notification(summary, body)
509517 notification.show()
510518
511− get_notification = lambda: notify_list.select_single('Notification', objectName='notification1')
512− self.assertThat(get_notification, Eventually(NotEquals(None)))
513− notification = get_notification()
519+ notification = notify_list.wait_select_single(
520+ 'Notification', objectName='notification1')
514521 self._assert_notification(
515522 notification,
516523 summary,
@@ -533,14 +540,14 @@ class EphemeralNotificationsTests(NotificationsBase):
533540 notification = self._create_ephemeral_notification(summary)
534541 notification.show()
535542
536− get_notification = lambda: notify_list.select_single('Notification', objectName='notification1')
537− self.assertThat(get_notification, Eventually(NotEquals(None)))
538− notification = get_notification()
543+ notification = notify_list.wait_select_single(
544+ 'Notification', objectName='notification1')
539545
540546 self._assert_notification(notification, summary, '', False, False, 1.0)
541547
542548 def test_update_notification_same_layout(self):
543− """Notification must allow updating its contents while being displayed."""
549+ """Notification must allow updating its contents while being
550+ displayed."""
544551 self.launch_unity()
545552 greeter = self.main_window.get_greeter()
546553 greeter.swipe()
@@ -558,8 +565,8 @@ class EphemeralNotificationsTests(NotificationsBase):
558565 )
559566 notification.show()
560567
561− get_notification = lambda: notify_list.select_single('Notification', summary=summary)
562− self.assertThat(get_notification, Eventually(NotEquals(None)))
568+ get_notification = lambda: notify_list.wait_select_single(
569+ 'Notification', summary=summary)
563570 self._assert_notification(
564571 get_notification(),
565572 summary,
@@ -575,11 +582,11 @@ class EphemeralNotificationsTests(NotificationsBase):
575582 icon_path = self._get_icon_path('avatars/amanda.png')
576583 notification.update(summary, body, icon_path)
577584 notification.show()
578− self.assertThat(get_notification, Eventually(NotEquals(None)))
579585 self._assert_notification(get_notification(), summary, body)
580586
581587 def test_update_notification_layout_change(self):
582− """Notification must allow updating its contents and layout while being displayed."""
588+ """Notification must allow updating its contents and layout while
589+ being displayed."""
583590 self.launch_unity()
584591 greeter = self.main_window.get_greeter()
585592 greeter.swipe()
@@ -603,8 +610,8 @@ class EphemeralNotificationsTests(NotificationsBase):
603610 )
604611 notification.show()
605612
606− get_notification = lambda: notify_list.select_single('Notification', objectName='notification1')
607− self.assertThat(get_notification, Eventually(NotEquals(None)))
613+ get_notification = lambda: notify_list.wait_select_single(
614+ 'Notification', objectName='notification1')
608615 self._assert_notification(
609616 get_notification(),
610617 summary,
@@ -645,8 +652,8 @@ class EphemeralNotificationsTests(NotificationsBase):
645652
646653 notification.show()
647654
648− get_notification = lambda: notify_list.select_single('Notification', objectName='notification1')
649− self.assertThat(get_notification, Eventually(NotEquals(None)))
655+ get_notification = lambda: notify_list.wait_select_single(
656+ 'Notification', objectName='notification1')
650657 notification = get_notification()
651658 self._assert_notification(
652659 notification,
@@ -662,7 +669,8 @@ class EphemeralNotificationsTests(NotificationsBase):
662669 'Did you watch the air-race in Oshkosh last week?',
663670 'Phil owned the place like no one before him!',
664671 '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.',
672+ 'Somehow I think to remember Burt Williams did cut corners and '
673+ 'was not punished for this.',
666674 'Hopefully the referees will watch the videos of the race.',
667675 'Burt could get fined with US$ 50000 for that rule-violation :)'
668676 ]
@@ -677,11 +685,10 @@ class EphemeralNotificationsTests(NotificationsBase):
677685 hints=[('x-canonical-append', 'true')]
678686 )
679687 notification.show()
680− get_notification = lambda: notify_list.select_single(
688+ get_notification = lambda: notify_list.wait_select_single(
681689 'Notification',
682690 objectName='notification1'
683691 )
684− self.assertThat(get_notification, Eventually(NotEquals(None)))
685692 notification = get_notification()
686693 self._assert_notification(
687694 notification,
tests/mocks/QMenuModel/CMakeLists.txt+23
@@ -9,3 +9,26 @@ add_custom_target(QMenuModelQmlFiles ALL
99 COMMAND cp ${QMLFILES} ${CMAKE_CURRENT_BINARY_DIR}
1010 DEPENDS ${QMLFILES}
1111)
12+
13+include_directories(
14+ ${Qt5Core_INCLUDE_DIRS}
15+ ${Qt5Quick_INCLUDE_DIRS}
16+)
17+
18+set(QMenuModelQml_SOURCES
19+ actionstateparser.cpp
20+ plugin.cpp
21+ unitymenumodel.cpp
22+)
23+
24+add_library(QMenuModelQml SHARED ${QMenuModelQml_SOURCES})
25+target_link_libraries(QMenuModelQml
26+ ${Qt5Core_LIBRARIES}
27+ ${Qt5Quick_LIBRARIES}
28+)
29+
30+qt5_use_modules(QMenuModelQml Core Quick)
31+
32+set_target_properties(QMenuModelQml PROPERTIES
33+ OUTPUT_NAME qmenumodel
34+ VERSION 0)
tests/mocks/QMenuModel/UnityMenuAction.qmlnew 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+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import QMenuModel 0.1
22+
23+QtObject {
24+ property string name
25+ property UnityMenuModel model
26+ property int index
27+
28+ readonly property var state: undefined
29+ readonly property bool valid: false
30+}
tests/mocks/QMenuModel/actionstateparser.cppnew file+19
@@ -0,0 +1,19 @@
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+ * Authors: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#include "actionstateparser.h"
tests/mocks/QMenuModel/actionstateparser.hnew file+35
@@ -0,0 +1,35 @@
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+ * Authors: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#ifndef MOCK_ACTIONSTATEPARSER_H
20+#define MOCK_ACTIONSTATEPARSER_H
21+
22+#include <QObject>
23+#include <QVariant>
24+
25+typedef struct _GVariant GVariant;
26+
27+class Q_DECL_EXPORT ActionStateParser : public QObject
28+{
29+ Q_OBJECT
30+public:
31+ ActionStateParser(QObject* parent = 0):QObject(parent) {}
32+ virtual QVariant toQVariant(GVariant*) const { return QVariant(); }
33+};
34+
35+#endif // MOCK_ACTIONSTATEPARSER_H
tests/mocks/QMenuModel/plugin.cppnew file+30
@@ -0,0 +1,30 @@
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: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#include "plugin.h"
20+#include "unitymenumodel.h"
21+#include "actionstateparser.h"
22+
23+#include <QtQml>
24+
25+void QMenuModelPlugin::registerTypes(const char *uri)
26+{
27+ Q_ASSERT(uri == QLatin1String("QMenuModel"));
28+ qmlRegisterType<UnityMenuModel>(uri, 0, 1, "UnityMenuModel");
29+ qmlRegisterType<ActionStateParser>(uri, 0, 1, "ActionStateParser");
30+}
tests/mocks/QMenuModel/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: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#ifndef MOCK_QMENUMODEL_PLUGIN_H
20+#define MOCK_QMENUMODEL_PLUGIN_H
21+
22+#include <QQmlEngine>
23+#include <QQmlExtensionPlugin>
24+
25+class QMenuModelPlugin : 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/QMenuModel/qmldir+3−1
@@ -1,3 +1,5 @@
11module QMenuModel
2+plugin qmenumodel
3+
24QDBusActionGroup 0.1 QDBusActionGroup.qml
3−UnityMenuModel 0.1 UnityMenuModel.qml
5+UnityMenuAction 0.1 UnityMenuAction.qml
tests/mocks/QMenuModel/unitymenumodel.cppnew file+239
@@ -0,0 +1,239 @@
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+ * Authors: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#include "unitymenumodel.h"
20+#include <QDebug>
21+
22+enum MenuRoles {
23+ LabelRole = Qt::DisplayRole + 1,
24+ SensitiveRole,
25+ IsSeparatorRole,
26+ IconRole,
27+ TypeRole,
28+ ExtendedAttributesRole,
29+ ActionRole,
30+ ActionStateRole,
31+ IsCheckRole,
32+ IsRadioRole,
33+ IsToggledRole
34+};
35+
36+UnityMenuModel::UnityMenuModel(QObject *parent)
37+: QAbstractListModel(parent)
38+{
39+}
40+
41+QVariant UnityMenuModel::modelData() const
42+{
43+ return m_modelData;
44+}
45+
46+void UnityMenuModel::setModelData(const QVariant& data)
47+{
48+ beginResetModel();
49+
50+ m_modelData.clear();
51+ m_modelData = data.toList();
52+
53+ endResetModel();
54+}
55+
56+
57+void UnityMenuModel::insertRow(int row, const QVariant& data)
58+{
59+ row = qMin(row, rowCount());
60+
61+ beginInsertRows(QModelIndex(), row, row);
62+
63+ m_modelData.insert(row, data);
64+
65+ endInsertRows();
66+}
67+
68+void UnityMenuModel::appendRow(const QVariant& data)
69+{
70+ insertRow(rowCount(), data);
71+}
72+
73+void UnityMenuModel::removeRow(int row)
74+{
75+ if (row < 0 || rowCount() <= row) {
76+ return;
77+ }
78+
79+ beginRemoveRows(QModelIndex(), row, row);
80+
81+ m_modelData.removeAt(row);
82+
83+ endRemoveRows();
84+}
85+
86+UnityMenuModel::~UnityMenuModel()
87+{
88+}
89+
90+QByteArray UnityMenuModel::busName() const
91+{
92+ return m_busName;
93+}
94+
95+void UnityMenuModel::setBusName(const QByteArray &busName)
96+{
97+ this->m_busName = busName;
98+}
99+
100+QVariantMap UnityMenuModel::actions() const
101+{
102+ return m_actions;
103+}
104+
105+void UnityMenuModel::setActions(const QVariantMap &actions)
106+{
107+ this->m_actions = actions;
108+}
109+
110+QByteArray UnityMenuModel::menuObjectPath() const
111+{
112+ return m_menuObjectPath;
113+}
114+
115+void UnityMenuModel::setMenuObjectPath(const QByteArray &path)
116+{
117+ this->m_menuObjectPath = path;
118+}
119+
120+ActionStateParser* UnityMenuModel::actionStateParser() const
121+{
122+ return NULL;
123+}
124+
125+void UnityMenuModel::setActionStateParser(ActionStateParser*)
126+{
127+}
128+
129+int UnityMenuModel::rowCount(const QModelIndex&) const
130+{
131+ return m_modelData.count();
132+}
133+
134+int UnityMenuModel::columnCount(const QModelIndex&) const
135+{
136+ return 1;
137+}
138+
139+QVariant UnityMenuModel::data(const QModelIndex &index, int role) const
140+{
141+ return rowData(index.row())[roleNames()[role]];
142+}
143+
144+QVariantMap UnityMenuModel::rowData(int row) const
145+{
146+ return m_modelData[row].toMap()["rowData"].toMap();
147+}
148+
149+QVariant UnityMenuModel::subMenuData(int row) const
150+{
151+ return m_modelData[row].toMap()["submenu"];
152+}
153+
154+QModelIndex UnityMenuModel::index(int row, int column, const QModelIndex&) const
155+{
156+ return createIndex(row, column);
157+}
158+
159+QModelIndex UnityMenuModel::parent(const QModelIndex &) const
160+{
161+ return QModelIndex();
162+}
163+
164+QHash<int, QByteArray> UnityMenuModel::roleNames() const
165+{
166+ QHash<int, QByteArray> names;
167+
168+ names[LabelRole] = "label";
169+ names[SensitiveRole] = "sensitive";
170+ names[IsSeparatorRole] = "isSeparator";
171+ names[IconRole] = "icon";
172+ names[TypeRole] = "type";
173+ names[ExtendedAttributesRole] = "ext";
174+ names[ActionRole] = "action";
175+ names[ActionStateRole] = "actionState";
176+ names[IsCheckRole] = "isCheck";
177+ names[IsRadioRole] = "isRadio";
178+ names[IsToggledRole] = "isToggled";
179+
180+ return names;
181+}
182+
183+QObject * UnityMenuModel::submenu(int position, QQmlComponent*)
184+{
185+ if (position < 0 || m_modelData.count() < position) {
186+ return NULL;
187+ }
188+
189+ while (submenus.count() <= position) {
190+ submenus.append(NULL);
191+ }
192+
193+ QVariant submenuData = subMenuData(position);
194+ if (submenuData.type() == (int)QMetaType::QVariantList) {
195+ UnityMenuModel*& model = submenus[position];
196+ if (!model) {
197+ model = new UnityMenuModel(this);
198+ }
199+ if (model->modelData() != submenuData) {
200+ model->setModelData(submenuData);
201+ }
202+ return model;
203+ }
204+
205+ return NULL;
206+}
207+
208+bool UnityMenuModel::loadExtendedAttributes(int, const QVariantMap &)
209+{
210+ return false;
211+}
212+
213+QVariant UnityMenuModel::get(int row, const QByteArray &role)
214+{
215+ static QHash<QByteArray, int> roles;
216+ if (roles.isEmpty()) {
217+ QHash<int, QByteArray> names = roleNames();
218+ Q_FOREACH (int role, names.keys())
219+ roles.insert(names[role], role);
220+ }
221+
222+ return this->data(this->index(row, 0), roles[role]);
223+}
224+
225+void UnityMenuModel::activate(int, const QVariant&)
226+{
227+}
228+
229+void UnityMenuModel::changeState(int, const QVariant&)
230+{
231+}
232+
233+void UnityMenuModel::registerAction(UnityMenuAction*)
234+{
235+}
236+
237+void UnityMenuModel::unregisterAction(UnityMenuAction*)
238+{
239+}
tests/mocks/QMenuModel/unitymenumodel.hnew file+96
@@ -0,0 +1,96 @@
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+ * Authors: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#ifndef MOCK_UNITYMENUMODEL_H
20+#define MOCK_UNITYMENUMODEL_H
21+
22+#include <QAbstractListModel>
23+class QQmlComponent;
24+class UnityMenuAction;
25+class ActionStateParser;
26+
27+class Q_DECL_EXPORT UnityMenuModel : public QAbstractListModel
28+{
29+ Q_OBJECT
30+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
31+ Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged)
32+ Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged)
33+ Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged)
34+ Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged)
35+
36+public:
37+ UnityMenuModel(QObject *parent = NULL);
38+ virtual ~UnityMenuModel();
39+
40+ Q_INVOKABLE void insertRow(int row, const QVariant& data);
41+ Q_INVOKABLE void appendRow(const QVariant& data);
42+ Q_INVOKABLE void removeRow(int row);
43+
44+ QVariant modelData() const;
45+ void setModelData(const QVariant& data);
46+
47+ QByteArray busName() const;
48+ void setBusName(const QByteArray &busName);
49+
50+ QVariantMap actions() const;
51+ void setActions(const QVariantMap &actions);
52+
53+ QByteArray menuObjectPath() const;
54+ void setMenuObjectPath(const QByteArray &path);
55+
56+ ActionStateParser* actionStateParser() const;
57+ void setActionStateParser(ActionStateParser* actionStateParser);
58+
59+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
60+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
61+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
62+ QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
63+ QModelIndex parent(const QModelIndex &index) const;
64+ QHash<int, QByteArray> roleNames() const;
65+
66+ Q_INVOKABLE QObject * submenu(int position, QQmlComponent* actionStateParser = NULL);
67+ Q_INVOKABLE bool loadExtendedAttributes(int position, const QVariantMap &schema);
68+ Q_INVOKABLE QVariant get(int row, const QByteArray &role);
69+
70+ Q_INVOKABLE void activate(int index, const QVariant& parameter = QVariant());
71+ Q_INVOKABLE void changeState(int index, const QVariant& parameter);
72+
73+ void registerAction(UnityMenuAction* action);
74+ void unregisterAction(UnityMenuAction* action);
75+
76+Q_SIGNALS:
77+ void busNameChanged();
78+ void actionsChanged();
79+ void menuObjectPathChanged();
80+ void actionStateParserChanged();
81+ void modelDataChanged();
82+
83+private:
84+ QVariantMap rowData(int row) const;
85+ QVariant subMenuData(int row) const;
86+
87+ class Row;
88+ QVariantList m_modelData;
89+ QList<UnityMenuModel*> submenus;
90+
91+ QByteArray m_busName;
92+ QVariantMap m_actions;
93+ QByteArray m_menuObjectPath;
94+};
95+
96+#endif // MOCK_UNITYMENUMODEL_H
tests/mocks/Unity/Indicators/IndicatorBase.qmlnew file+37
@@ -0,0 +1,37 @@
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
22+
23+Item {
24+ id: indicatorBase
25+
26+ //const
27+ property string busName
28+ property string actionsObjectPath
29+ property string rootMenuType: "com.canonical.indicator.root"
30+ property string deviceMenuObjectPath
31+
32+ property var menuModel: UnityMenuModel {}
33+
34+ property RootActionState rootActionState: RootActionState {
35+ onUpdated: indicatorBase.rootActionStateChanged()
36+ }
37+}
plugins/Unity/Indicators/Messaging/qml/utils.js → tests/mocks/Unity/Indicators/IndicatorWidget.qmlrenamed+8−7
@@ -14,14 +14,15 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 *
1616 * Authors:
17− * Renato Araujo Oliveira Filho <renato@canonical.com>
18− * Olivier Tilloy <olivier.tilloy@canonical.com>
17+ * Nick Dedekind <nick.dedekind@canonical.com>
1918 */
2019
21−.pragma library
20+import QtQuick 2.0
21+import Unity.Indicators 0.1 as Indicators
2222
23−function formatDate(timestamp) {
24− // timestamp is in microseconds since epoch
25− var date = new Date(timestamp / 1000);
26− return Qt.formatDateTime(date, "hh:mm - MMM dd");
23+IndicatorBase {
24+ id: indicatorWidget
25+
26+ enabled: false
27+ property int iconSize: height
2728}
tests/mocks/Unity/Indicators/RootActionState.qml+17−9
@@ -20,14 +20,22 @@
2020import QtQuick 2.0
2121
2222QtObject {
23− property var menu
24− property bool valid: false
25− property string title
26− property string leftLabel
27− property string rightLabel
28− property var icons
29− property string accessibleName
30− property bool visible: true
23+ property var menu
24+ property bool valid: false
25+ property string title
26+ property string leftLabel
27+ property string rightLabel
28+ property var icons
29+ property string accessibleName
30+ property bool visible: true
3131
32− signal updated
32+ signal updated
33+
34+ onValidChanged: updated()
35+ onTitleChanged: updated()
36+ onLeftLabelChanged: updated()
37+ onRightLabelChanged: updated()
38+ onIconsChanged: updated()
39+ onAccessibleNameChanged: updated()
40+ onVisibleChanged: updated()
3341}
tests/mocks/Unity/Indicators/qmldir+2
@@ -2,6 +2,8 @@ module Unity.Indicators
22plugin IndicatorsFakeQml
33
44CachedUnityMenuModel 0.1 CachedUnityMenuModel.qml
5+IndicatorBase 0.1 IndicatorBase.qml
6+IndicatorWidget 0.1 IndicatorWidget.qml
57IndicatorsModel 0.1 IndicatorsModel.qml
68FakeMenuPage 0.1 FakeMenuPage.qml
79RootActionState 0.1 RootActionState.qml
tests/mocks/Unity/fake_preview.cpp+9−1
@@ -16,9 +16,13 @@
1616
1717#include "fake_preview.h"
1818
19+Preview::Preview(QObject *parent): QObject(parent)
20+{
21+}
22+
1923QString Preview::rendererName() const
2024{
21− return "foo";
25+ return "generic-preview";
2226}
2327
2428QString Preview::title() const
@@ -61,3 +65,7 @@ void Preview::execute(const QString& actionId, const QHash<QString, QVariant>& h
6165 Q_UNUSED(actionId);
6266 Q_UNUSED(hints);
6367}
68+
69+void Preview::cancelAction()
70+{
71+}
tests/mocks/Unity/fake_preview.h+2
@@ -51,6 +51,8 @@ public:
5151
5252 Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
5353
54+ Q_INVOKABLE void cancelAction();
55+
5456Q_SIGNALS:
5557 void previewChanged();
5658};
tests/mocks/Unity/fake_scope.cpp+21−17
@@ -14,31 +14,19 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−// Self
1817#include "fake_scope.h"
19−
20−#include <dee.h>
18+#include "fake_preview.h"
2119#include "paths.h"
2220
23−#include "fake_preview.h"
21+#include <dee.h>
2422
2523static DeeModel* create_categories_model(unsigned category_count);
2624static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
2725
2826// TODO: Implement remaining pieces
2927
30−Scope::Scope(QObject* parent)
31− : QObject(parent)
32− , m_visible(false)
33− , m_searching(false)
34− , m_isActive(false)
35− , m_categories(new Categories(this))
36− , m_results(new DeeListModel(this))
28+Scope::Scope(QObject* parent) : Scope(QString(), QString(), false, parent)
3729{
38− DeeModel* results_model = create_results_model(4, 30);
39− m_categories->setResultModel(results_model);
40− m_categories->setModel(create_categories_model(4));
41− m_results->setModel(results_model);
4230}
4331
4432Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent)
@@ -51,10 +39,17 @@ Scope::Scope(QString const& id, QString const& name, bool visible, QObject* pare
5139 , m_categories(new Categories(this))
5240 , m_results(new DeeListModel(this))
5341{
54− DeeModel* results_model = create_results_model(4, 30);
42+ DeeModel* results_model = create_results_model(20, 300);
5543 m_categories->setResultModel(results_model);
56− m_categories->setModel(create_categories_model(4));
44+ m_categories->setModel(create_categories_model(20));
5745 m_results->setModel(results_model);
46+
47+ m_timer.setInterval(1000);
48+ m_timer.setSingleShot(true);
49+ connect(&m_timer, &QTimer::timeout, [this]() {
50+ Preview *p = new Preview(this);
51+ Q_EMIT previewReady(p);
52+ });
5853}
5954
6055QString Scope::id() const {
@@ -168,6 +163,8 @@ void Scope::activate(const QVariant &uri, const QVariant &icon_hint, const QVari
168163 Q_UNUSED(comment);
169164 Q_UNUSED(dnd_uri);
170165 Q_UNUSED(metadata);
166+
167+ m_timer.start();
171168}
172169
173170void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
@@ -183,6 +180,8 @@ void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVaria
183180 Q_UNUSED(comment);
184181 Q_UNUSED(dnd_uri);
185182 Q_UNUSED(metadata);
183+
184+ m_timer.start();
186185}
187186
188187static const gchar * categories_model_schema[] = {
@@ -248,3 +247,8 @@ DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
248247 }
249248 return results_model;
250249}
250+
251+void Scope::cancelActivation()
252+{
253+ m_timer.stop();
254+}
tests/mocks/Unity/fake_scope.h+7−2
@@ -17,10 +17,11 @@
1717#ifndef FAKE_SCOPE_H
1818#define FAKE_SCOPE_H
1919
20−// Qt
21−#include <QObject>
2220#include "fake_categories.h"
2321
22+#include <QObject>
23+#include <QDebug>
24+
2425class Preview;
2526
2627class Scope : public QObject
@@ -78,6 +79,8 @@ public:
7879 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
7980 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
8081
82+ Q_INVOKABLE void cancelActivation();
83+
8184Q_SIGNALS:
8285 void idChanged(const QString&);
8386 void nameChanged(const QString&);
@@ -115,6 +118,8 @@ protected:
115118
116119 Categories* m_categories;
117120 DeeListModel* m_results;
121+
122+ QTimer m_timer;
118123};
119124
120125#endif // FAKE_SCOPE_H
tests/mocks/Unity/fake_unity_plugin.cpp+3
@@ -22,6 +22,7 @@
2222// local
2323#include "fake_scopes.h"
2424#include "fake_categories.h"
25+#include "fake_preview.h"
2526#include "categoryresults.h"
2627
2728// External
@@ -37,8 +38,10 @@ void FakeUnityPlugin::registerTypes(const char *uri)
3738
3839 Q_ASSERT(uri == QLatin1String("Unity"));
3940
41+ qmlRegisterUncreatableType<Preview>(uri, 0, 1, "Preview", "Can't create Preview object in QML.");
4042 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
4143 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
4244 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
4345 qmlRegisterUncreatableType<CategoryResults>(uri, 0, 1, "CategoryResults", "Can't create");
46+ qmlRegisterType<DeeListModel>(uri, 0, 1, "DeeListModel");
4447}
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp+20
@@ -1724,6 +1724,9 @@ private Q_SLOTS:
17241724
17251725 void testMaximizeVisibleAreaMoveUpAndShowHeader()
17261726 {
1727+#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
1728+ QSKIP("This test is extremely unstable in 5.0.x");
1729+#endif
17271730 model->setProperty(0, "size", 800);
17281731 verifyItem(0, 50., 800., false);
17291732
@@ -1824,6 +1827,23 @@ private Q_SLOTS:
18241827 lvwph->positionAtBeginning();
18251828 }
18261829
1830+ void testHeaderPositionBug1240118()
1831+ {
1832+ scrollToBottom();
1833+ lvwph->showHeader();
1834+ QTRY_VERIFY(!lvwph->m_contentYAnimation->isRunning());
1835+ QTest::qWait(100); // Make sure stuff is stable
1836+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 100));
1837+ model->setProperty(3, "size", 10);
1838+ model->setProperty(4, "size", 10);
1839+ model->setProperty(5, "size", 10);
1840+ model->setProperty(6, "size", 10);
1841+#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 3))
1842+ QTRY_COMPARE(lvwph->m_minYExtent, 210.);
1843+#endif
1844+ QTRY_COMPARE(lvwph->m_headerItem->y(), -lvwph->m_minYExtent);
1845+ }
1846+
18271847private:
18281848 QQuickView *view;
18291849 ListViewWithPageHeader *lvwph;
tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml+2−10
@@ -31,10 +31,7 @@ Item {
3131 y: followDragArea()
3232
3333 function followDragArea() {
34− if (dragArea.status === DirectionalDragArea.Rejected)
35− return -height
36− else
37− return dragArea.distance < height ? -height + dragArea.distance : 0
34+ return dragArea.distance < height ? -height + dragArea.distance : 0
3835 }
3936 }
4037
@@ -66,15 +63,10 @@ Item {
6663 dragAreaRect.opacity = 0.3
6764 launcher.y = Qt.binding(launcher.followDragArea)
6865 break;
69− case DirectionalDragArea.Recognized:
66+ defaut: // DirectionalDragArea.Recognized:
7067 dragAreaRect.color = "green"
7168 dragAreaRect.opacity = 0.5
7269 break;
73− default: //case DirectionalDragArea.Rejected:
74− dragAreaRect.color = "red"
75− dragAreaRect.opacity = 0.5
76− launcher.y = -launcher.height
77− break;
7870 }
7971 }
8072
tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml+5−13
@@ -32,13 +32,10 @@ Item {
3232 y: 0
3333
3434 function followDragArea() {
35− if (dragArea.status === DirectionalDragArea.Rejected)
36− return root.width
37− else
38− return dragArea.distance > -width ?
39− root.width + dragArea.distance
40− :
41− root.width - width
35+ return dragArea.distance > -width ?
36+ root.width + dragArea.distance
37+ :
38+ root.width - width
4239 }
4340 }
4441
@@ -69,15 +66,10 @@ Item {
6966 dragAreaRect.opacity = 0.3
7067 launcher.x = Qt.binding(launcher.followDragArea)
7168 break;
72− case DirectionalDragArea.Recognized:
69+ default: //case DirectionalDragArea.Recognized:
7370 dragAreaRect.color = "green"
7471 dragAreaRect.opacity = 0.5
7572 break;
76− default: //case DirectionalDragArea.Rejected:
77− dragAreaRect.color = "red"
78− dragAreaRect.opacity = 0.5
79− launcher.x = -launcher.height
80− break;
8173 }
8274 }
8375
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml+3−10
@@ -19,6 +19,7 @@ import Ubuntu.Gestures 0.1
1919import Ubuntu.Components 0.1
2020
2121Item {
22+ objectName: "rightwardsLauncher"
2223
2324 function reset() { launcher.x = -launcher.width }
2425
@@ -31,10 +32,7 @@ Item {
3132 y: 0
3233
3334 function followDragArea() {
34− if (dragArea.status === DirectionalDragArea.Rejected)
35− return -width
36− else
37− return dragArea.distance < width ? -width + dragArea.distance : 0
35+ return dragArea.distance < width ? -width + dragArea.distance : 0
3836 }
3937 }
4038
@@ -66,15 +64,10 @@ Item {
6664 dragAreaRect.opacity = 0.3
6765 launcher.x = Qt.binding(launcher.followDragArea)
6866 break;
69− case DirectionalDragArea.Recognized:
67+ default: //case DirectionalDragArea.Recognized:
7068 dragAreaRect.color = "green"
7169 dragAreaRect.opacity = 0.5
7270 break;
73− default: //case DirectionalDragArea.Rejected:
74− dragAreaRect.color = "red"
75− dragAreaRect.opacity = 0.5
76− launcher.x = -launcher.height
77− break;
7871 }
7972 }
8073
tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml+5−13
@@ -32,13 +32,10 @@ Item {
3232 y: root.height
3333
3434 function followDragArea() {
35− if (dragArea.status === DirectionalDragArea.Rejected)
36− return root.height
37− else
38− return dragArea.distance > -height ?
39− root.height + dragArea.distance
40− :
41− root.height - height
35+ return dragArea.distance > -height ?
36+ root.height + dragArea.distance
37+ :
38+ root.height - height
4239 }
4340 }
4441
@@ -69,15 +66,10 @@ Item {
6966 dragAreaRect.opacity = 0.3
7067 launcher.y = Qt.binding(launcher.followDragArea)
7168 break;
72− case DirectionalDragArea.Recognized:
69+ default: //case DirectionalDragArea.Recognized:
7370 dragAreaRect.color = "green"
7471 dragAreaRect.opacity = 0.5
7572 break;
76− default: //case DirectionalDragArea.Rejected:
77− dragAreaRect.color = "red"
78− dragAreaRect.opacity = 0.5
79− launcher.y = -launcher.height
80− break;
8173 }
8274 }
8375
tests/plugins/Ubuntu/Gestures/edgeDragExample.qml+7−1
@@ -33,7 +33,13 @@ Rectangle {
3333 }
3434 }
3535
36− RightwardsLauncher { id: hpLauncher; anchors.fill: parent }
36+ // NB: Do not anchor it as we will move it programmatically from the test
37+ RightwardsLauncher {
38+ id: hpLauncher;
39+ width: parent.width
40+ height: parent.height
41+ }
42+
3743 LeftwardsLauncher { id: hnLauncher; anchors.fill: parent }
3844 DownwardsLauncher { id: vpLauncher; anchors.fill: parent }
3945 UpwardsLauncher { id: vnLauncher; anchors.fill: parent }
tests/plugins/Ubuntu/Gestures/tst_AxisVelocityCalculator.cpp+3−3
@@ -54,12 +54,12 @@ private Q_SLOTS:
5454
5555private:
5656 AxisVelocityCalculator *velCalc;
57− FakeTimeSource *fakeTimeSource;
57+ QSharedPointer<FakeTimeSource> fakeTimeSource;
5858};
5959
6060void tst_AxisVelocityCalculator::init()
6161{
62− fakeTimeSource = new FakeTimeSource;
62+ fakeTimeSource.reset(new FakeTimeSource);
6363
6464 velCalc = new AxisVelocityCalculator;
6565 velCalc->setTimeSource(fakeTimeSource);
@@ -68,7 +68,7 @@ void tst_AxisVelocityCalculator::init()
6868void tst_AxisVelocityCalculator::cleanup()
6969{
7070 delete velCalc;
71− delete fakeTimeSource;
71+ fakeTimeSource.reset();
7272}
7373
7474void tst_AxisVelocityCalculator::simpleSamples()
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+285−96
@@ -19,23 +19,38 @@
1919#include <qpa/qwindowsysteminterface.h>
2020#include <QtQuick/QQuickView>
2121#include <QtQml/QQmlEngine>
22+#include <QPointer>
2223
2324#include <DirectionalDragArea.h>
2425
25−class FakeTimer : public UbuntuGestures::AbstractTimer
26+using namespace UbuntuGestures;
27+
28+class FakeTimer : public AbstractTimer
2629{
2730 Q_OBJECT
2831public:
29− FakeTimer(QObject *parent = 0)
30− : UbuntuGestures::AbstractTimer(parent)
32+ FakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
33+ : UbuntuGestures::AbstractTimer(parent),
34+ m_timeSource(timeSource)
3135 {}
3236
33− virtual int interval() const { return m_duration; }
34− virtual void setInterval(int msecs) { m_duration = msecs; }
37+ int interval() const override { return m_interval; }
38+ void setInterval(int msecs) override { m_interval = msecs; }
39+ void start() override {
40+ AbstractTimer::start();
41+ m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)m_interval;
42+ }
43+
44+ void emitTimeout() {
45+ m_nextTimeoutTime += m_interval;
46+ Q_EMIT timeout();
47+ }
3548
36− void emitTimeout() {Q_EMIT timeout();}
49+ qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
3750private:
38− int m_duration;
51+ int m_interval;
52+ SharedTimeSource m_timeSource;
53+ qint64 m_nextTimeoutTime;
3954};
4055
4156class FakeTimeSource : public UbuntuGestures::TimeSource
@@ -67,14 +82,17 @@ private Q_SLOTS:
6782 void maxSilenceTime();
6883 void sceneXAndX();
6984 void sceneYAndY();
70− void emitDraggingChangedOnDirectRecognition();
85+ void twoFingerTap();
86+ void movingDDA();
87+ void ignoreOldFinger();
7188
7289private:
7390 QQuickView *createView();
91+ void passTime(qint64 timeSpan);
7492 QQuickView *view;
7593 QTouchDevice *device;
7694 FakeTimer *fakeTimer;
77− FakeTimeSource *fakeTimeSource;
95+ QSharedPointer<FakeTimeSource> fakeTimeSource;
7896};
7997
8098void tst_DirectionalDragArea::initTestCase()
@@ -101,8 +119,8 @@ void tst_DirectionalDragArea::init()
101119 QVERIFY(view->rootObject() != 0);
102120 qApp->processEvents();
103121
104− fakeTimer = new FakeTimer;
105− fakeTimeSource = new FakeTimeSource;
122+ fakeTimeSource.reset(new FakeTimeSource);
123+ fakeTimer = new FakeTimer(fakeTimeSource);
106124}
107125
108126void tst_DirectionalDragArea::cleanup()
@@ -113,8 +131,7 @@ void tst_DirectionalDragArea::cleanup()
113131 delete fakeTimer;
114132 fakeTimer = 0;
115133
116− delete fakeTimeSource;
117− fakeTimeSource = 0;
134+ fakeTimeSource.reset();
118135}
119136
120137QQuickView *tst_DirectionalDragArea::createView()
@@ -127,6 +144,23 @@ QQuickView *tst_DirectionalDragArea::createView()
127144 return window;
128145}
129146
147+void tst_DirectionalDragArea::passTime(qint64 timeSpan)
148+{
149+ qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpan;
150+
151+ if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
152+ fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
153+ fakeTimer->emitTimeout();
154+
155+ qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
156+ if (timeSpanRemainder > 0) {
157+ passTime(timeSpanRemainder);
158+ }
159+ } else {
160+ fakeTimeSource->m_msecsSinceReference = finalTime;
161+ }
162+}
163+
130164namespace {
131165QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea, QQuickView *view)
132166{
@@ -180,7 +214,7 @@ void tst_DirectionalDragArea::edgeDrag()
180214 QFETCH(QString, dragAreaObjectName);
181215 QFETCH(qreal, wideningAngleMultiplier);
182216 QFETCH(qreal, dragDistanceFactor);
183− QFETCH(int, expectedGestureRecognition);
217+ QFETCH(bool, expectGestureRecognition);
184218
185219 DirectionalDragArea *edgeDragArea =
186220 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
@@ -196,7 +230,10 @@ void tst_DirectionalDragArea::edgeDrag()
196230 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
197231 QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
198232 wideningAngleMultiplier);
199− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
233+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
234+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
235+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
236+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
200237
201238 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
202239
@@ -211,15 +248,16 @@ void tst_DirectionalDragArea::edgeDrag()
211248 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
212249 }
213250
214− do {
251+ for (int i = 0; i < totalMovementSteps; ++i) {
215252 touchPoint += touchMovement;
253+ passTime(movementTimeStepMs);
216254 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
217− } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
255+ }
218256
219− if (expectedGestureRecognition)
257+ if (expectGestureRecognition)
220258 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
221259
222− if (edgeDragArea->status() == DirectionalDragArea::Rejected) {
260+ if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
223261 QCOMPARE(edgeDragArea->dragging(), false);
224262 QCOMPARE(draggingSpy.count(), 2);
225263 }
@@ -235,49 +273,49 @@ void tst_DirectionalDragArea::edgeDrag_data()
235273 QTest::addColumn<QString>("dragAreaObjectName");
236274 QTest::addColumn<qreal>("wideningAngleMultiplier");
237275 QTest::addColumn<qreal>("dragDistanceFactor");
238− QTest::addColumn<int>("expectedGestureRecognition");
276+ QTest::addColumn<bool>("expectGestureRecognition");
239277
240278 QTest::newRow("rightwards, tiny drag")
241− << "hpDragArea" << 0.0 << 0.2 << 0;
279+ << "hpDragArea" << 0.0 << 0.2 << false;
242280
243281 QTest::newRow("rightwards, straight drag")
244− << "hpDragArea" << 0.0 << 3.0 << 1;
282+ << "hpDragArea" << 0.0 << 3.0 << true;
245283
246284 QTest::newRow("rightwards, diagonal drag")
247− << "hpDragArea" << 0.9 << 3.0 << 1;
285+ << "hpDragArea" << 0.9 << 3.0 << true;
248286
249287 QTest::newRow("rightwards, overly diagonal drag")
250− << "hpDragArea" << 2.0 << 3.0 << 0;
288+ << "hpDragArea" << 2.0 << 3.0 << false;
251289
252290 QTest::newRow("leftwards, tiny drag")
253− << "hnDragArea" << 0.0 << 0.2 << 0;
291+ << "hnDragArea" << 0.0 << 0.2 << false;
254292
255293 QTest::newRow("leftwards, straight drag")
256− << "hnDragArea" << 0.0 << 3.0 << 1;
294+ << "hnDragArea" << 0.0 << 3.0 << true;
257295
258296 QTest::newRow("leftwards, diagonal drag")
259− << "hnDragArea" << 0.9 << 3.0 << 1;
297+ << "hnDragArea" << 0.9 << 3.0 << true;
260298
261299 QTest::newRow("downwards, tiny drag")
262− << "vpDragArea" << 0.0 << 0.2 << 0;
300+ << "vpDragArea" << 0.0 << 0.2 << false;
263301
264302 QTest::newRow("downwards, straight drag")
265− << "vpDragArea" << 0.0 << 3.0 << 1;
303+ << "vpDragArea" << 0.0 << 3.0 << true;
266304
267305 QTest::newRow("downwards, diagonal drag")
268− << "vpDragArea" << 0.9 << 3.0 << 1;
306+ << "vpDragArea" << 0.9 << 3.0 << true;
269307
270308 QTest::newRow("upwards, tiny drag")
271− << "vnDragArea" << 0.0 << 0.2 << 0;
309+ << "vnDragArea" << 0.0 << 0.2 << false;
272310
273311 QTest::newRow("upwards, straight drag")
274− << "vnDragArea" << 0.0 << 3.0 << 1;
312+ << "vnDragArea" << 0.0 << 3.0 << true;
275313
276314 QTest::newRow("upwards, diagonal drag")
277− << "vnDragArea" << 0.9 << 3.0 << 1;
315+ << "vnDragArea" << 0.9 << 3.0 << true;
278316
279317 QTest::newRow("upwards, overly diagonal drag")
280− << "vnDragArea" << 2.0 << 3.0 << 0;
318+ << "vnDragArea" << 2.0 << 3.0 << false;
281319}
282320
283321/*
@@ -298,25 +336,33 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
298336
299337 qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
300338 QPointF dragDirectionVector(1.0, 0.0);
301− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.1f);
339+ qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
340+ // make sure we are above minimum speed
341+ int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
342+ QPointF touchMovement = dragDirectionVector * touchStepDistance;
302343
303344 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
304345
305346 // Move a bit in the proper direction
306347 for (int i=0; i < 3; ++i) {
307348 touchPoint += touchMovement;
349+ passTime(touchStepTimeMs);
308350 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
309351 }
310352
311353 // Then a sudden and small movement to the opposite direction
312− touchPoint -= dragDirectionVector * (edgeDragArea->maxDeviation() * 0.7);
354+ touchPoint -= touchMovement*0.2;
355+ passTime(touchStepTimeMs*0.2);
313356 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
314357
315− // And then resume movment in the correct direction until it crosses the distance threshold.
358+ // And then resume movment in the correct direction until it crosses the distance and time
359+ // thresholds.
316360 do {
317361 touchPoint += touchMovement;
362+ passTime(touchStepTimeMs);
318363 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
319− } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance);
364+ } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
365+ || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
320366
321367 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
322368
@@ -329,7 +375,8 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
329375 */
330376void tst_DirectionalDragArea::minSpeed()
331377{
332− QFETCH(int, minSpeedMsecsDeviation);
378+ QFETCH(qreal, minSpeed);
379+ QFETCH(qreal, speed);
333380 QFETCH(int, expectedStatusAfterSpeedCheck);
334381
335382 DirectionalDragArea *edgeDragArea =
@@ -338,44 +385,40 @@ void tst_DirectionalDragArea::minSpeed()
338385 edgeDragArea->setRecognitionTimer(fakeTimer);
339386 edgeDragArea->setTimeSource(fakeTimeSource);
340387
388+ // A really long, unattainable, number. We don't want it getting recognized before
389+ // the speed checks we want have been performed
390+ edgeDragArea->setDistanceThreshold(500000);
391+
392+ edgeDragArea->setMinSpeed(minSpeed);
393+
341394 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
342395 QPointF touchPoint = initialTouchPos;
343396
344397 QPointF dragDirectionVector(1.0, 0.0);
345− qreal distanceStep = edgeDragArea->distanceThreshold() * 0.1f;
398+ qint64 timeStepMsecs = 10;
399+ qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
346400 QPointF touchMovement = dragDirectionVector * distanceStep;
347− qreal minSpeedMsecs = edgeDragArea->minSpeed() / 1000.0;
348− qint64 timeStepMsecs = qFloor(distanceStep / minSpeedMsecs) + minSpeedMsecsDeviation;
349401
350− // if it fails it means the params set in the QML file are not in harmony with the values
351− // used in this test
352− Q_ASSERT(timeStepMsecs > 0);
353−
354− fakeTimeSource->m_msecsSinceReference = 0;
355402 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
356403
357− // Move a bit in the proper direction
358− for (int i=0; i < 4; ++i) {
404+ // Move for a while to ensure our speed check is performed a couple of times
405+ for (int i=0; i < 20; ++i) {
359406 touchPoint += touchMovement;
360− fakeTimeSource->m_msecsSinceReference += timeStepMsecs;
407+ passTime(timeStepMsecs);
361408 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
362409 }
363410
364− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
365−
366− // Force the periodic speed check.
367− fakeTimer->emitTimeout();
368−
369411 QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
370412}
371413
372414void tst_DirectionalDragArea::minSpeed_data()
373415{
374− QTest::addColumn<int>("minSpeedMsecsDeviation");
416+ QTest::addColumn<qreal>("minSpeed");
417+ QTest::addColumn<qreal>("speed");
375418 QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
376419
377− QTest::newRow("slower than minSpeed") << 20 << (int)DirectionalDragArea::Rejected;
378− QTest::newRow("faster than minSpeed") << -20 << (int)DirectionalDragArea::Undecided;
420+ QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
421+ QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
379422}
380423
381424/*
@@ -391,32 +434,37 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
391434 edgeDragArea->setRecognitionTimer(fakeTimer);
392435 edgeDragArea->setTimeSource(fakeTimeSource);
393436
437+ // don't let it interfere with our test
438+ edgeDragArea->setMinSpeed(0.0);
439+
440+ int timeStepMs = 5; // some arbitrary small value.
441+
394442 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
395443 QPointF touchPoint = initialTouchPos;
396444
397445 QPointF dragDirectionVector(1.0, 0.0);
398446 QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
399447
448+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
400449 QVERIFY(!fakeTimer->isRunning());
401450
402451 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
403452
453+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
404454 QVERIFY(fakeTimer->isRunning());
405455
406− // Move a bit in the proper direction
407− for (int i=0; i < 3; ++i) {
456+ // Move beyond distance threshold and composition time to ensure recognition
457+ while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
458+ (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
459+
460+ QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
461+
408462 touchPoint += touchMovement;
463+ passTime(timeStepMs);
409464 QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
410465 }
411466
412− QVERIFY(fakeTimer->isRunning());
413−
414− // Move beyond distance threshold
415− touchPoint += 3*touchMovement;
416− QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
417−
418467 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
419−
420468 QVERIFY(!fakeTimer->isRunning());
421469}
422470
@@ -432,29 +480,23 @@ void tst_DirectionalDragArea::maxSilenceTime()
432480 edgeDragArea->setRecognitionTimer(fakeTimer);
433481 edgeDragArea->setTimeSource(fakeTimeSource);
434482
483+ // Make sure this property is not disabled
484+ edgeDragArea->setMaxSilenceTime(100);
485+
435486 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
436487 QPointF touchPoint = initialTouchPos;
437488
438− fakeTimeSource->m_msecsSinceReference = 0;
439489 QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
440490
441491 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
492+ QVERIFY(fakeTimer->isRunning());
442493
443− int neededTimeouts = qCeil((qreal)edgeDragArea->maxSilenceTime() / (qreal)fakeTimer->interval());
444− if ((edgeDragArea->maxSilenceTime() % fakeTimer->interval()) == 0) {
445− ++neededTimeouts;
446− }
447−
448− // Force the periodic speed check.
449− for (int i = 0; i < neededTimeouts; ++i) {
450− fakeTimer->emitTimeout();
451−
452− if (i < neededTimeouts - 1) {
453− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
454− }
494+ // Force timer to timeout until after maxSilenceTime has been reached
495+ while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
496+ passTime(fakeTimer->interval());
455497 }
456498
457− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Rejected);
499+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
458500}
459501
460502/*
@@ -471,7 +513,6 @@ void tst_DirectionalDragArea::sceneXAndX()
471513
472514 QPointF touchScenePos(view->width() - (edgeDragArea->width()/2.0f), view->height()/2.0f);
473515
474− fakeTimeSource->m_msecsSinceReference = 0;
475516 QTest::touchEvent(view, device).press(0, touchScenePos.toPoint());
476517
477518 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
@@ -500,7 +541,6 @@ void tst_DirectionalDragArea::sceneYAndY()
500541
501542 QPointF touchScenePos(view->width()/2.0f, view->height() - (edgeDragArea->height()/2.0f));
502543
503− fakeTimeSource->m_msecsSinceReference = 0;
504544 QTest::touchEvent(view, device).press(0, touchScenePos.toPoint());
505545
506546 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
@@ -516,11 +556,12 @@ void tst_DirectionalDragArea::sceneYAndY()
516556}
517557
518558/*
519− Emit draggingChanged() when status change from WaitingForTouch directly to Recognized.
520− This happens when the gesture recognition is effectively disabled (e.g. by setting a
521− distanceThreshold of zero)
559+ Regression test for https://bugs.launchpad.net/bugs/1228336
560+
561+ Perform a quick two-finger double-tap and check that DirectionalDragArea properly
562+ rejects those touch points. In the bug above it got confused and crashed.
522563 */
523−void tst_DirectionalDragArea::emitDraggingChangedOnDirectRecognition()
564+void tst_DirectionalDragArea::twoFingerTap()
524565{
525566 DirectionalDragArea *edgeDragArea =
526567 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
@@ -528,22 +569,170 @@ void tst_DirectionalDragArea::emitDraggingChangedOnDirectRecognition()
528569 edgeDragArea->setRecognitionTimer(fakeTimer);
529570 edgeDragArea->setTimeSource(fakeTimeSource);
530571
531− QPointF touchPos = calculateInitialTouchPos(edgeDragArea, view);
572+ // Make touches evenly spaced along the edgeDragArea
573+ QPoint touchAPos(edgeDragArea->width()/2.0f, view->height()*0.33f);
574+ QPoint touchBPos(edgeDragArea->width()/2.0f, view->height()*0.66f);
532575
533− QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
576+ qint64 timeStepMsecs = 5; // some arbitrary, small value
534577
535− // That will make it completetly skip the recognition phase ("Undecided" status).
536− edgeDragArea->setDistanceThreshold(0.0);
578+ // Perform the first two-finger tap
579+ // NB: using move() instead of stationary() becasue in the latter you cannot provide
580+ // the touch position and therefore it's left with some garbage numbers.
581+ QTest::touchEvent(view, device)
582+ .press(0, touchAPos);
537583
584+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
585+
586+ passTime(timeStepMsecs);
587+ QTest::touchEvent(view, device)
588+ .move(0, touchAPos)
589+ .press(1, touchBPos);
590+
591+ // A second touch point appeared during recognition, reject immediately as this
592+ // can't be a single-touch gesture anymore.
538593 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
539− QCOMPARE(edgeDragArea->dragging(), false);
540594
541− fakeTimeSource->m_msecsSinceReference = 0;
542− QTest::touchEvent(view, device).press(0, touchPos.toPoint());
595+ passTime(timeStepMsecs);
596+ QTest::touchEvent(view, device)
597+ .release(0, touchAPos)
598+ .move(1, touchBPos);
599+
600+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
601+
602+ passTime(timeStepMsecs);
603+ QTest::touchEvent(view, device)
604+ .release(1, touchBPos);
605+
606+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
607+
608+ // Perform the second two-finger tap
609+
610+ passTime(timeStepMsecs);
611+ QTest::touchEvent(view, device)
612+ .press(0, touchAPos);
613+
614+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
615+
616+ passTime(timeStepMsecs);
617+ QTest::touchEvent(view, device)
618+ .move(0, touchAPos)
619+ .press(1, touchBPos);
620+
621+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
622+
623+ passTime(timeStepMsecs);
624+ QTest::touchEvent(view, device)
625+ .release(0, touchAPos)
626+ .move(1, touchBPos);
627+
628+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
629+
630+ passTime(timeStepMsecs);
631+ QTest::touchEvent(view, device)
632+ .release(1, touchBPos);
633+
634+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
635+}
636+
637+/*
638+ Tests that gesture recognition works normally even if the DirectionalDragArea moves
639+ during recognition.
640+ This effectively means that we have to do gesture recognition with scene coordinates
641+ instead of local item coordinates.
642+ */
643+void tst_DirectionalDragArea::movingDDA()
644+{
645+ QQuickItem *rightwardsLauncher = view->rootObject()->findChild<QQuickItem*>("rightwardsLauncher");
646+ Q_ASSERT(rightwardsLauncher != 0);
647+
648+ DirectionalDragArea *edgeDragArea =
649+ rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
650+ Q_ASSERT(edgeDragArea != 0);
651+ edgeDragArea->setRecognitionTimer(fakeTimer);
652+ edgeDragArea->setTimeSource(fakeTimeSource);
653+
654+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
655+ QPointF touchPoint = initialTouchPos;
656+
657+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
658+ QPointF dragDirectionVector(1.0f, 0.0f);
659+
660+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
661+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
662+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
663+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
664+
665+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
666+
667+ // Move it far ahead along the direction of the gesture
668+ // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
669+ rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
670+
671+ for (int i = 0; i < totalMovementSteps; ++i) {
672+ touchPoint += touchMovement;
673+ passTime(movementTimeStepMs);
674+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
675+ }
543676
544677 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
545− QCOMPARE(draggingSpy.count(), 1);
546− QCOMPARE(edgeDragArea->dragging(), true);
678+
679+ QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
680+}
681+
682+/*
683+ The presence of and old, rejected, touch point lying on the DirectionalDragArea
684+ shouldn't impede the recognition of a gesture from a new touch point.
685+ */
686+void tst_DirectionalDragArea::ignoreOldFinger()
687+{
688+ DirectionalDragArea *edgeDragArea =
689+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
690+ Q_ASSERT(edgeDragArea != 0);
691+ edgeDragArea->setRecognitionTimer(fakeTimer);
692+ edgeDragArea->setTimeSource(fakeTimeSource);
693+
694+ // Make touches evenly spaced along the edgeDragArea
695+ QPoint touch0Pos(edgeDragArea->width()/2.0f, view->height()*0.33f);
696+ QPoint touch1Pos(edgeDragArea->width()/2.0f, view->height()*0.66f);
697+
698+ QTest::touchEvent(view, device).press(0, touch0Pos);
699+
700+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
701+
702+ // leave it lying around for some time
703+ passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
704+
705+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
706+
707+ qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
708+ QPointF dragDirectionVector(1.0f, 0.0f);
709+
710+ qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
711+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
712+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
713+ int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
714+
715+ QTest::touchEvent(view, device)
716+ .move(0, touch0Pos)
717+ .press(1, touch1Pos);
718+
719+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
720+
721+ for (int i = 0; i < totalMovementSteps; ++i) {
722+ touch1Pos += touchMovement.toPoint();
723+ passTime(movementTimeStepMs);
724+ QTest::touchEvent(view, device)
725+ .move(0, touch0Pos)
726+ .move(1, touch1Pos);
727+ }
728+
729+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
730+
731+ QTest::touchEvent(view, device)
732+ .move(0, touch0Pos)
733+ .release(1, touch1Pos);
734+
735+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
547736}
548737
549738QTEST_MAIN(tst_DirectionalDragArea)
tests/plugins/Unity/Indicators/CMakeLists.txt+36−26
@@ -1,33 +1,43 @@
1+pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
2+pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
13pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
24
5+set(INDICATORS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../plugins/Unity/Indicators)
6+set(TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
7+
38include_directories(
49 ${CMAKE_CURRENT_BINARY_DIR}
5− ${CMAKE_CURRENT_SOURCE_DIR}/../../../../plugins/Unity/Indicators
6− ${QMENUMODEL_INCLUDE_DIRS}
10+ ${INDICATORS_DIR}
11+ ${TEST_DIR}/mocks/QMenuModel
12+ ${GLIB_INCLUDE_DIRS}
13+ ${GIO_INCLUDE_DIRS}
714 )
815
9−macro(run_tests)
10− set(_test_list "")
11− foreach(_test ${ARGN})
12− set(testCommand ${CMAKE_CURRENT_BINARY_DIR}/${_test}Exec
13− -o ${CMAKE_BINARY_DIR}/${_test}.xml,xunitxml
14− -o -,txt)
15− add_test(NAME ${_test} COMMAND ${testCommand})
16− add_custom_target(${_test} ${testCommand})
17− add_executable(${_test}Exec ${_test}.cpp
18− )
19− qt5_use_modules(${_test}Exec Test Core Qml)
20− target_link_libraries(${_test}Exec
21− IndicatorsQml
22− ${QMENUMODEL_LDFLAGS}
23− )
24− set(_test_list "${_test_list};${_test}")
25− endforeach()
26−endmacro(run_tests)
16+macro(indicator_test TEST)
17+ set(multi_value_keywords ADDITIONAL_CPPS ADDITIONAL_LIBS)
18+ cmake_parse_arguments(test "" "" "${multi_value_keywords}" ${ARGN})
2719
28−run_tests(
29− indicatorsmanagertest
30− indicatorsmodeltest
31− menucontentactivatortest
32− rootactionstatetest
33− )
20+ set(testCommand ${CMAKE_CURRENT_BINARY_DIR}/${TEST}Exec
21+ -o ${CMAKE_BINARY_DIR}/${TEST}.xml,xunitxml
22+ -o -,txt)
23+ add_test(NAME ${TEST} COMMAND ${testCommand})
24+ add_custom_target(${TEST} ${testCommand})
25+ add_executable(${TEST}Exec
26+ ${TEST}.cpp
27+ ${test_ADDITIONAL_CPPS}
28+ )
29+ qt5_use_modules(${TEST}Exec Test Core Qml)
30+ target_link_libraries(${TEST}Exec
31+ ${test_ADDITIONAL_LIBS}
32+ ${GLIB_LIBRARIES}
33+ ${GIO_LIBRARIES}
34+ ${QMENUMODEL_LDFLAGS}
35+ )
36+endmacro(indicator_test)
37+
38+indicator_test(indicatorsmanagertest ADDITIONAL_LIBS IndicatorsQml)
39+indicator_test(indicatorsmodeltest ADDITIONAL_LIBS IndicatorsQml)
40+indicator_test(menucontentactivatortest ADDITIONAL_CPPS ${INDICATORS_DIR}/menucontentactivator.cpp)
41+indicator_test(unitymenumodelstacktest ADDITIONAL_CPPS ${TEST_DIR}/mocks/QMenuModel/unitymenumodel.cpp ${INDICATORS_DIR}/unitymenumodelstack.cpp)
42+indicator_test(rootactionstatetest ADDITIONAL_LIBS IndicatorsQml)
43+indicator_test(timeformattertest ADDITIONAL_LIBS IndicatorsQml)
tests/plugins/Unity/Indicators/timeformattertest.cppnew 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+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17+ */
18+
19+#include "timeformatter.h"
20+
21+#include <QtTest>
22+#include <QDebug>
23+
24+class TimeFormatterTest : public QObject
25+{
26+ Q_OBJECT
27+private Q_SLOTS:
28+
29+ void initTestCase()
30+ {
31+ setenv("UNITY_TEST_ENV", "1", 1);
32+ }
33+
34+ void cleanupTestCase()
35+ {
36+ unsetenv("UNITY_TEST_ENV");
37+ }
38+
39+ void testFormat()
40+ {
41+ const QString format = "hh:mm dd.MM.yy";
42+
43+ QDateTime time = QDateTime::currentDateTime();
44+
45+ TimeFormatter formatter;
46+ formatter.setTime(time.toMSecsSinceEpoch() * 1000);
47+ formatter.setFormat(format);
48+
49+ QCOMPARE(formatter.timeString(), time.toString(format));
50+ }
51+};
52+
53+QTEST_GUILESS_MAIN(TimeFormatterTest)
54+
55+#include "timeformattertest.moc"
tests/plugins/Unity/Indicators/unitymenumodelstacktest.cppnew file+206
@@ -0,0 +1,206 @@
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+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "indicatorsmanager.h"
21+#include "paths.h"
22+#include "unitymenumodelstack.h"
23+
24+#include <QtTest>
25+#include <QDebug>
26+#include <unitymenumodel.h>
27+
28+class UnityMenuModelStackTest : public QObject
29+{
30+ Q_OBJECT
31+private Q_SLOTS:
32+
33+ void init()
34+ {
35+ m_model = new UnityMenuModel();
36+ m_headChanged = false;
37+ m_tailChanged = false;
38+ }
39+
40+ void cleanup()
41+ {
42+ delete m_model;
43+ m_model = NULL;
44+ }
45+
46+ void testHeadOnSetHead()
47+ {
48+ UnityMenuModelStack stack;
49+ connect(&stack, SIGNAL(headChanged(UnityMenuModel*)), this, SLOT(onHeadChanged()));
50+ stack.setHead(m_model);
51+
52+ QCOMPARE(stack.head(), m_model);
53+ QCOMPARE(m_headChanged, true);
54+ }
55+
56+ void testTailOnSetHead()
57+ {
58+ UnityMenuModelStack stack;
59+ connect(&stack, SIGNAL(tailChanged(UnityMenuModel*)), this, SLOT(onTailChanged()));
60+ stack.setHead(m_model);
61+
62+ QCOMPARE(stack.tail(), m_model);
63+ QCOMPARE(m_tailChanged, true);
64+ }
65+
66+ void testPushPop_data() {
67+ QTest::addColumn<int>("menuDepth");
68+ QTest::addColumn<int>("subMenuCount");
69+ QTest::addColumn<int>("subMenuIndex");
70+
71+ QTest::newRow("depth=0") << 0 << 1 << 0;
72+ QTest::newRow("depth=1") << 1 << 1 << 0;
73+ QTest::newRow("depth=8") << 8 << 2 << 1;
74+ }
75+
76+ void testPushPop()
77+ {
78+ QFETCH(int, menuDepth);
79+ QFETCH(int, subMenuCount);
80+ QFETCH(int, subMenuIndex);
81+
82+ m_model->setModelData(recuseAddMenu(subMenuCount, menuDepth));
83+
84+ UnityMenuModelStack stack;
85+ connect(&stack, SIGNAL(tailChanged(UnityMenuModel*)), this, SLOT(onTailChanged()));
86+
87+ QList<UnityMenuModel*> models;
88+
89+ int count = 0;
90+ auto foreachChild = [&](UnityMenuModel* child, int childIndex) {
91+ stack.push(child, childIndex);
92+ QCOMPARE(stack.count(), count+1);
93+
94+ count++;
95+ models << child;
96+ };
97+ recuseSubmenus(m_model, subMenuIndex, foreachChild);
98+
99+ QCOMPARE(stack.count(), models.count());
100+ QCOMPARE(stack.count(), menuDepth+1);
101+ while(stack.count() > 0) {
102+ m_tailChanged = false;
103+
104+ QCOMPARE(stack.pop(), models.takeLast());
105+
106+ QCOMPARE(m_tailChanged, true);
107+ if (stack.count()) {
108+ QCOMPARE(stack.tail(), models.last());
109+ }
110+ }
111+ QCOMPARE(m_tailChanged, true);
112+ }
113+
114+ void testPopOnRemove_data() {
115+ QTest::addColumn<int>("menuDepth");
116+ QTest::addColumn<int>("subMenuCount");
117+ QTest::addColumn<int>("subMenuIndex");
118+ QTest::addColumn<int>("removeIndex");
119+ QTest::addColumn<int>("resultCount");
120+
121+ QTest::newRow("removeIndexBefore") << 4 << 2 << 1 << 0 << 5;
122+ QTest::newRow("removeCurrentIndex") << 4 << 2 << 0 << 0 << 1;
123+ QTest::newRow("removeIndexAfter") << 4 << 2 << 0 << 1 << 5;
124+ }
125+
126+ void testPopOnRemove()
127+ {
128+ QFETCH(int, menuDepth);
129+ QFETCH(int, subMenuCount);
130+ QFETCH(int, subMenuIndex);
131+ QFETCH(int, removeIndex);
132+ QFETCH(int, resultCount);
133+
134+ m_model->setModelData(recuseAddMenu(subMenuCount, menuDepth));
135+
136+ UnityMenuModelStack stack;
137+
138+ auto foreachChild = [&](UnityMenuModel* child, int childIndex) {
139+ stack.push(child, childIndex);
140+ };
141+ recuseSubmenus(m_model, subMenuIndex, foreachChild);
142+
143+ QCOMPARE(stack.count(), menuDepth+1);
144+
145+ m_model->removeRow(removeIndex);
146+
147+ waitFor([&stack, resultCount]() { return stack.count() == resultCount; }, 1000);
148+ QCOMPARE(stack.count(), resultCount);
149+ }
150+
151+private:
152+ bool waitFor(std::function<bool()> functor, int ms) {
153+
154+ QElapsedTimer timer;
155+ timer.start();
156+ while(!functor() && timer.elapsed() < ms) { QTest::qWait(10); }
157+ return functor();
158+ }
159+
160+ QVariant recuseAddMenu(int subMenuCount, int depth_remaining)
161+ {
162+ QVariantList rows;
163+
164+ for (int i = 0; i < subMenuCount; i ++) {
165+ QVariantMap row;
166+ QVariantMap rowData;
167+
168+ if (depth_remaining > 0) {
169+ row["submenu"] = recuseAddMenu(subMenuCount, depth_remaining-1);
170+ }
171+
172+ row["rowData"] = rowData;
173+
174+ rows << row;
175+ }
176+
177+ return rows;
178+ }
179+
180+ void recuseSubmenus(UnityMenuModel* model, int childIndex, std::function<void(UnityMenuModel*, int)> func) {
181+ UnityMenuModel* parent = model;
182+ UnityMenuModel* child = model;
183+
184+ while(child) {
185+ if (func) {
186+ func(child, childIndex);
187+ }
188+
189+ parent = child;
190+ child = qobject_cast<UnityMenuModel*>(parent->submenu(childIndex));
191+ }
192+
193+ }
194+
195+private Q_SLOTS:
196+ void onHeadChanged() { m_headChanged = true; }
197+ void onTailChanged() { m_tailChanged = true; }
198+
199+public:
200+ UnityMenuModel* m_model;
201+ bool m_headChanged;
202+ bool m_tailChanged;
203+};
204+
205+QTEST_GUILESS_MAIN(UnityMenuModelStackTest)
206+#include "unitymenumodelstacktest.moc"
tests/qmltests/CMakeLists.txt+5−3
@@ -13,11 +13,9 @@ set(qmltest_DEFAULT_NO_ADD_TEST FALSE)
1313set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
1414add_qml_test(Components AnimationControllerWithSignals)
1515add_qml_test(Components Carousel)
16−add_qml_test(Components MathLocal)
1716add_qml_test(Components OpenEffect)
1817add_qml_test(Components RatingStars)
1918add_qml_test(Components TimeLocal)
20−add_qml_test(Greeter Clock IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
2119add_qml_test(Panel IndicatorItem)
2220add_qml_test(utils/Unity/Test UnityTest)
2321
@@ -61,15 +59,19 @@ add_qml_test(Greeter Phone IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DE
6159 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
6260add_qml_test(Greeter Infographics IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
6361 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
62+add_qml_test(Greeter Clock IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
63+ ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
6464add_qml_test(Hud Hud)
6565add_qml_test(Hud Result)
6666add_qml_test(Launcher Launcher IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
67−add_qml_test(Notifications Notifications IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_SOURCE_DIR}/tests/mocks)
67+add_qml_test(Notifications Notifications IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
6868add_qml_test(Panel IndicatorRow)
6969add_qml_test(Panel Indicators IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
7070add_qml_test(Panel MenuContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
7171add_qml_test(Panel Panel IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
7272add_qml_test(Panel SearchIndicator)
73+add_qml_test(Panel/Indicators DefaultIndicatorWidget IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
74+ ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
7375add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
7476 ${CMAKE_BINARY_DIR}/tests/mocks
7577 ${CMAKE_BINARY_DIR}/plugins)
tests/qmltests/Components/tst_DragHandle.cpp+4−5
@@ -41,7 +41,7 @@ class FakeTimeSource : public UbuntuGestures::TimeSource
4141{
4242public:
4343 FakeTimeSource() { m_msecsSinceReference = 0; }
44− virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
44+ qint64 msecsSinceReference() override {return m_msecsSinceReference;}
4545 qint64 m_msecsSinceReference;
4646};
4747
@@ -73,7 +73,7 @@ private:
7373 QQuickView *m_view;
7474 QTouchDevice *m_device;
7575 FakeTimer *m_fakeTimer;
76− FakeTimeSource *m_fakeTimeSource;
76+ QSharedPointer<FakeTimeSource> m_fakeTimeSource;
7777};
7878
7979
@@ -102,7 +102,7 @@ void tst_DragHandle::init()
102102 qApp->processEvents();
103103
104104 m_fakeTimer = new FakeTimer;
105− m_fakeTimeSource = new FakeTimeSource;
105+ m_fakeTimeSource.reset(new FakeTimeSource);
106106}
107107
108108void tst_DragHandle::cleanup()
@@ -113,8 +113,7 @@ void tst_DragHandle::cleanup()
113113 delete m_fakeTimer;
114114 m_fakeTimer = 0;
115115
116− delete m_fakeTimeSource;
117− m_fakeTimeSource = 0;
116+ m_fakeTimeSource.reset();
118117}
119118
120119QQuickView *tst_DragHandle::createView()
tests/qmltests/Components/tst_EdgeDemoOverlay.qml+2−2
@@ -112,8 +112,8 @@ Item {
112112 // Test that the 'none' edge skips anywhere
113113 signalSpy.target = none
114114 signalSpy.clear()
115− var backgroundShade = findChild(none, "backgroundShade")
116− tryCompare(none, "opacity", 1)
115+ var backgroundShade = findChild(none, "backgroundShadeMouseArea")
116+ tryCompare(backgroundShade, "enabled", true)
117117 mousePress(backgroundShade, 1, 1)
118118 mouseRelease(backgroundShade, 1, 1)
119119 signalSpy.wait()
tests/qmltests/Components/tst_MathLocal.qmldeleted−91
@@ -1,91 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import "../../../Components/Math.js" as MathLocal
20−
21−TestCase {
22− name: "MathLocal"
23−
24− property int minValue
25− property int maxValue
26− property int clampValue
27− property int clamped
28−
29− function test_clamp_positive_lower() {
30− minValue = 9
31− maxValue = 42
32− clampValue = -7
33−
34− clamped = MathLocal.clamp(clampValue, minValue, maxValue)
35− compare(clamped, minValue, "clamped value not within range")
36− }
37−
38− function test_clamp_positive_greater() {
39− minValue = 9
40− maxValue = 42
41− clampValue = 111
42−
43− clamped = MathLocal.clamp(clampValue, minValue, maxValue)
44− compare(clamped, maxValue, "clamped value not within range")
45− }
46−
47− function test_clamp_positive_within() {
48− minValue = 9
49− maxValue = 53
50− clampValue = 42
51−
52− clamped = MathLocal.clamp(clampValue, minValue, maxValue)
53− compare(clamped, clampValue, "clamped value changed even though it shouldn't have")
54− }
55−
56− function test_clamp_positive_on_border() {
57− minValue = 9
58− maxValue = 42
59− clampValue = 9
60−
61− clamped = MathLocal.clamp(clampValue, minValue, maxValue)
62− compare(clamped, clampValue, "clamped value changed even though it shouldn't have")
63− }
64−
65− function test_clamp_negative_lower() {
66− minValue = -42
67− maxValue = -9
68− clampValue = -50
69−
70− clamped = MathLocal.clamp(clampValue, minValue, maxValue)
71− compare(clamped, minValue, "clamped value not within range")
72− }
73−
74− function test_clamp_negative_greater() {
75− minValue = -42
76− maxValue = -9
77− clampValue = 50
78−
79− clamped = MathLocal.clamp(clampValue, minValue, maxValue)
80− compare(clamped, maxValue, "clamped value not within range")
81− }
82−
83− function test_clamp_postive_and_negative_greater() {
84− minValue = -42
85− maxValue = 9
86− clampValue = 50
87−
88− clamped = MathLocal.clamp(clampValue, minValue, maxValue)
89− compare(clamped, maxValue, "clamped value not within range")
90− }
91−}
tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml+1
@@ -226,6 +226,7 @@ Item {
226226 verify(fakeRunningAppsModel.contains("calendar"))
227227
228228 mouseClick(calendarTileCloseButton, calendarTileCloseButton.width/2, calendarTileCloseButton.height/2)
229+ wait(0) // spin event loop to start any pending animation
229230
230231 verify(!fakeRunningAppsModel.contains("calendar"))
231232
tests/qmltests/Dash/qml/FakeScopeView.qml+1
@@ -23,6 +23,7 @@ ScopeView {
2323 id: fakeScopeView
2424
2525 property alias backColor : back.color
26+ moving: listView.moving
2627
2728 onEndReached: {
2829 if (shell != undefined && shell.scopeStatus != undefined) {
tests/qmltests/Dash/tst_Dash.qml+2
@@ -103,6 +103,8 @@ Item {
103103 return;
104104 }
105105 dash.showScopeOnLoaded = data.tag
106+ scopes.clear();
107+ scopes.load();
106108
107109 var dashContentList = findChild(dash, "dashContentList");
108110 verify(dashContentList != undefined);
tests/qmltests/Dash/tst_DashContent.qml+14
@@ -229,5 +229,19 @@ Item {
229229 tryCompare(scopesModel.get(1), "isActive", data.active1);
230230 tryCompare(scopesModel.get(2), "isActive", data.active2);
231231 }
232+
233+ function test_hswipe_disabled_vswipe() {
234+ var dashContentList = findChild(dashContent, "dashContentList");
235+
236+ tryCompare(dashContentList, "interactive", true);
237+
238+ var startX = dashContentList.width/2;
239+ var startY = dashContentList.height/2;
240+ touchFlick(dashContentList, startX, startY, startX, startY - units.gu(800));
241+
242+ tryCompare(dashContentList.currentItem, "moving", true);
243+
244+ compare(dashContentList.interactive, false);
245+ }
232246 }
233247}
tests/qmltests/Dash/tst_GenericScopeView.qml+126−13
@@ -23,11 +23,21 @@ import Ubuntu.Components 0.1
2323import Unity.Test 0.1 as UT
2424
2525Item {
26+ id: shell
2627 width: units.gu(120)
2728 height: units.gu(80)
2829
2930 Scopes {
3031 id: scopes
32+
33+ onLoadedChanged: {
34+ genericScopeView.scope = scopes.get(0)
35+ }
36+ }
37+
38+ property Item applicationManager: Item {
39+ signal sideStageFocusedApplicationChanged()
40+ signal mainStageFocusedApplicationChanged()
3141 }
3242
3343 GenericScopeView {
@@ -38,44 +48,147 @@ Item {
3848 name: "GenericScopeView"
3949 when: scopes.loaded
4050
41− function init() {
42− genericScopeView.scope = scopes.get(0)
43− }
44−
4551 function test_isCurrent() {
4652 var pageHeader = findChild(genericScopeView, "pageHeader");
47− var previewLoader = findChild(genericScopeView, "previewLoader");
53+ var previewListView = findChild(genericScopeView, "previewListView");
4854 genericScopeView.isCurrent = true
4955 pageHeader.searchQuery = "test"
50− previewLoader.open = true
56+ previewListView.open = true
5157 genericScopeView.isCurrent = false
5258 tryCompare(pageHeader, "searchQuery", "")
5359 tryCompare(genericScopeView, "previewShown", false);
5460 }
5561
5662 function test_showDash() {
57− var previewLoader = findChild(genericScopeView, "previewLoader");
58− previewLoader.open = true;
63+ var previewListView = findChild(genericScopeView, "previewListView");
64+ previewListView.open = true;
5965 scopes.get(0).showDash();
6066 tryCompare(genericScopeView, "previewShown", false);
6167 }
6268
6369 function test_hideDash() {
64− var previewLoader = findChild(genericScopeView, "previewLoader");
65− previewLoader.open = true;
70+ var previewListView = findChild(genericScopeView, "previewListView");
71+ previewListView.open = true;
6672 scopes.get(0).hideDash();
6773 tryCompare(genericScopeView, "previewShown", false);
6874 }
6975
76+ function openPreview() {
77+ var categoryListView = findChild(genericScopeView, "categoryListView");
78+ categoryListView.positionAtBeginning();
79+
80+ var tile = findChild(genericScopeView, "delegate0");
81+ mouseClick(tile, tile.width / 2, tile.height / 2);
82+ var openEffect = findChild(genericScopeView, "openEffect");
83+ tryCompare(openEffect, "gap", 1);
84+ }
85+
86+ function checkArrowPosition(index) {
87+ var tile = findChild(genericScopeView, "delegate" + index);
88+ var tileCenter = tile.x + tile.width/2;
89+ var pointerArrow = findChild(genericScopeView, "pointerArrow");
90+ var pointerArrowCenter = pointerArrow.x + pointerArrow.width/2;
91+ compare(pointerArrowCenter, tileCenter, "Pointer did not move to tile");
92+ }
93+
94+ function closePreview() {
95+ var closePreviewMouseArea = findChild(genericScopeView, "closePreviewMouseArea");
96+ mouseClick(closePreviewMouseArea, closePreviewMouseArea.width / 2, closePreviewMouseArea.height / 2);
97+
98+ var previewListView = findChild(genericScopeView, "previewListView");
99+ tryCompare(previewListView, "open", false);
100+ var openEffect = findChild(genericScopeView, "openEffect");
101+ tryCompare(openEffect, "gap", 0);
102+
103+ var categoryListView = findChild(genericScopeView, "categoryListView");
104+ categoryListView.flick(0, units.gu(200));
105+ tryCompare(categoryListView, "flicking", false);
106+ }
107+
108+ function test_previewOpenClose() {
109+ var previewListView = findChild(genericScopeView, "previewListView");
110+ tryCompare(previewListView, "open", false);
111+
112+ openPreview();
113+
114+ // check for it opening successfully
115+ var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
116+ tryCompareFunction(function() {
117+ var parts = currentPreviewItem.source.toString().split("/");
118+ var name = parts[parts.length - 1];
119+ return name == "DashPreviewPlaceholder.qml";
120+ },
121+ true);
122+ tryCompareFunction(function() {
123+ var parts = currentPreviewItem.source.toString().split("/");
124+ var name = parts[parts.length - 1];
125+ return name == "GenericPreview.qml";
126+ },
127+ true);
128+ tryCompare(currentPreviewItem, "progress", 1);
129+ tryCompare(previewListView, "open", true);
130+
131+ closePreview();
132+ tryCompare(previewListView, "open", false);
133+ }
134+
135+ function test_previewCycleOne() {
136+ var previewListView = findChild(genericScopeView, "previewListView");
137+ tryCompare(previewListView, "open", false);
138+
139+ openPreview();
140+
141+ // wait for it to be loaded
142+ var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
143+ tryCompareFunction(function() {
144+ var parts = currentPreviewItem.source.toString().split("/");
145+ var name = parts[parts.length - 1];
146+ return name == "GenericPreview.qml";
147+ },
148+ true);
149+ tryCompare(currentPreviewItem, "progress", 1);
150+ waitForRendering(currentPreviewItem);
151+
152+ checkArrowPosition(0);
153+
154+ // flick to the next previews
155+
156+ for (var i = 1; i < previewListView.count; ++i) {
157+
158+ mouseFlick(previewListView, previewListView.width - units.gu(1),
159+ previewListView.height / 2,
160+ units.gu(2),
161+ previewListView.height / 2);
162+
163+ // wait for it to be loaded
164+ var nextPreviewItem = findChild(genericScopeView, "previewLoader" + i);
165+ tryCompareFunction(function() {
166+ var parts = nextPreviewItem.source.toString().split("/");
167+ var name = parts[parts.length - 1];
168+ return name == "GenericPreview.qml";
169+ },
170+ true);
171+ tryCompare(nextPreviewItem, "progress", 1);
172+ waitForRendering(nextPreviewItem);
173+
174+ checkArrowPosition(i);
175+ }
176+
177+ closePreview();
178+ }
179+
70180 function test_show_spinner() {
71− var previewLoader = findChild(genericScopeView, "previewLoader");
72− previewLoader.open = true;
73− previewLoader.source = "../../../Dash/Generic/GenericPreview.qml";
181+ openPreview();
182+ var previewListView = findChild(genericScopeView, "previewListView");
183+ var previewLoader = findChild(genericScopeView, "previewLoader0");
184+
74185 previewLoader.item.showProcessingAction = true;
75186 var waitingForAction = findChild(genericScopeView, "waitingForActionMouseArea");
76187 tryCompare(waitingForAction, "enabled", true);
77188 previewLoader.closePreviewSpinner();
78189 tryCompare(waitingForAction, "enabled", false);
190+
191+ closePreview();
79192 }
80193 }
81194 }
tests/qmltests/Greeter/tst_Clock.qml+16
@@ -19,6 +19,7 @@ import QtTest 1.0
1919import ".."
2020import "../../../Greeter"
2121import Ubuntu.Components 0.1
22+import QMenuModel 0.1
2223import Unity.Test 0.1 as UT
2324
2425Rectangle {
@@ -35,9 +36,24 @@ Rectangle {
3536 }
3637 }
3738
39+ UnityMenuModel {
40+ id: menuModel
41+ modelData: [{
42+ "rowData": {
43+ "actionState": { "label": Qt.formatTime(new Date("October 13, 1975 11:13:00")) }
44+ }
45+ }]
46+ }
47+
3848 UT.UnityTestCase {
3949 name: "Clock"
4050
51+ function init() {
52+ var cachedModel = findChild(clock, "timeModel");
53+ verify(cachedModel !== undefined);
54+ cachedModel.model = menuModel;
55+ }
56+
4157 function test_customDate() {
4258 var dateObj = new Date("October 13, 1975 11:13:00")
4359 var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
tests/qmltests/Greeter/tst_Lockscreen.qml+41−4
@@ -175,15 +175,17 @@ Rectangle {
175175
176176 function test_unlock_data() {
177177 return [
178− {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234"},
179− {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password"},
180− {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321"},
181− {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap"},
178+ {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", pinLength: 4},
179+ {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password", pinLength: -1},
180+ {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321", pinLength: 4},
181+ {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap", pinLength: -1},
182+ {tag: "flexible length", alphanumeric: false, username: "has-pin", password: "1234", pinLength: -1},
182183 ]
183184 }
184185
185186 function test_unlock(data) {
186187 enteredLabel.text = ""
188+ pinLengthTextField.text = data.pinLength
187189 LightDM.Greeter.authenticate(data.username)
188190 waitForRendering(lockscreen)
189191
@@ -198,6 +200,10 @@ Rectangle {
198200 var button = findChild(lockscreen, "pinPadButton" + character)
199201 mouseClick(button, units.gu(1), units.gu(1))
200202 }
203+ if (data.pinLength == -1) {
204+ var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
205+ mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
206+ }
201207 }
202208 tryCompare(enteredLabel, "text", data.password)
203209 }
@@ -237,5 +243,36 @@ Rectangle {
237243 // wait for animation to finish to not disturb other tests
238244 tryCompare(animation, "running", false)
239245 }
246+
247+ function test_backspace_data() {
248+ return [
249+ {tag: "fixed length", pinLength: 4},
250+ {tag: "variable length", pinLength: -1}
251+ ];
252+ }
253+
254+ function test_backspace(data) {
255+ pinPadCheckBox.checked = false
256+ pinLengthTextField.text = data.pinLength
257+ waitForRendering(lockscreen);
258+
259+ var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
260+ var backspaceIcon = findChild(lockscreen, "backspaceIcon");
261+ var pinEntryField = findChild(lockscreen, "pinentryField");
262+
263+ compare(pinPadButtonErase.iconName, data.pinLength == -1 ? "" : "erase");
264+ compare(backspaceIcon.visible, data.pinLength == -1);
265+
266+ var pinPadButton5 = findChild(lockscreen, "pinPadButton5");
267+ mouseClick(pinPadButton5, units.gu(1), units.gu(1));
268+ compare(pinEntryField.text, "5");
269+
270+ if (data.pinLength == -1) {
271+ mouseClick(backspaceIcon, units.gu(1), units.gu(1));
272+ } else {
273+ mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
274+ }
275+ compare(pinEntryField.text, "");
276+ }
240277 }
241278}
tests/qmltests/Greeter/tst_Phone.qml+23−10
@@ -83,8 +83,9 @@ Item {
8383
8484 function test_teaseLockedUnlocked_data() {
8585 return [
86− {tag: "unlocked", locked: false},
87− {tag: "locked", locked: true}
86+ {tag: "unlocked", locked: false, moved: false},
87+ {tag: "locked", locked: true, moved: false},
88+ {tag: "moved", locked: false, moved: true}
8889 ];
8990 }
9091
@@ -93,10 +94,16 @@ Item {
9394 tryCompare(greeter, "x", 0);
9495 greeter.locked = data.locked;
9596
97+ // simulate greeter being in the middle of a swipe
98+ if (data.moved) {
99+ greeter.x = -units.gu(4);
100+ tryCompare(greeter, "x", -units.gu(4));
101+ }
102+
96103 mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
97104 greeter.minX = 0; // This is needed because the transition actually makes x jump once before animating
98105
99− if (!data.locked) {
106+ if (!data.locked && !data.moved) {
100107 // Check if it has been moved over by 2 GUs. Give it a 2 pixel grace area
101108 // because animation duration and teaseTimer are the same duration and
102109 // might cause slight offsets
@@ -104,10 +111,16 @@ Item {
104111 } else {
105112 // waiting 100ms to make sure nothing moves
106113 wait(100);
107− compare(greeter.minX, 0, "Greeter moved even tho its locked");
114+ compare(greeter.minX, 0, "Greeter teasing not disabled even though it's locked or moving.");
108115 }
116+
117+ // Restore position in case we moved it for the test
118+ if (data.moved) {
119+ greeter.x = 0;
120+ }
121+
109122 // Wait until we're back to 0
110− tryCompareFunction(function() { return greeter.x;}, 0);
123+ tryCompareFunction(function() { return greeter.x;}, 0);
111124 }
112125
113126 function test_statsWelcomeScreen() {
@@ -120,7 +133,7 @@ Item {
120133 tryCompare(LightDM.Infographic, "username", "single")
121134 }
122135
123− function test_wallpaper_data() {
136+ function test_background_data() {
124137 return [
125138 {tag: "set", accounts: "../tests/data/unity/backgrounds/blue.png", expected: "blue.png"},
126139 {tag: "unset", accounts: "", expected: "background.jpg"},
@@ -128,12 +141,12 @@ Item {
128141 ]
129142 }
130143
131− function test_wallpaper(data) {
144+ function test_background(data) {
132145 var loader = findChild(greeter, "greeterContentLoader")
133− var wallpaper = findChild(loader.item, "wallpaper")
146+ var background = findChild(loader.item, "greeterBackground")
134147 AccountsService.backgroundFile = data.accounts
135− tryCompareFunction(function() { return wallpaper.source.toString().indexOf(data.expected) !== -1; }, true)
136− tryCompare(wallpaper, "status", Image.Ready)
148+ tryCompareFunction(function() { return background.source.toString().indexOf(data.expected) !== -1; }, true)
149+ tryCompare(background, "status", Image.Ready)
137150 }
138151 }
139152}
tests/qmltests/Launcher/tst_Launcher.qml+47−11
@@ -363,25 +363,60 @@ Item {
363363 var draggedItem = findChild(launcher, "launcherDelegate5")
364364 var item0 = findChild(launcher, "launcherDelegate0")
365365 var fakeDragItem = findChild(launcher, "fakeDragItem")
366− var dndArea = findChild(launcher, "dndArea")
366+ var quickListShape = findChild(launcher, "quickListShape")
367367
368368 // Initial state
369− compare(dndArea.quickListPopover, null)
369+ compare(quickListShape.visible, false)
370370
371371 // Doing longpress
372372 mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
373373 tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
374− verify(dndArea.quickListPopover != null)
374+ tryCompare(quickListShape, "visible", true)
375375
376376 // Dragging a bit (> 1.5 gu)
377377 mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
378378
379379 // QuickList needs to be closed when a drag operation starts
380− tryCompare(dndArea, "quickListPopover", null)
380+ tryCompare(quickListShape, "visible", false)
381381
382382 mouseRelease(draggedItem);
383383 }
384384
385+ function test_quicklist_positioning_data() {
386+ return [
387+ {tag: "top", flickTo: "top", itemIndex: 0},
388+ {tag: "bottom", flickTo: "bottom", itemIndex: 9}
389+ ];
390+ }
391+
392+ function test_quicklist_positioning(data) {
393+ revealer.dragLauncherIntoView();
394+ var quickList = findChild(launcher, "quickList")
395+ var draggedItem = findChild(launcher, "launcherDelegate" + data.itemIndex)
396+ var quickListShape = findChild(launcher, "quickListShape")
397+
398+ // Position launcher to where we need it
399+ var listView = findChild(launcher, "launcherListView");
400+ listView.flick(0, units.gu(200) * (data.flickTo === "top" ? 1 : -1));
401+ tryCompare(listView, "flicking", false);
402+
403+ // Doing longpress
404+ mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
405+ tryCompare(quickListShape, "opacity", 0.8)
406+ mouseRelease(draggedItem);
407+
408+ verify(quickList.y >= units.gu(1))
409+ verify(quickList.y + quickList.height + units.gu(1) <= launcher.height)
410+
411+ // Click somewhere in the empty space to dismiss the quicklist
412+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
413+ tryCompare(quickListShape, "visible", false)
414+
415+ // Click somewhere in the empty space to dismiss the launcher
416+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
417+ revealer.waitUntilLauncherDisappears()
418+ }
419+
385420 function test_quicklist_click_data() {
386421 return [
387422 {tag: "non-clickable", index: 1, clickable: false },
@@ -392,19 +427,20 @@ Item {
392427 function test_quicklist_click(data) {
393428 revealer.dragLauncherIntoView();
394429 var clickedItem = findChild(launcher, "launcherDelegate5")
395− var dndArea = findChild(launcher, "dndArea")
430+ var quickList = findChild(launcher, "quickList")
431+ var quickListShape = findChild(launcher, "quickListShape")
396432
397433 // Initial state
398− compare(dndArea.quickListPopover, null)
434+ tryCompare(quickListShape, "visible", false)
399435
400436 // Doing longpress
401437 mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
402438 tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
403− verify(dndArea.quickListPopover != null)
439+ verify(quickListShape.visible, "QuickList must be visible")
404440
405441 mouseRelease(clickedItem);
406442
407− var quickListEntry = findChild(dndArea.quickListPopover, "quickListEntry" + data.index)
443+ var quickListEntry = findChild(quickList, "quickListEntry" + data.index)
408444
409445 signalSpy.clear();
410446 signalSpy.signalName = "quickListTriggered"
@@ -413,7 +449,7 @@ Item {
413449
414450 if (data.clickable) {
415451 // QuickList needs to be closed when some clickable item is clicked
416− tryCompare(dndArea, "quickListPopover", null)
452+ tryCompare(quickListShape, "visible", false)
417453
418454 compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
419455 compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
@@ -422,13 +458,13 @@ Item {
422458 } else {
423459
424460 // QuickList must not be closed when a non-clickable item is clicked
425− verify(dndArea.quickListPopover != null)
461+ verify(quickListShape.visible, "QuickList must be visible")
426462
427463 compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
428464
429465 // Click somewhere in the empty space to dismiss the quicklist
430466 mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
431− tryCompare(dndArea, "quickListPopover", null)
467+ tryCompare(quickListShape, "visible", false)
432468 }
433469 }
434470 }
tests/qmltests/Panel/Indicators/tst_DefaultIndicatorWidget.qmlnew file+97
@@ -0,0 +1,97 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import Unity.Test 0.1 as UT
20+import QMenuModel 0.1
21+import "../../../../Panel/Indicators"
22+
23+Item {
24+ id: testView
25+ width: units.gu(40)
26+ height: units.gu(70)
27+
28+ DefaultIndicatorWidget {
29+ id: widget
30+
31+ anchors {
32+ left: parent.left
33+ top: parent.top
34+ }
35+
36+ busName: "test"
37+ actionsObjectPath: "test"
38+ deviceMenuObjectPath: "test"
39+
40+ rootMenuType: ""
41+
42+ iconSize: units.gu(3.2)
43+ height: units.gu(3)
44+
45+ rootActionState {
46+ icons: [ "image://theme/audio-volume-high", "image://theme/audio-volume-low" ]
47+ leftLabel: "left"
48+ rightLabel: "right"
49+ }
50+ }
51+
52+ UT.UnityTestCase {
53+ name: "DefaultIndicatorWidget"
54+ when: windowShown
55+
56+ function init() {
57+ widget.rootActionState.icons = [];
58+ widget.rootActionState.leftLabel = "";
59+ widget.rootActionState.rightLabel = "";
60+ wait(50);
61+ }
62+
63+ function test_guRoundedWidth_data() {
64+ return [
65+ { tag: "empty", icons: [], leftLabel: "", rightLabel: "" },
66+ { tag: "1-icon-no-label", icons: [ "image://theme/audio-volume-high" ], leftLabel: "", rightLabel: "" },
67+ { tag: "2-icon-no-label", icons: [ "image://theme/audio-volume-high", "image://theme/audio-volume-low" ], leftLabel: "", rightLabel: "" },
68+ { tag: "no-icon-l-label", icons: [], leftLabel: "left", rightLabel: "" },
69+ { tag: "no-icon-lr-label", icons: [], leftLabel: "left", rightLabel: "right" },
70+ { tag: "1-icon-l-label", icons: [ "image://theme/audio-volume-high" ], leftLabel: "left", rightLabel: "" },
71+ { tag: "1-icon-lr-label", icons: [ "image://theme/audio-volume-high" ], leftLabel: "left", rightLabel: "right" },
72+ { tag: "2-icon-l-label", icons: [ "image://theme/audio-volume-high", "image://theme/audio-volume-low" ], leftLabel: "left", rightLabel: "" },
73+ { tag: "2-icon-lr-label", icons: [ "image://theme/audio-volume-high", "image://theme/audio-volume-low" ], leftLabel: "left", rightLabel: "right" }
74+ ];
75+ }
76+
77+ function test_guRoundedWidth(data) {
78+ widget.rootActionState.icons = data.icons
79+ widget.rootActionState.leftLabel = data.leftLabel;
80+ widget.rootActionState.rightLabel = data.rightLabel;
81+ wait(50);
82+
83+ compare(widget.width, guRoundUp(widget.width));
84+ }
85+ }
86+
87+ // TODO: Use toolkit function https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1242575
88+ function guRoundUp(width) {
89+ if (width == 0) {
90+ return 0;
91+ }
92+ var gu1 = units.gu(1.0);
93+ var mod = (width % gu1);
94+
95+ return mod == 0 ? width : width + (gu1 - mod);
96+ }
97+}
tests/qmltests/Panel/tst_Panel.qml−1
@@ -52,7 +52,6 @@ Item {
5252 {tag: "fullscreen", fullscreenFlag: true }
5353 ]}
5454
55−
5655 function init() {
5756 searchClicked = false;
5857 panel.indicators.hide();
tests/qmltests/tst_Shell.qml+85−22
@@ -55,19 +55,42 @@ Item {
5555 when: windowShown
5656
5757 function initTestCase() {
58− swipeAwayGreeter();
58+ var ok = false;
59+ var attempts = 0;
60+ var maxAttempts = 1000;
61+
62+ // Qt loads a qml scene asynchronously. So early on, some findChild() calls made in
63+ // tests may fail because the desired child item wasn't loaded yet.
64+ // Thus here we try to ensure the scene has been fully loaded before proceeding with the tests.
65+ // As I couldn't find an API in QQuickView & friends to tell me that the scene is 100% loaded
66+ // (all items instantiated, etc), I resort to checking the existence of some key items until
67+ // repeatedly until they're all there.
68+ do {
69+ var dashContentList = findChild(shell, "dashContentList");
70+ waitForRendering(dashContentList);
71+ var homeLoader = findChild(dashContentList, "home.scope loader");
72+ ok = homeLoader !== undefined
73+ && homeLoader.item !== undefined;
5974
60− // Ensure DashHome is loaded before continuing
61− var dashContentList = findChild(shell, "dashContentList");
62− waitForRendering(dashContentList);
63− var homeLoader = findChild(dashContentList, "home.scope loader");
64− verify(homeLoader);
65− tryCompareFunction(function() {return homeLoader.item !== undefined;}, true);
75+ var greeter = findChild(shell, "greeter");
76+ ok &= greeter !== undefined;
77+
78+ var launcherPanel = findChild(shell, "launcherPanel");
79+ ok &= launcherPanel !== undefined;
80+
81+ attempts++;
82+ if (!ok) {
83+ console.log("Attempt " + attempts + " failed. Waiting a bit before trying again.");
84+ // wait a bit before retrying
85+ wait(100);
86+ } else {
87+ console.log("All seem fine after " + attempts + " attempts.");
88+ }
89+ } while (!ok && attempts <= maxAttempts);
6690
67− // FIXME: Desperate measure to ensure Jenkins has the scene graph fully loaded
68− // before it calls the test functions (otherwise findChild calls will fail).
69− // The code above just didn't solve it.
70− wait(2000);
91+ verify(ok);
92+
93+ swipeAwayGreeter();
7194 }
7295
7396 function cleanup() {
@@ -111,14 +134,14 @@ Item {
111134 waitUntilApplicationWindowIsFullyVisible();
112135
113136 // Minimize the application we just launched
114− swipeFromLeftEdge();
137+ swipeFromLeftEdge(units.gu(27));
115138
116139 waitForUIToSettle();
117140
118141 checkRightEdgeDragWithMinimizedApp();
119142
120143 // Minimize that application once again
121− swipeFromLeftEdge();
144+ swipeFromLeftEdge(units.gu(27));
122145
123146 // Right edge behavior should now be the same as before that app,
124147 // was launched. Manually cleanup beforehand to get to initial
@@ -130,8 +153,10 @@ Item {
130153
131154 function test_leftEdgeDrag_data() {
132155 return [
133− {tag: "without launcher", revealLauncher: false},
134− {tag: "with launcher", revealLauncher: true},
156+ {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true},
157+ {tag: "with launcher", revealLauncher: true, swipeLength: units.gu(27), appHides: true},
158+ {tag: "small swipe", revealLauncher: false, swipeLength: units.gu(25), appHides: false},
159+ {tag: "long swipe", revealLauncher: false, swipeLength: units.gu(27), appHides: true}
135160 ];
136161 }
137162
@@ -144,8 +169,11 @@ Item {
144169 dragLauncherIntoView();
145170 }
146171
147− swipeFromLeftEdge();
148− waitUntilApplicationWindowIsFullyHidden();
172+ swipeFromLeftEdge(data.swipeLength);
173+ if (data.appHides)
174+ waitUntilApplicationWindowIsFullyHidden();
175+ else
176+ waitUntilApplicationWindowIsFullyVisible();
149177 }
150178
151179 function test_suspend() {
@@ -292,8 +320,12 @@ Item {
292320
293321 waitUntilApplicationWindowIsFullyVisible();
294322
323+ // Dragging launcher into view with a little bit of gap (units.gu(1)) should switch to Apps scope
324+ dragLauncherIntoView();
325+ verify(itemIsOnScreen(dashApps));
326+
295327 // Minimize the application we just launched
296− swipeFromLeftEdge();
328+ swipeFromLeftEdge(units.gu(27));
297329
298330 // Wait for the whole UI to settle down
299331 waitUntilApplicationWindowIsFullyHidden();
@@ -376,10 +408,10 @@ Item {
376408 }
377409 }
378410
379− function swipeFromLeftEdge() {
411+ function swipeFromLeftEdge(swipeLength) {
380412 var touchStartX = 2;
381413 var touchStartY = shell.height / 2;
382− touchFlick(shell, touchStartX, touchStartY, shell.width * 0.75, touchStartY);
414+ touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
383415 }
384416
385417 function swipeLeftFromCenter() {
@@ -432,7 +464,7 @@ Item {
432464 } while (!isStill);
433465 }
434466
435− function test_wallpaper_data() {
467+ function test_background_data() {
436468 return [
437469 {tag: "red", url: "tests/data/unity/backgrounds/red.png", expectedUrl: "tests/data/unity/backgrounds/red.png"},
438470 {tag: "blue", url: "tests/data/unity/backgrounds/blue.png", expectedUrl: "tests/data/unity/backgrounds/blue.png"},
@@ -441,13 +473,21 @@ Item {
441473 ]
442474 }
443475
444− function test_wallpaper(data) {
476+ function test_background(data) {
445477 var backgroundImage = findChild(shell, "backgroundImage")
446478 GSettingsController.setPictureUri(data.url)
447479 tryCompareFunction(function() { return backgroundImage.source.toString().indexOf(data.expectedUrl) !== -1; }, true)
448480 tryCompare(backgroundImage, "status", Image.Ready)
449481 }
450482
483+ function test_lockscreen_background() {
484+ var backgroundImage = findChild(shell, "backgroundImage")
485+ var lockscreen = findChild(shell, "lockscreen")
486+ var lockscreenBackground = findChild(lockscreen, "lockscreenBackground")
487+ var lockscreenBackgroundMapped = lockscreenBackground.mapToItem(shell, 0, 0)
488+ compare(backgroundImage.y, lockscreenBackgroundMapped.y)
489+ }
490+
451491 function test_DashShown_data() {
452492 return [
453493 {tag: "in focus", greeter: false, app: false, launcher: false, indicators: false, expectedShown: true},
@@ -483,5 +523,28 @@ Item {
483523 var dash = findChild(shell, "dash");
484524 tryCompare(dash, "shown", data.expectedShown);
485525 }
526+
527+ function test_searchIndicatorHidesOnAppFocus() {
528+ var searchIndicator = findChild(shell, "container")
529+ tryCompare(searchIndicator, "opacity", 1)
530+ dragLauncherIntoView();
531+
532+ // Launch an app from the launcher
533+ tapOnAppIconInLauncher();
534+ waitUntilApplicationWindowIsFullyVisible();
535+
536+ tryCompare(searchIndicator, "opacity", 0);
537+ }
538+
539+ function test_searchIndicatorHidesOnGreeterShown() {
540+ var searchIndicator = findChild(shell, "container")
541+ var greeter = findChild(shell, "greeter");
542+
543+ tryCompare(searchIndicator, "opacity", 1)
544+
545+ greeter.show()
546+ tryCompare(greeter, "shown", true)
547+ tryCompare(searchIndicator, "opacity", 0)
548+ }
486549 }
487550}
tests/utils/modules/Unity/Test/UnityTestCase.qml+34
@@ -14,6 +14,7 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17+import QtQuick 2.0
1718import QtTest 1.0
1819import Ubuntu.Components 0.1
1920import Unity.Test 0.1 as UT
@@ -297,4 +298,37 @@ TestCase {
297298 event.release(0 /* touchId */, x, y)
298299 event.commit()
299300 }
301+
302+ Component.onCompleted: {
303+ var rootItem = parent;
304+ while (rootItem.parent != undefined) {
305+ rootItem = rootItem.parent;
306+ }
307+ removeTimeConstraintsFromDirectionalDragAreas(rootItem);
308+ }
309+
310+ /*
311+ In qmltests, sequences of touch events are sent all at once, unlike in "real life".
312+ Also qmltests might run really slowly, e.g. when run from inside virtual machines.
313+ Thus to remove a variable that qmltests cannot really control, namely time, this
314+ function removes all constraints from DirectionalDragAreas that are sensible to
315+ elapsed time.
316+
317+ This effectively makes DirectionalDragAreas easier to fool.
318+ */
319+ function removeTimeConstraintsFromDirectionalDragAreas(item) {
320+
321+ // use duck-typing to identify a DirectionalDragArea
322+ if (item.minSpeed != undefined
323+ && item.maxSilenceTime != undefined
324+ && item.compositionTime != undefined) {
325+ item.minSpeed = 0;
326+ item.maxSilenceTime = 60 * 60 * 1000;
327+ item.compositionTime = 0;
328+ } else {
329+ for (var i in item.children) {
330+ removeTimeConstraintsFromDirectionalDragAreas(item.children[i]);
331+ }
332+ }
333+ }
300334}