on xenial help sign in
Merge from trunk
commit00ec39566abed400f0ee2b4a702fc4460a329e29 authorMichael Terry <michael.terry@canonical.com> dateWed 28 Aug 2013, 16:50 (13 years ago) parent2f04183c3755ee
301 files changed+11898−1888
Bottombar/Bottombar.qml+60−6 Bottombar/HudButton.qml+8−8 Components/CrossFadeImage.qml+3−1 Components/FilterGrid.qml+31−61 Components/ListItems/Header.qml+19−2 Components/ResponsiveGridView.qml+7−1 Components/Tile.qml−6 Dash/Apps/AppInfo.qml+1 Dash/Apps/AppPreview.qml+37−32 Dash/Apps/ApplicationsFilterGrid.qml−1 Dash/Apps/AppsAvailableForDownloadModel.qml+61−6 Dash/Apps/FrequentlyUsedAppsModel.qml+60−6 Dash/DashApps.qml+16−225 Dash/DashHome.qml+8−123 Dash/Generic/GenericFilterGrid.qml−1 Dash/Generic/GenericPreview.qml+10−2 Dash/GenericScopeView.qml+59−1 Dash/Music/MusicFilterGrid.qml−1 Dash/ScopeView.qml+8−1 Dash/SearchableResultModel.qml+40 Dash/Video/VideosFilterGrid.qml−1 Dash/graphics/header_handlearrow2@20.png Dash/graphics/header_handlearrow@20.png Greeter/GreeterContent.qml−1 Launcher/FoldingLauncherDelegate.qml+144 Launcher/Launcher.qml+16−7 Launcher/LauncherDelegate.qml+89−175 Launcher/LauncherPanel.qml+434−92 Launcher/QuicklistForegroundStyle.qml+23 Launcher/graphics/launcher_bg@18.png Launcher/graphics/launcher_bg@18.sci−5 Launcher/graphics/launcher_bg@30.png Launcher/graphics/launcher_bg@30.sci+5 Launcher/graphics/notification@30.png Launcher/graphics/notification@30.sci+5 Launcher/graphics/progressbar-fill@30.png Launcher/graphics/progressbar-fill@30.sci+5 Launcher/graphics/progressbar-trough@30.png Launcher/graphics/progressbar-trough@30.sci+5 Launcher/graphics/quicklist_tooltip@30.png Panel/Indicators.qml+3−6 Panel/Indicators/DatetimeIndicatorWidget.qml+1−1 Panel/Indicators/DefaultIndicatorPage.qml+1−1 Panel/Indicators/DefaultIndicatorPage2.qml+25 Panel/Indicators/DefaultIndicatorWidget.qml+1−1 Panel/Indicators/DefaultIndicatorWidget2.qml+76 Panel/Indicators/MessagingIndicatorPage.qml+1−1 Panel/Indicators/NetworkIndicatorPage.qml+18−14 Panel/Indicators/NetworkIndicatorPage2.qml+82 Panel/Indicators/NetworkIndicatorWidget.qml+1−1 Panel/Indicators/client/IndicatorsList.qml+1−1 Panel/Indicators/client/IndicatorsPage.qml+6−9 Panel/Indicators/client/IndicatorsTree.qml+7−7 Panel/IndicatorsDataModel.qml+29−11 Panel/MenuContent.qml+78−97 Shell.qml+50−18 build+7−3 debian/changelog+309 debian/control+11−4 debian/rules+2−1 main.cpp−1 plugins/HudClient/qmldir+1 plugins/LightDM/plugin.cpp+1−1 plugins/ListViewWithPageHeader/listviewwithpageheader.cpp+47−11 plugins/ListViewWithPageHeader/listviewwithpageheader.h+5−1 plugins/Powerd/Powerd.cpp+2−2 plugins/Powerd/Powerd.h+15−1 plugins/Powerd/plugin.cpp+1−1 plugins/Ubuntu/DownloadDaemonListener/plugin.cpp+1−1 plugins/Unity/CMakeLists.txt+15−1 plugins/Unity/Indicators/CMakeLists.txt+13−10 plugins/Unity/Indicators/Messaging/qml/CMakeLists.txt−5 plugins/Unity/Indicators/Network/plugin.cpp+1−1 plugins/Unity/Indicators/Network/qml/AccessPoint.qml+19−15 plugins/Unity/Indicators/Network/qml/PasswordPage.qml+3−3 plugins/Unity/Indicators/Network/qml/qmldir+1−2 plugins/Unity/Indicators/indicatorsmanager.cpp+40−1 plugins/Unity/Indicators/indicatorsmanager.h+3 plugins/Unity/Indicators/indicatorsmodel.cpp+2−2 plugins/Unity/Indicators/modelprinter.cpp+66−43 plugins/Unity/Indicators/modelprinter.h+12−7 plugins/Unity/Indicators/plugin.cpp+23−13 plugins/Unity/Indicators/plugin.h+10−9 plugins/Unity/Indicators/qml/BaseMenuItem.qml+38 plugins/Unity/Indicators/qml/ButtonMenuItem.qml+9−12 plugins/Unity/Indicators/qml/CachedUnityMenuModel.qml+60 plugins/Unity/Indicators/qml/DivMenuItem.qml+1−1 plugins/Unity/Indicators/qml/FramedMenuItem.qml+39 plugins/Unity/Indicators/qml/IndicatorBase.qml+13−19 plugins/Unity/Indicators/qml/IndicatorPage.qml+43−79 plugins/Unity/Indicators/qml/IndicatorWidget.qml+4−39 plugins/Unity/Indicators/qml/MenuItemFactory.qml+158−56 plugins/Unity/Indicators/qml/ProgressMenuItem.qml+11−38 plugins/Unity/Indicators/qml/SectionMenuItem.qml+5−8 plugins/Unity/Indicators/qml/SliderMenuItem.qml+75−34 plugins/Unity/Indicators/qml/StandardMenuItem.qml+71 plugins/Unity/Indicators/qml/SwitchMenuItem.qml+21−11 plugins/Unity/Indicators/qml/qmldir+5−3 plugins/Unity/Indicators/rootactionstate.cpp+249 plugins/Unity/Indicators/rootactionstate.h+82 plugins/Unity/Indicators/unitymenumodelcache.cpp+42 plugins/Unity/Indicators/unitymenumodelcache.h+44 plugins/Unity/Indicators/unitymenumodelstack.cpp+69 plugins/Unity/Indicators/unitymenumodelstack.h+60 plugins/Unity/IndicatorsLegacy/CMakeLists.txt+49 plugins/Unity/Indicators/Messaging/CMakeLists.txt → plugins/Unity/IndicatorsLegacy/Messaging/CMakeLists.txt plugins/Unity/Indicators/Messaging/qml/ActionButton.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/ActionButton.qml+1−1 plugins/Unity/Indicators/Messaging/qml/ActionTextField.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/ActionTextField.qml plugins/Unity/IndicatorsLegacy/Messaging/qml/CMakeLists.txt+5 plugins/Unity/Indicators/Messaging/qml/GroupedMessage.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/GroupedMessage.qml+1−1 plugins/Unity/Indicators/Messaging/qml/HeroMessage.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/HeroMessage.qml+1−1 plugins/Unity/Indicators/Messaging/qml/HeroMessageHeader.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/HeroMessageHeader.qml plugins/Unity/Indicators/Messaging/qml/MessageMenuItem.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/MessageMenuItem.qml+1−1 plugins/Unity/Indicators/Messaging/qml/QuickReply.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/QuickReply.qml+1−1 plugins/Unity/Indicators/Messaging/qml/SimpleTextMessage.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/SimpleTextMessage.qml plugins/Unity/Indicators/Messaging/qml/SnapDecision.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/SnapDecision.qml+1−1 plugins/Unity/Indicators/Messaging/qml/TextMessage.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/TextMessage.qml+1−1 plugins/Unity/Indicators/Messaging/qml/qmldir → plugins/Unity/IndicatorsLegacy/Messaging/qml/qmldir+1−1 plugins/Unity/Indicators/Messaging/qml/utils.js → plugins/Unity/IndicatorsLegacy/Messaging/qml/utils.js plugins/Unity/IndicatorsLegacy/Network/CMakeLists.txt+45 plugins/Unity/IndicatorsLegacy/Network/networkagent.cpp+146 plugins/Unity/IndicatorsLegacy/Network/networkagent.h+65 plugins/Unity/IndicatorsLegacy/Network/plugin.cpp+31 plugins/Unity/IndicatorsLegacy/Network/plugin.h+32 plugins/Unity/IndicatorsLegacy/Network/qml/AccessPoint.qml+68 plugins/Unity/IndicatorsLegacy/Network/qml/CMakeLists.txt+5 plugins/Unity/IndicatorsLegacy/Network/qml/PasswordPage.qml+88 plugins/Unity/IndicatorsLegacy/Network/qml/PasswordTextField.qml+68 plugins/Unity/Indicators/Network/qml/WifiSection.qml → plugins/Unity/IndicatorsLegacy/Network/qml/WifiSection.qml+1−1 plugins/Unity/IndicatorsLegacy/Network/qml/qmldir+9 plugins/Unity/IndicatorsLegacy/Network/secret-agent.c+382 plugins/Unity/IndicatorsLegacy/Network/secret-agent.h+102 plugins/Unity/Indicators/flatmenuproxymodel.cpp → plugins/Unity/IndicatorsLegacy/flatmenuproxymodel.cpp+13 plugins/Unity/Indicators/flatmenuproxymodel.h → plugins/Unity/IndicatorsLegacy/flatmenuproxymodel.h plugins/Unity/IndicatorsLegacy/modelprinter.cpp+114 plugins/Unity/IndicatorsLegacy/modelprinter.h+50 plugins/Unity/IndicatorsLegacy/plugin.cpp+42 plugins/Unity/IndicatorsLegacy/plugin.h+32 plugins/Unity/Indicators/qml/BasicMenuItem.qml → plugins/Unity/IndicatorsLegacy/qml/BasicMenuItem.qml plugins/Unity/IndicatorsLegacy/qml/ButtonMenuItem.qml+47 plugins/Unity/IndicatorsLegacy/qml/CMakeLists.txt+5 plugins/Unity/IndicatorsLegacy/qml/DivMenuItem.qml+26 plugins/Unity/IndicatorsLegacy/qml/HLine.qml+29 plugins/Unity/IndicatorsLegacy/qml/IndicatorBase.qml+59 plugins/Unity/IndicatorsLegacy/qml/IndicatorPage.qml+191 plugins/Unity/IndicatorsLegacy/qml/IndicatorWidget.qml+68 plugins/Unity/Indicators/qml/MenuAction.qml → plugins/Unity/IndicatorsLegacy/qml/MenuAction.qml plugins/Unity/Indicators/qml/MenuActionBinding.qml → plugins/Unity/IndicatorsLegacy/qml/MenuActionBinding.qml plugins/Unity/Indicators/qml/MenuItem.qml → plugins/Unity/IndicatorsLegacy/qml/MenuItem.qml plugins/Unity/IndicatorsLegacy/qml/MenuItemFactory.qml+110 plugins/Unity/IndicatorsLegacy/qml/ProgressMenuItem.qml+65 plugins/Unity/IndicatorsLegacy/qml/RemoveBackground.qml+55 plugins/Unity/IndicatorsLegacy/qml/SectionMenuItem.qml+57 plugins/Unity/IndicatorsLegacy/qml/SliderMenuItem.qml+64 plugins/Unity/IndicatorsLegacy/qml/SwitchMenuItem.qml+43 plugins/Unity/IndicatorsLegacy/qml/qmldir+21 plugins/Unity/Indicators/resources/artwork/messaging/default_app.svg → plugins/Unity/IndicatorsLegacy/resources/artwork/messaging/default_app.svg plugins/Unity/Indicators/resources/artwork/messaging/default_contact.png → plugins/Unity/IndicatorsLegacy/resources/artwork/messaging/default_contact.png plugins/Unity/Indicators/resources/artwork/messaging/message_sms01_54px.png → plugins/Unity/IndicatorsLegacy/resources/artwork/messaging/message_sms01_54px.png plugins/Unity/Indicators/resources/indicators.qrc → plugins/Unity/IndicatorsLegacy/resources/indicators.qrc plugins/Unity/Launcher/backend/launcherbackend.cpp+1−1 plugins/Unity/Launcher/common/quicklistentry.cpp+5 plugins/Unity/Launcher/common/quicklistentry.h+2 plugins/Unity/Launcher/launcheritem.cpp+11−1 plugins/Unity/Launcher/launchermodel.cpp+68−4 plugins/Unity/Launcher/launchermodel.h+5 plugins/Unity/Launcher/plugin.cpp+3−1 plugins/Unity/Launcher/quicklistmodel.cpp+13 plugins/Unity/Launcher/quicklistmodel.h+10 plugins/Unity/abstractfilteroption.cpp+25 plugins/Unity/abstractfilteroption.h+58 plugins/Unity/categories.cpp+72 plugins/Unity/categories.h+6 plugins/Unity/checkoptionfilter.cpp+63 plugins/Unity/checkoptionfilter.h+57 plugins/Unity/filter.cpp+129 plugins/Unity/filter.h+74 plugins/Unity/filteroption.cpp+91 plugins/Unity/filteroption.h+61 plugins/Unity/filters.cpp+165 plugins/Unity/filters.h+77 plugins/Unity/genericoptionsmodel.cpp+170 plugins/Unity/genericoptionsmodel.h+77 plugins/Unity/multirangefilter.cpp+63 plugins/Unity/multirangefilter.h+60 plugins/Unity/plugin.cpp+4−1 plugins/Unity/radiooptionfilter.cpp+59 plugins/Unity/radiooptionfilter.h+59 plugins/Unity/ratingfilteroption.cpp+62 plugins/Unity/ratingfilteroption.h+53 plugins/Unity/ratingoptionsmodel.cpp+33 plugins/Unity/ratingoptionsmodel.h+33 plugins/Unity/ratingsfilter.cpp+72 plugins/Unity/ratingsfilter.h+66 plugins/Unity/scope.cpp+27−9 plugins/Unity/scope.h+7 plugins/Unity/signalslist.cpp+39 plugins/Unity/signalslist.h+38 plugins/Unity/unityoptionsmodel.cpp+67 plugins/Unity/unityoptionsmodel.h+49 plugins/Utils/plugin.cpp+1−1 plugins/Utils/qlimitproxymodelqml.cpp+90−4 plugins/Utils/qlimitproxymodelqml.h+2 plugins/Utils/qsortfilterproxymodelqml.cpp+1−1 po/ar.po+42−6 po/az.po+41−6 po/bg.po+41−6 po/ca.po+41−6 po/de.po+41−6 po/el.po+41−6 po/en_AU.po+41−6 po/en_GB.po+41−6 po/eo.po+41−6 po/es.po+41−6 po/fi.po+44−9 po/fo.po+41−6 po/fr.po+41−6 po/gl.po+41−6 po/he.po+41−6 po/hr.po+42−6 po/hu.po+41−6 po/hy.po+41−6 po/id.po+41−6 po/it.po+41−6 po/ja.po+41−6 po/kn.po+41−6 po/ko.po+41−6 po/ky.po+41−6 po/lv.po+41−6 po/my.po+41−6 po/nb.po+41−6 po/nl.po+41−6 po/pl.po+42−6 po/pt_BR.po+41−6 po/ro.po+42−6 po/ru.po+42−6 po/si.po+41−6 po/sl.po+42−6 po/sr.po+42−6 po/sv.po+41−6 po/te.po+41−6 po/tr.po+41−6 po/ug.po+41−6 po/uk.po+42−6 po/zh_CN.po+41−6 po/zh_TW.po+41−6 src/Panel/Indicators/client/indicatorsclient.cpp+12 src/Panel/Indicators/servicefiles/CMakeLists.txt−4 src/Panel/Indicators/servicefiles/com.canonical.indicator.battery−10 src/Panel/Indicators/servicefiles/com.canonical.indicator.messages+1−1 src/Panel/Indicators/servicefiles/com.canonical.indicator.time−10 src/Panel/Indicators/servicefiles/com.canonical.settings.network−10 src/Panel/Indicators/servicefiles/com.canonical.settings.sound−10 tests/autopilot/unity8/indicators_client/tests/test_battery.py+14−8 tests/autopilot/unity8/shell/emulators/create_interactive_notification.py+102 tests/autopilot/unity8/shell/emulators/dash.py+3−3 tests/autopilot/unity8/shell/tests/__init__.py+20−20 tests/autopilot/unity8/shell/tests/test_lock_screen.py+17−2 tests/autopilot/unity8/shell/tests/test_notifications.py+737 tests/data/unity/backgrounds/blue.png tests/data/unity/backgrounds/red.png tests/mocks/CMakeLists.txt+3−1 tests/mocks/GSettings.1.0/CMakeLists.txt+23 tests/mocks/GSettings.1.0/fake_gsettings.cpp+102 tests/mocks/GSettings.1.0/fake_gsettings.h+90 tests/mocks/GSettings.1.0/plugin.cpp+33 tests/mocks/GSettings.1.0/plugin.h+31 tests/mocks/GSettings.1.0/qmldir+2 tests/mocks/Powerd/CMakeLists.txt+21 tests/mocks/Powerd/Powerd.cpp+24 tests/mocks/Powerd/Powerd.h+49 tests/mocks/Powerd/plugin.cpp+35 tests/mocks/Powerd/plugin.h+34 tests/mocks/Powerd/qmldir+2 tests/mocks/Ubuntu/DownloadDaemonListener/plugin.cpp−1 tests/mocks/Unity/Indicators/IndicatorsModel.qml+1−1 tests/mocks/Unity/Indicators/fakeplugin.cpp+1−1 tests/mocks/Unity/Launcher/MockLauncherItem.cpp+2−2 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+48−18 tests/mocks/Unity/Launcher/MockLauncherModel.h+4 tests/mocks/Unity/Launcher/MockQuickListModel.cpp+2 tests/mocks/Unity/Launcher/plugin.cpp+3 tests/mocks/Unity/fake_unity_plugin.cpp+2−1 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp+44−1 tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsection.cpp+44−1 tests/plugins/Unity/CMakeLists.txt+9−1 tests/plugins/Unity/Indicators/indicatorsmanagertest.cpp−1 tests/plugins/Unity/filtersbindingstest.cpp+484 tests/plugins/Unity/filtersbindingstest.h+47 tests/plugins/Unity/signalslisttest.cpp+57 tests/plugins/Unity/signalslisttest.h+30 tests/plugins/Utils/qlimitproxymodeltest.cpp+133−8 tests/qmltests/CMakeLists.txt+1−1 tests/qmltests/Components/tst_FilterGrid.qml−35 tests/qmltests/Dash/Apps/tst_AppPreview.qml+17−3 tests/qmltests/Greeter/tst_Clock.qml+1−1 tests/qmltests/Launcher/tst_Launcher.qml+183−6 tests/qmltests/Panel/tst_MenuContent.qml+32−47 tests/qmltests/tst_Shell.qml+77−18 tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+1−1 tests/utils/modules/Unity/Test/UnityTestCase.qml+21−2
Bottombar/Bottombar.qml+60−6
@@ -18,6 +18,7 @@ import QtQuick 2.0
1818import "../Components"
1919import "../Components/Math.js" as MathLocal
2020import Unity 0.1
21+import Ubuntu.Application 0.1
2122import Ubuntu.Gestures 0.1
2223
2324Item {
@@ -28,15 +29,46 @@ Item {
2829
2930 property variant theHud
3031 property bool enabled: false
32+ property bool preventHiding: dragArea.dragging
3133 readonly property real bottomEdgeButtonCenterDistance: units.gu(34)
3234
33− state: dragArea.status === DirectionalDragArea.Recognized ? "shown" : "hidden"
35+ state: "hidden"
36+
37+ onStateChanged: {
38+ if (state == "hidden") {
39+ dismissTimer.stop()
40+ } else {
41+ dismissTimer.restart()
42+ }
43+ }
44+
45+ onPreventHidingChanged: {
46+ if (dismissTimer.running) {
47+ dismissTimer.restart();
48+ }
49+ }
50+
51+ Timer {
52+ id: dismissTimer
53+ interval: 5000
54+ onTriggered: {
55+ if (!bottombar.preventHiding) {
56+ bottombar.state = "hidden"
57+ } else {
58+ dismissTimer.restart()
59+ }
60+ }
61+ }
3462
3563 HudButton {
3664 id: hudButton
3765
38− x: MathLocal.clamp(dragArea.touchStartX - (width / 2), 0, bottombar.width - width)
66+ readonly property bool centeredHud: parent.width < units.gu(68) // Nexus 7 has 67 gu width
67+
68+ x: centeredHud ? parent.width / 2 - width / 2 : MathLocal.clamp(dragArea.touchStartX - (width / 2), 0, bottombar.width - width)
3969 y: bottombar.height - bottomEdgeButtonCenterDistance - (height / 2) - bottomMargin
70+ z: 1
71+ visible: opacity != 0
4072
4173 mouseOver: {
4274 if (dragArea.status === DirectionalDragArea.Recognized) {
@@ -55,13 +87,18 @@ Item {
5587 }
5688
5789 Behavior on opacity {
58− NumberAnimation{ duration: 200; easing.type: Easing.OutCubic}
90+ NumberAnimation{duration: 200; easing.type: Easing.OutCubic}
5991 }
6092 }
6193
6294 Connections {
6395 target: theHud
64− onShownChanged: bottomBarVisibilityCommunicatorShell.forceHidden = theHud.shown
96+ onShownChanged: {
97+ bottomBarVisibilityCommunicatorShell.forceHidden = theHud.shown
98+ if (theHud.shown) {
99+ bottombar.state = "hidden"
100+ }
101+ }
65102 }
66103
67104 EdgeDragArea {
@@ -70,6 +107,7 @@ Item {
70107 height: distanceThreshold
71108 anchors.bottom: parent.bottom
72109
110+ distanceThreshold: units.gu(8)
73111 enabled: !theHud.shown && bottombar.enabled && applicationIsOnForeground
74112 direction: Direction.Upwards
75113
@@ -84,12 +122,29 @@ Item {
84122 }
85123 }
86124 } else if (status === DirectionalDragArea.Undecided) {
87− touchStartX = touchX
125+ if (!hudButton.centeredHud) {
126+ touchStartX = touchX
127+ }
128+ } else if (status === DirectionalDragArea.Recognized) {
129+ bottombar.state = "shown"
88130 }
89131 previousStatus = status
90132 }
91133 }
92134
135+ MouseArea {
136+ anchors.fill: parent
137+ enabled: bottombar.state == "shown"
138+ onPressed: {
139+ bottombar.state = "hidden"
140+ }
141+ }
142+
143+ InputFilterArea {
144+ anchors.fill: parent
145+ blockInput: hudButton.visible
146+ }
147+
93148 states: [
94149 State {
95150 name: "hidden"
@@ -102,5 +157,4 @@ Item {
102157 PropertyChanges { target: hudButton; bottomMargin: units.gu(0)}
103158 }
104159 ]
105−
106160}
Bottombar/HudButton.qml+8−8
@@ -33,24 +33,24 @@ Item {
3333
3434 Item {
3535 id: hudButton
36− property alias actionButton: actionButton
3736
3837 anchors.centerIn: parent
3938 height: units.gu(12)
4039 width: height
41− opacity: item.mouseOver ? 1 : 0.7
42− scale: item.mouseOver ? scaleOnMouseOver : 1
40+ opacity: item.mouseOver || abstractButton.pressed ? 1 : 0.7
41+ scale: item.mouseOver || abstractButton.pressed ? scaleOnMouseOver : 1
4342 Behavior on opacity {NumberAnimation{duration: 200; easing.type: Easing.OutQuart}}
4443 Behavior on scale {NumberAnimation{duration: 200; easing.type: Easing.OutQuart}}
4544
46− UbuntuShape {
47− id: actionButton
45+ AbstractButton {
46+ id: abstractButton
4847 anchors.fill: parent
49−
50− borderSource: ""
51− image: Image {
48+ style: Image {
49+ anchors.fill: parent
5250 source: "graphics/hud_invoke_button_active.png"
5351 }
52+
53+ onClicked: item.clicked()
5454 }
5555
5656 Image {
Components/CrossFadeImage.qml+3−1
@@ -85,7 +85,7 @@ Item {
8585 Connections {
8686 target: __nextImage
8787 onStatusChanged: {
88− if (__nextImage.status == Image.Ready) {
88+ if (__nextImage.status == Image.Ready || __nextImage.status == Image.Error) {
8989 swapImages();
9090 }
9191 }
@@ -96,6 +96,7 @@ Item {
9696 anchors.fill: parent
9797 cache: false
9898 asynchronous: true
99+ fillMode: Image.PreserveAspectCrop
99100 z: 1
100101 }
101102
@@ -104,6 +105,7 @@ Item {
104105 anchors.fill: parent
105106 cache: false
106107 asynchronous: true
108+ fillMode: Image.PreserveAspectCrop
107109 z: 0
108110 }
109111
Components/FilterGrid.qml+31−61
@@ -35,12 +35,18 @@ Item {
3535
3636 /* Whether, when collapsed, a button should be displayed enabling the user to expand
3737 the grid to its full size. */
38− property bool expandable: true
38+ readonly property bool expandable: model.count > collapsedRowCount * iconTileGrid.columns
3939
4040 property var model: null
4141
4242 /* Maximum number of rows to be show when filter=true. */
4343 property int collapsedRowCount: 2
44+ readonly property int collapsedHeight: {
45+ return iconTileGrid.contentHeightForRows(Math.min(collapsedRowCount, Math.ceil(model.count / columns)))
46+ }
47+ readonly property int uncollapsedHeight: {
48+ return iconTileGrid.contentHeightForRows(Math.ceil(model.count / columns))
49+ }
4450
4551 property alias minimumHorizontalSpacing: iconTileGrid.minimumHorizontalSpacing
4652 property alias maximumNumberOfColumns: iconTileGrid.maximumNumberOfColumns
@@ -55,7 +61,29 @@ Item {
5561 readonly property alias moving: iconTileGrid.moving
5662 readonly property alias pressDelay: iconTileGrid.pressDelay
5763
58− height: childrenRect.height
64+ height: !filterAnimation.running ? childrenRect.height : height
65+ clip: filterAnimation.running
66+
67+ NumberAnimation {
68+ property bool filterEndValue
69+ id: filterAnimation
70+ target: root
71+ property: "height"
72+ to: filterEndValue ? root.collapsedHeight : root.uncollapsedHeight
73+ // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation
74+ // otherwise since both animations can run at the same time you'll get
75+ // some visual weirdness.
76+ duration: 200
77+ easing.type: Easing.InOutQuad
78+ onStopped: {
79+ root.filter = filterEndValue;
80+ }
81+ }
82+
83+ function startFilterAnimation(filter) {
84+ filterAnimation.filterEndValue = filter
85+ filterAnimation.start();
86+ }
5987
6088 ResponsiveGridView {
6189 id: iconTileGrid
@@ -72,66 +100,8 @@ Item {
72100
73101 model: LimitProxyModel {
74102 model: root.model
75− limit: (filter) ? collapsedRowCount * iconTileGrid.columns : -1
103+ limit: (filter && !filterAnimation.running) ? collapsedRowCount * iconTileGrid.columns : -1
76104 }
77105
78106 }
79−
80− Item {
81− anchors {
82− left: parent.left
83− right: parent.right
84− top: iconTileGrid.bottom
85− }
86− visible: (expandable && filter && model.count > collapsedRowCount * iconTileGrid.columns)
87− height: (visible) ? childrenRect.height + units.gu(2) : 0
88−
89− AbstractButton {
90− id: button
91− objectName: "filterToggleButton"
92−
93− anchors {
94− top: parent.top
95− horizontalCenter: parent.horizontalCenter
96− }
97− width: units.gu(22)
98− height: units.gu(4)
99−
100− UbuntuShape {
101− anchors.fill: parent
102− color: "#33ffffff" // FIXME no palette
103− radius: "small"
104− }
105−
106− UbuntuShape {
107− id: borderPressed
108−
109− anchors.fill: parent
110− radius: "small"
111− borderSource: "radius_pressed.sci"
112− opacity: button.pressed ? 1.0 : 0.0
113− Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
114− }
115−
116− Label {
117− anchors {
118− verticalCenter: parent.verticalCenter
119− left: parent.left
120− right: parent.right
121− leftMargin: units.gu(1)
122− rightMargin: units.gu(1)
123− }
124− text: (filter) ? "+ View all (" + model.count + ")" : "- Show fewer"
125− fontSize: "small"
126− color: Theme.palette.selected.backgroundText
127− opacity: 0.6
128− style: Text.Raised
129− styleColor: "black"
130− elide: Text.ElideMiddle
131− horizontalAlignment: Text.AlignHCenter
132− }
133−
134− onClicked: filter = !filter
135− }
136− }
137107}
Components/ListItems/Header.qml+19−2
@@ -24,6 +24,9 @@ Item {
2424 \qmlproperty string text
2525 */
2626 property alias text: label.text
27+ property alias image: image.source
28+
29+ signal clicked(variant mouse)
2730
2831 height: units.gu(4.5)
2932
@@ -31,6 +34,7 @@ Item {
3134 anchors {
3235 left: parent.left
3336 right: parent.right
37+ rightMargin: units.gu(0.5)
3438 top: parent.top
3539 bottom: bottomBorder.top
3640 }
@@ -40,8 +44,6 @@ Item {
4044 anchors {
4145 left: parent.left
4246 leftMargin: units.gu(2)
43− right: parent.right
44− rightMargin: units.gu(0.5)
4547 verticalCenter: parent.verticalCenter
4648 }
4749
@@ -53,6 +55,16 @@ Item {
5355 textFormat: Text.PlainText
5456 style: Text.Raised
5557 styleColor: "black"
58+ width: parent.width - image.width - image.leftMargin - anchors.leftMargin
59+ }
60+
61+ Image {
62+ id: image
63+ readonly property double leftMargin: units.gu(1)
64+ x: label.x + label.contentWidth + leftMargin
65+ anchors {
66+ verticalCenter: parent.verticalCenter
67+ }
5668 }
5769 }
5870
@@ -64,4 +76,9 @@ Item {
6476 bottom: parent.bottom
6577 }
6678 }
79+
80+ MouseArea {
81+ anchors.fill: parent
82+ onClicked: parent.clicked(mouse)
83+ }
6784}
Components/ResponsiveGridView.qml+7−1
@@ -32,12 +32,18 @@ Item {
3232 property alias delegate: gridView.delegate
3333 readonly property int cellWidth: gridView.cellWidth
3434 readonly property int cellHeight: gridView.cellHeight
35− readonly property int totalContentHeight: Math.ceil(gridView.model.count / columns) * cellHeight + verticalSpacing
35+ readonly property int totalContentHeight: {
36+ return contentHeightForRows(Math.ceil(gridView.model.count / columns))
37+ }
3638 property alias interactive: gridView.interactive
3739 readonly property alias flicking: gridView.flicking
3840 readonly property alias moving: gridView.moving
3941 readonly property alias pressDelay: gridView.pressDelay
4042
43+ function contentHeightForRows(rows) {
44+ return rows * cellHeight + verticalSpacing
45+ }
46+
4147 GridView {
4248 id: gridView
4349 objectName: "responsiveGridViewGrid"
Components/Tile.qml−6
@@ -84,10 +84,4 @@ AbstractButton {
8484 horizontalAlignment: Text.AlignHCenter
8585 }
8686 }
87−
88− GridView.onRemove: SequentialAnimation {
89− PropertyAction { target: root; property: "GridView.delayRemove"; value: true }
90− NumberAnimation { target: root; property: "opacity"; from: 1; to: 0; duration: 200; easing.type: Easing.InOutQuad }
91− PropertyAction { target: root; property: "GridView.delayRemove"; value: false }
92− }
9387}
Dash/Apps/AppInfo.qml+1
@@ -53,6 +53,7 @@ Row {
5353 }
5454
5555 Row {
56+ visible: root.rating >= 0
5657 spacing: units.gu(1)
5758
5859 RatingStars {
Dash/Apps/AppPreview.qml+37−32
@@ -38,7 +38,7 @@ DashPreview {
3838 right: parent.right
3939 margins: units.gu(1)
4040 }
41− model: previewData.infoMap["more-screenshots"].value
41+ model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
4242
4343 delegate: UbuntuShape {
4444 id: shape
@@ -138,9 +138,9 @@ DashPreview {
138138
139139 appName: root.previewData.title
140140 icon: IconUtil.from_gicon(root.previewData.appIcon)
141− rating: root.previewData.infoMap["rating"] ? root.previewData.infoMap["rating"].value : 0
141+ rating: Math.round(root.previewData.rating * 10)
142+ reviews: root.previewData.numRatings
142143 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
143− reviews: root.previewData.infoMap["reviews"] ? root.previewData.infoMap["reviews"].value : 0
144144 }
145145
146146 Label {
@@ -154,42 +154,47 @@ DashPreview {
154154 styleColor: "black"
155155 }
156156
157− ListItem.ThinDivider {}
158−
159− Item {
157+ Column {
158+ visible: root.previewData.rating >= 0
160159 anchors { left: parent.left; right: parent.right }
161− height: rateLabel.height
162−
163− Label {
164− id: rateLabel
165− fontSize: "medium"
166− color: "white"
167− style: Text.Raised
168− styleColor: "black"
169− opacity: .9
170− text: i18n.tr("Rate this")
171−
172− anchors.left: parent.left
173− anchors.verticalCenter: parent.verticalCenter
174− }
160+ spacing: parent.spacing
161+
162+ ListItem.ThinDivider { }
163+
164+ Item {
165+ anchors { left: parent.left; right: parent.right }
166+ height: rateLabel.height
167+
168+ Label {
169+ id: rateLabel
170+ fontSize: "medium"
171+ color: "white"
172+ style: Text.Raised
173+ styleColor: "black"
174+ opacity: .9
175+ text: i18n.tr("Rate this")
176+
177+ anchors.left: parent.left
178+ anchors.verticalCenter: parent.verticalCenter
179+ }
175180
176− // FIXME these need to be made interactive and connected to the scope
177− RatingStars {
178− anchors.right: parent.right
179− anchors.verticalCenter: parent.verticalCenter
181+ // FIXME these need to be made interactive and connected to the scope
182+ RatingStars {
183+ anchors.right: parent.right
184+ anchors.verticalCenter: parent.verticalCenter
185+ }
180186 }
181− }
182187
183− ListItem.ThinDivider {}
188+ ListItem.ThinDivider { }
184189
185− AppReviews {
186− objectName: "appReviews"
187− anchors { left: parent.left; right: parent.right }
190+ AppReviews {
191+ objectName: "appReviews"
192+ anchors { left: parent.left; right: parent.right }
188193
189− model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
194+ model: root.previewData.infoMap["comments"] ? root.previewData.infoMap["comments"].value : undefined
190195
191− onSendReview: root.sendUserReview(review);
196+ onSendReview: root.sendUserReview(review);
197+ }
192198 }
193−
194199 }
195200}
Dash/Apps/ApplicationsFilterGrid.qml−1
@@ -23,7 +23,6 @@ FilterGrid {
2323 id: filterGrid
2424
2525 filter: false
26− expandable: false
2726 minimumHorizontalSpacing: units.gu(0.5)
2827 maximumNumberOfColumns: 6
2928 delegateWidth: units.gu(11)
Dash/Apps/AppsAvailableForDownloadModel.qml+61−6
@@ -15,12 +15,67 @@
1515 */
1616
1717import QtQuick 2.0
18+import "../../Applications"
1819
1920ListModel {
20− ListElement { desktopFile: "/usr/share/applications/evernote.desktop"; }
21− ListElement { desktopFile: "/usr/share/applications/map.desktop"; }
22− ListElement { desktopFile: "/usr/share/applications/pinterest.desktop"; }
23− ListElement { desktopFile: "/usr/share/applications/soundcloud.desktop"; }
24− ListElement { desktopFile: "/usr/share/applications/wikipedia.desktop"; }
25− ListElement { desktopFile: "/usr/share/applications/youtube.desktop"; }
21+ ListElement {
22+ uri: "application://evernote.desktop"
23+ icon: "../../graphics/applicationIcons/evernote.png"
24+ category: 0
25+ mimetype: "application/x-desktop"
26+ title: "Evernote"
27+ comment: ""
28+ dndUri: "file:///usr/share/applications/evernote.desktop"
29+ metadata: ""
30+ }
31+ ListElement {
32+ uri: "application://map.desktop"
33+ icon: "../../graphics/applicationIcons/map.png"
34+ category: 0
35+ mimetype: "application/x-desktop"
36+ title: "Map"
37+ comment: ""
38+ dndUri: "file:///usr/share/applications/map.desktop"
39+ metadata: ""
40+ }
41+ ListElement {
42+ uri: "application://pinterest.desktop"
43+ icon: "../../graphics/applicationIcons/pinterest.png"
44+ category: 0
45+ mimetype: "application/x-desktop"
46+ title: "Pinterest"
47+ comment: ""
48+ dndUri: "file:///usr/share/applications/pinterest.desktop"
49+ metadata: ""
50+ }
51+ ListElement {
52+ uri: "application://soundcloud.desktop"
53+ icon: "../../graphics/applicationIcons/soundcloud.png"
54+ category: 0
55+ mimetype: "application/x-desktop"
56+ title: "Soundcloud"
57+ comment: ""
58+ dndUri: "file:///usr/share/applications/soundcloud.desktop"
59+ metadata: ""
60+ }
61+ ListElement {
62+ uri: "application://wikipedia.desktop"
63+ icon: "../../graphics/applicationIcons/wikipedia.png"
64+ category: 0
65+ mimetype: "application/x-desktop"
66+ title: "Wikipedia"
67+ comment: ""
68+ dndUri: "file:///usr/share/applications/wikipedia.desktop"
69+ metadata: ""
70+ }
71+ ListElement {
72+ uri: "application://youtube.desktop"
73+ icon: "../../graphics/applicationIcons/youtube.png"
74+ category: 0
75+ mimetype: "application/x-desktop"
76+ title: "Youtube"
77+ comment: ""
78+ dndUri: "file:///usr/share/applications/youtube.desktop"
79+ metadata: ""
80+ }
2681}
Dash/Apps/FrequentlyUsedAppsModel.qml+60−6
@@ -17,10 +17,64 @@
1717import QtQuick 2.0
1818
1919ListModel {
20− ListElement { desktopFile: "/usr/share/applications/camera-app.desktop"; }
21− ListElement { desktopFile: "/usr/share/applications/phone-app.desktop"; }
22− ListElement { desktopFile: "/usr/share/applications/gallery-app.desktop"; }
23− ListElement { desktopFile: "/usr/share/applications/facebook-webapp.desktop"; }
24− ListElement { desktopFile: "/usr/share/applications/webbrowser-app.desktop"; }
25− ListElement { desktopFile: "/usr/share/applications/gmail-webapp.desktop"; }
20+ ListElement {
21+ uri: "application:///usr/share/applications/camera-app.desktop"
22+ icon: "../../graphics/applicationIcons/camera.png"
23+ category: 0
24+ mimetype: "application/x-desktop"
25+ title: "Camera"
26+ comment: ""
27+ dndUri: "file:///usr/share/applications/camera-app.desktop"
28+ metadata: ""
29+ }
30+ ListElement {
31+ uri: "application:///usr/share/applications/phone-app.desktop"
32+ icon: "../../graphics/applicationIcons/phone-app.png"
33+ category: 0
34+ mimetype: "application/x-desktop"
35+ title: "Phone"
36+ comment: ""
37+ dndUri: "file:///usr/share/applications/phone-app.desktop"
38+ metadata: ""
39+ }
40+ ListElement {
41+ uri: "application:///usr/share/applications/gallery-app.desktop"
42+ icon: "../../graphics/applicationIcons/gallery.png"
43+ category: 0
44+ mimetype: "application/x-desktop"
45+ title: "Gallery"
46+ comment: ""
47+ dndUri: "file:///usr/share/applications/gallery.desktop"
48+ metadata: ""
49+ }
50+ ListElement {
51+ uri: "application:///usr/share/applications/facebook-webapp.desktop"
52+ icon: "../../graphics/applicationIcons/facebook.png"
53+ category: 0
54+ mimetype: "application/x-desktop"
55+ title: "Facebook"
56+ comment: ""
57+ dndUri: "file:///usr/share/applications/facebook-webapp.desktop"
58+ metadata: ""
59+ }
60+ ListElement {
61+ uri: "application:///usr/share/applications/webbrowser-app.desktop"
62+ icon: "../../graphics/applicationIcons/browser.png"
63+ category: 0
64+ mimetype: "application/x-desktop"
65+ title: "Browser"
66+ comment: ""
67+ dndUri: "file:///usr/share/applications/webbrowser-app.desktop"
68+ metadata: ""
69+ }
70+ ListElement {
71+ uri: "application:///usr/share/applications/gmail-webapp.desktop"
72+ icon: "../../graphics/applicationIcons/gmail.png"
73+ category: 0
74+ mimetype: "application/x-desktop"
75+ title: "GMail"
76+ comment: ""
77+ dndUri: "file:///usr/share/applications/gmail-webapp.desktop"
78+ metadata: ""
79+ }
2680}
Dash/DashApps.qml+16−225
@@ -19,246 +19,37 @@ import Ubuntu.Components 0.1
1919import Utils 0.1
2020import "../Components"
2121import "../Components/ListItems"
22+import "../Applications"
2223import "Apps"
2324
24−ScopeView {
25+GenericScopeView {
2526 id: scopeView
2627
2728 // FIXME: a way to aggregate these models would be ideal
2829 property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications
2930 property var sideStageApplicationModel: shell.applicationManager.sideStageApplications
3031
31− onMovementStarted: categoryView.showHeader()
32− onPositionedAtBeginning: {
33− if (isCurrent) {
34− categoryView.positionAtBeginning()
35− }
36− }
37−
38− onIsCurrentChanged: {
39− pageHeader.resetSearch();
40− }
41−
42− Binding {
43− target: scopeView.scope
44− property: "searchQuery"
45− value: pageHeader.searchQuery
46− }
47−
48− Connections {
49− target: panel
50− onSearchClicked: if (isCurrent) {
51− pageHeader.triggerSearch()
52− categoryView.showHeader()
53− }
54− }
55−
5632 ListModel {
57− id: categoryListModel
58− // specifies app categories, type of delegate and model used in each category
59− ListElement { category: "Running apps"; component: "Apps/RunningApplicationsGrid.qml"; modelName: "RunningApplicationsModel" }
60− ListElement { category: "Frequently used"; component: "Apps/ApplicationsFilterGrid.qml"; modelName: "FrequentlyUsedAppsModel" }
61− ListElement { category: "Installed"; component: "Apps/ApplicationsFilterGrid.qml"; modelName: "InstalledApplicationsModel" }
62− ListElement { category: "Available for download"; component: "Apps/ApplicationsFilterGrid.qml"; modelName: "AppsAvailableForDownloadModel" }
63− function getCategory(category1) {
64− if (category1 === "Running apps") {
65− return i18n.tr("Running apps");
66− }
67− if (category1 === "Frequently used") {
68− return i18n.tr("Frequently used");
69− }
70− if (category1 === "Installed") {
71− return i18n.tr("Installed");
72− }
73− if (category1 === "Available for download") {
74− return i18n.tr("Available for download");
75− }
76− return ""
77− }
78− }
79−
80− FrequentlyUsedAppsModel { id: frequentlyUsedAppsModel }
81− AppsAvailableForDownloadModel { id: appsAvailableForDownloadModel }
82−
83− // FIXME this should not be needed, the backend should handle all that itself
84− property var installedModel: null
85−
86− Repeater {
87− model: scopeView.scope.categories
88−
89− delegate: Item {
90− Component.onCompleted: if (categoryId == "installed") scopeView.installedModel = Qt.binding(function() { return model.results; });
91− }
92− }
93−
94− property var categoryModels: {
95− "FrequentlyUsedAppsModel": frequentlyUsedAppsModel,
96− "InstalledApplicationsModel": installedModel,
97− "AppsAvailableForDownloadModel": appsAvailableForDownloadModel,
98− }
99−
100− ScopeListView {
101− id: categoryView
102− anchors.fill: parent
103− model: SortFilterProxyModel {
104− model: categoryListModel
105− filterRole: 0 // 0 == modelName
106− // FIXME: need to use invertMatch here, otherwise the filter won't update correctly
107− // if filterRegExp is set before invertMatch. Bug in SortFilterProxyModel?
108− filterRegExp: invertMatch ? ((mainStageApplicationsModel.count === 0
109− && sideStageApplicationModel.count === 0) ? RegExp("RunningApplicationsModel") : RegExp("")) :
110− RegExp("InstalledApplicationsModel")
111− invertMatch: pageHeader.searchQuery.length == 0
112− }
113−
114− onAtYEndChanged: if (atYEnd) endReached()
115− onMovingChanged: if (moving && atYEnd) endReached()
33+ id: dummyVisibilityModifier
11634
117− delegate: Base {
118− id: container
119− highlightWhenPressed: false
120−
121− Loader {
122− anchors {
123− top: parent.top
124− left: parent.left
125− right: parent.right
126− }
127− source: component
128− onLoaded: {
129− if (modelName == "RunningApplicationsModel") {
130− item.firstModel = mainStageApplicationsModel
131− item.secondModel = sideStageApplicationModel
132− item.canEnableTerminationMode =
133− Qt.binding(function() { return isCurrent; })
134− } else {
135− function activateApplication(index, data) {
136− shell.activateApplication(data);
137− }
138−
139− item.model = Qt.binding(function() { return categoryModels[modelName]; });
140− item.clicked.connect(activateApplication);
141−
142− }
143− }
144− }
145−
146− ListView.onRemove: SequentialAnimation {
147− PropertyAction {
148− target: container; property: "ListView.delayRemove"; value: true
149− }
150− NumberAnimation {
151− target: container; property: "height"; to: 0;
152− duration: 250; easing.type: Easing.InOutQuad
153− }
154− PropertyAction {
155− target: container; property: "ListView.delayRemove"; value: false
156− }
157− }
158− }
159−
160− sectionProperty: "category"
161− sectionDelegate: Header {
162− width: categoryView.width
163− text: categoryListModel.getCategory(section)
164− }
165− pageHeader: PageHeader {
166− id: pageHeader
167− width: categoryView.width
168− text: i18n.tr("Apps")
169− searchEntryEnabled: true
170− searchHistory: scopeView.searchHistory
171− }
172− }
173−
174− OpenEffect {
175− id: effect
176− anchors {
177− fill: parent
178− bottomMargin: -bottomOverflow
179− }
180− sourceItem: categoryView
181−
182− enabled: gap > 0.0
183−
184− topGapPx: (1 - gap) * positionPx
185− topOpacity: (1 - gap * 1.2)
186− bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
187− bottomOverflow: units.gu(20)
188− bottomOpacity: 1 - (gap * 0.8)
189−
190− property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
191− property real gap: previewLoader.open ? 1.0 : 0.0
192−
193− Behavior on gap {
194− NumberAnimation {
195− duration: 200
196− easing.type: Easing.InOutQuad
197− onRunningChanged: {
198− if (!previewLoader.open && !running) {
199− previewLoader.onScreen = false
200− }
201− }
202− }
203− }
35+ ListElement { name: "running-apps" }
20436 }
20537
206− Connections {
207− target: scopeView.scope
208− onPreviewReady: {
209− previewLoader.previewData = preview
210− previewLoader.open = true
211− }
212− }
213−
214− Connections {
215− ignoreUnknownSignals: true
216− target: previewLoader.valid ? previewLoader.item : null
217− onClose: {
218− previewLoader.open = false
219− }
220− }
221−
222− PreviewDelegateMapper {
223− id: previewDelegateMapper
224− }
225−
226− Loader {
227− id: previewLoader
228− property var previewData
229− height: effect.bottomGapPx - effect.topGapPx
230− anchors {
231− top: parent.top
232− topMargin: effect.topGapPx
233− left: parent.left
234− right: parent.right
235− }
236− source: onScreen ? previewDelegateMapper.map("preview-application") : ""
237−
238− property bool open: false
239− property bool onScreen: false
240− property bool valid: item !== null
38+ SortFilterProxyModel {
39+ id: runningApplicationsModel
24140
242− onOpenChanged: {
243− if (open) {
244− onScreen = true
245− }
246− }
41+ property var firstModel: mainStageApplicationsModel
42+ property var secondModel: sideStageApplicationModel
43+ property bool canEnableTerminationMode: scopeView.isCurrent
24744
248− onLoaded: {
249− item.previewData = previewLoader.previewData
250− }
45+ model: dummyVisibilityModifier
46+ filterRole: 0
47+ filterRegExp: invertMatch ? ((mainStageApplicationsModel.count === 0 &&
48+ sideStageApplicationModel.count === 0) ? RegExp("running-apps") : RegExp("")) : RegExp("disabled")
49+ invertMatch: scopeView.scope.searchQuery.length == 0
25150 }
25251
253− // TODO: Move as InverseMouseArea to DashPreview
254− MouseArea {
255− enabled: previewLoader.onScreen
256− anchors {
257− fill: parent
258− topMargin: effect.bottomGapPx
259− }
260− onClicked: {
261− previewLoader.open = false;
262− }
52+ onScopeChanged: {
53+ scopeView.scope.categories.overrideResults("recent", runningApplicationsModel);
26354 }
26455}
Dash/DashHome.qml+8−123
@@ -19,136 +19,21 @@ import Ubuntu.Components 0.1
1919import Utils 0.1
2020import "../Components"
2121import "../Components/ListItems"
22−import "../Applications"
2322import "Apps"
24−import "Video"
25−import "Music"
2623
27−ScopeView {
24+GenericScopeView {
2825 id: root
2926 objectName: "DashHome"
3027
31− onMovementStarted: listView.showHeader()
28+ SearchableResultModel {
29+ id: appsModel
3230
33− ListModel {
34− id: categoryListModel
35− // specifies page's content categories, type of delegate and model used in each category
36− ListElement { category: "Frequent Apps"; component: "Apps/ApplicationsFilterGrid.qml"; modelName: "AppsModel" }
37− ListElement { category: "Recent Music"; component: "Music/MusicFilterGrid.qml"; modelName: "MusicModel" }
38− ListElement { category: "Videos Popular Online"; component: "Video/VideosFilterGrid.qml"; modelName: "VideosModel" }
39− function getCategory(category1) {
40− if (category1 === "Frequent Apps") {
41− return i18n.tr("Frequent Apps");
42− }
43− if (category1 === "Recent Music") {
44− return i18n.tr("Recent Music");
45− }
46− if (category1 === "Videos Popular Online") {
47− return i18n.tr("Videos Popular Online");
48− }
49− return ""
50− }
31+ model: FrequentlyUsedAppsModel {}
32+ filterRole: 3
33+ searchQuery: root.scope.searchQuery
5134 }
5235
53− FrequentlyUsedAppsModel { id: appsModel }
54−
55− // FIXME this should be handled by the backends by populating the global search model
56− property var musicModel: null
57− property var videosModel: null
58−
59− Repeater {
60− id: musicRepeater
61−
62− delegate: Item {
63− Component.onCompleted: if (index == 1) musicModel = Qt.binding(function() { return model.results; });
64− }
65− }
66−
67− Repeater {
68− id: videosRepeater
69−
70− delegate: Item {
71− Component.onCompleted: if (index == 3) videosModel = Qt.binding(function() { return model.results; });
72− }
73− }
74−
75− Component.onCompleted: {
76− var scope = dashContent.scopes.get("mockmusicmaster.scope")
77− if (scope) {
78− musicRepeater.model = dashContent.scopes.get("mockmusicmaster.scope").categories
79− }
80− scope = dashContent.scopes.get("mockvideosmaster.scope")
81− if (scope) {
82− videosRepeater.model = dashContent.scopes.get("mockvideosmaster.scope").categories
83− }
84− }
85−
86− Connections {
87− target: dashContent
88− onScopeLoaded: switch (scopeId) {
89− case "mockmusicmaster.scope":
90− musicRepeater.model = dashContent.scopes.get("mockmusicmaster.scope").categories
91− break;
92− case "mockvideosmaster.scope":
93− videosRepeater.model = dashContent.scopes.get("mockvideosmaster.scope").categories
94− break;
95− }
96− }
97−
98− property var categoryModels: {"AppsModel": appsModel,
99− "MusicModel": musicModel,
100− "VideosModel": videosModel,
101− }
102−
103− ScopeListView {
104− id: listView
105− anchors.fill: parent
106− model: categoryListModel
107−
108− onAtYEndChanged: if (atYEnd) endReached()
109− onMovingChanged: if (moving && atYEnd) endReached()
110−
111− delegate: Base {
112− id: container
113− highlightWhenPressed: false
114− width: listView.width
115−
116− Loader {
117− anchors { top: parent.top; left: parent.left; right: parent.right }
118− source: component
119− onLoaded: {
120− item.model = Qt.binding(function() { return categoryModels[modelName]; });
121−
122− //FIXME: workaround for lack of previews for videos in Home scope.
123− //Need to connect to the clicked() signal here and act upon it here instead.
124− if (modelName === "VideosModel") {
125− function playVideo(index, data) {
126− if (data.fileUri) {
127− shell.activateApplication('/usr/share/applications/mediaplayer-app.desktop', "/usr/share/demo-assets/videos/" + data.fileUri);
128− }
129− }
130−
131− item.clicked.connect(playVideo);
132− } else if (modelName === "AppsModel") {
133− function activateApplication(index, data) {
134− shell.activateApplication(data);
135− }
136−
137− item.objectName = "dashHomeApplicationsGrid";
138− item.clicked.connect(activateApplication);
139− }
140− }
141− }
142− }
143−
144− sectionProperty: "category"
145− sectionDelegate: Header {
146− width: listView.width
147− text: listView.model.getCategory(section)
148− }
149− pageHeader: PageHeader {
150− width: listView.width
151− text: i18n.tr("Home")
152− }
36+ onScopeChanged: {
37+ root.scope.categories.overrideResults("applications.scope", appsModel);
15338 }
15439}
Dash/Generic/GenericFilterGrid.qml−1
@@ -21,7 +21,6 @@ import "../../Components/IconUtil.js" as IconUtil
2121FilterGrid {
2222 id: filtergrid
2323
24− filter: false
2524 minimumHorizontalSpacing: units.gu(0.5)
2625 delegateWidth: units.gu(11)
2726 delegateHeight: units.gu(9.5)
Dash/Generic/GenericPreview.qml+10−2
@@ -74,25 +74,33 @@ DashPreview {
7474 spacing: units.gu(2)
7575
7676 Label {
77+ anchors { left: parent.left; right: parent.right }
7778 visible: text != ""
7879 fontSize: "medium"
7980 opacity: 0.6
8081 color: "white"
81− text: previewData.subtitle
82+ text: previewData.subtitle.replace(/[\r\n]+/g, "<br />")
8283 style: Text.Raised
8384 styleColor: "black"
8485 wrapMode: Text.WordWrap
86+ textFormat: Text.RichText
87+ // FIXME: workaround for https://bugreports.qt-project.org/browse/QTBUG-33020
88+ onWidthChanged: { wrapMode = Text.NoWrap; wrapMode = Text.WordWrap }
8589 }
8690
8791 Label {
92+ anchors { left: parent.left; right: parent.right }
8893 visible: text != ""
8994 fontSize: "small"
9095 opacity: 0.6
9196 color: "white"
92− text: previewData.description
97+ text: previewData.description.replace(/[\r\n]+/g, "<br />")
9398 style: Text.Raised
9499 styleColor: "black"
95100 wrapMode: Text.WordWrap
101+ textFormat: Text.RichText
102+ // FIXME: workaround for https://bugreports.qt-project.org/browse/QTBUG-33020
103+ onWidthChanged: { wrapMode = Text.NoWrap; wrapMode = Text.WordWrap }
96104 }
97105 }
98106}
Dash/GenericScopeView.qml+59−1
@@ -53,9 +53,14 @@ ScopeView {
5353 onAtYEndChanged: if (atYEnd) endReached()
5454 onMovingChanged: if (moving && atYEnd) endReached()
5555
56+ property int expandedIndex: -1
57+
5658 delegate: ListItems.Base {
5759 highlightWhenPressed: false
5860
61+ readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
62+ readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true
63+
5964 Loader {
6065 id: rendererLoader
6166 anchors {
@@ -67,7 +72,20 @@ ScopeView {
6772 source: getRenderer(model.renderer, model.contentType)
6873
6974 onLoaded: {
70− item.model = results
75+ if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
76+ // TODO: the running apps grid doesn't support standard scope results model yet
77+ item.firstModel = results.firstModel
78+ item.secondModel = results.secondModel
79+ } else {
80+ item.model = results
81+ }
82+ item.objectName = categoryId
83+ if (item.expandable) {
84+ var shouldFilter = index != categoryView.expandedIndex;
85+ if (shouldFilter != item.filter) {
86+ item.filter = shouldFilter;
87+ }
88+ }
7189 }
7290
7391 Connections {
@@ -97,13 +115,47 @@ ScopeView {
97115 delegateItem.model.metadata)
98116 }
99117 }
118+ Connections {
119+ target: categoryView
120+ onExpandedIndexChanged: {
121+ var item = rendererLoader.item;
122+ if (item.expandable) {
123+ var shouldFilter = index != categoryView.expandedIndex;
124+ if (shouldFilter != item.filter) {
125+ // If the filter animation will be seen start it, otherwise, just flip the switch
126+ var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
127+ var growingVisible = !shouldFilter && y + height < categoryView.height;
128+ if (shrinkingVisible || growingVisible) {
129+ item.startFilterAnimation(shouldFilter)
130+ } else {
131+ item.filter = shouldFilter;
132+ }
133+ if (!shouldFilter) {
134+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
135+ }
136+ }
137+ }
138+ }
139+ }
100140 }
101141 }
102142
103143 sectionProperty: "name"
104144 sectionDelegate: ListItems.Header {
145+ property var delegate: categoryView.item(delegateIndex)
105146 width: categoryView.width
106147 text: section
148+ image: {
149+ if (delegate && delegate.expandable)
150+ return delegate.filtered ? "graphics/header_handlearrow.png" : "graphics/header_handlearrow2.png"
151+ return "";
152+ }
153+ onClicked: {
154+ if (categoryView.expandedIndex != delegateIndex)
155+ categoryView.expandedIndex = delegateIndex;
156+ else
157+ categoryView.expandedIndex = -1;
158+ }
107159 }
108160 pageHeader: PageHeader {
109161 id: pageHeader
@@ -132,6 +184,12 @@ ScopeView {
132184 }
133185 }
134186 case "carousel": return "Generic/GenericCarousel.qml";
187+ case "special": {
188+ switch (contentType) {
189+ case "apps": return "Apps/RunningApplicationsGrid.qml";
190+ default: return "Generic/GenericFilterGrid.qml";
191+ }
192+ }
135193 default: return "Generic/GenericFilterGrid.qml";
136194 }
137195 }
Dash/Music/MusicFilterGrid.qml−1
@@ -20,7 +20,6 @@ import "../../Components"
2020FilterGrid {
2121 id: filterGrid
2222
23− expandable: false
2423 minimumHorizontalSpacing: units.gu(2)
2524 maximumNumberOfColumns: 5
2625 delegateWidth: units.gu(17)
Dash/ScopeView.qml+8−1
@@ -30,7 +30,14 @@ FocusScope {
3030
3131 // FIXME delay the search so that daemons have time to settle, note that
3232 // removing this will break ScopeView::test_changeScope
33− onScopeChanged: timer.restart()
33+ onScopeChanged: {
34+ timer.restart();
35+ scope.activateApplication.connect(activateApp);
36+ }
37+
38+ function activateApp(desktopFilePath) {
39+ shell.activateApplication(desktopFilePath);
40+ }
3441
3542 Timer {
3643 id: timer
Dash/SearchableResultModel.qmlnew file+40
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import Utils 0.1
18+import Unity 0.1
19+
20+SortFilterProxyModel {
21+ id: root
22+
23+ property string searchQuery: ""
24+ property bool hideOnSearch: false
25+
26+ filterRole: CategoryResults.RoleTitle
27+
28+ onSearchQueryChanged: {
29+ if (searchQuery.length == 0) {
30+ filterRegExp = RegExp("");
31+ filterCaseSensitivity = Qt.CaseInsensitive;
32+ } else if (!hideOnSearch) {
33+ setFilterWildcard(searchQuery);
34+ filterCaseSensitivity = Qt.CaseInsensitive;
35+ } else {
36+ filterRegExp = /^$/;
37+ filterCaseSensitivity = Qt.CaseInsensitive;
38+ }
39+ }
40+}
Dash/Video/VideosFilterGrid.qml−1
@@ -20,7 +20,6 @@ import "../../Components"
2020FilterGrid {
2121 id: filtergrid
2222
23− expandable: false
2423 minimumHorizontalSpacing: units.gu(0.5)
2524 maximumNumberOfColumns: 5
2625 delegateWidth: units.gu(11)
Dash/graphics/header_handlearrow2@20.pngnew file

Binary file not shown.

Dash/graphics/header_handlearrow@20.pngnew file

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

Panel/Indicators.qml+3−6
@@ -177,15 +177,12 @@ Showable {
177177 }
178178 indicatorsModel: indicatorsModel
179179 clip: !indicators.fullyOpened
180+ activeHeader: indicators.state == "hint" || indicators.state == "reveal"
180181
181182 Connections {
182183 target: indicatorRow
183− onCurrentItemIndexChanged: menuContent.updateCurrentMenu();
184− }
185−
186− function updateCurrentMenu() {
187− if (currentMenuIndex != indicatorRow.currentItemIndex) {
188− currentMenuIndex = indicatorRow.currentItemIndex;
184+ onCurrentItemIndexChanged: {
185+ menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex);
189186 }
190187 }
191188
Panel/Indicators/DatetimeIndicatorWidget.qml+1−1
@@ -19,7 +19,7 @@
1919
2020import QtQuick 2.0
2121import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
2323
2424Indicators.IndicatorWidget {
2525 id: indicatorWidget
Panel/Indicators/DefaultIndicatorPage.qml+1−1
@@ -18,7 +18,7 @@
1818 */
1919
2020import QtQuick 2.0
21−import Unity.Indicators 0.1 as Indicators
21+import Unity.IndicatorsLegacy 0.1 as Indicators
2222
2323Indicators.IndicatorPage {
2424 anchors.fill: parent
Panel/Indicators/DefaultIndicatorPage2.qmlnew file+25
@@ -0,0 +1,25 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Unity.Indicators 0.1 as Indicators
22+
23+Indicators.IndicatorPage {
24+ anchors.fill: parent
25+}
Panel/Indicators/DefaultIndicatorWidget.qml+1−1
@@ -19,7 +19,7 @@
1919
2020import QtQuick 2.0
2121import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
2323
2424Indicators.IndicatorWidget {
2525 id: indicatorWidget
Panel/Indicators/DefaultIndicatorWidget2.qmlnew file+76
@@ -0,0 +1,76 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+import Unity.Indicators 0.1 as Indicators
23+
24+Indicators.IndicatorWidget {
25+ id: indicatorWidget
26+
27+ width: itemRow.width + units.gu(1)
28+
29+ property alias label: itemLabel.text
30+ property alias iconSource: itemImage.source
31+
32+ Row {
33+ id: itemRow
34+ objectName: "itemRow"
35+ anchors {
36+ top: parent.top
37+ bottom: parent.bottom
38+ horizontalCenter: parent.horizontalCenter
39+ }
40+ spacing: units.gu(0.5)
41+
42+ // FIXME : Should us Ubuntu.Icon . results in low res images
43+ Image {
44+ id: itemImage
45+ objectName: "itemImage"
46+ visible: source != ""
47+ height: indicatorWidget.iconSize
48+ width: indicatorWidget.iconSize
49+ anchors.verticalCenter: parent.verticalCenter
50+ }
51+
52+ Label {
53+ id: itemLabel
54+ objectName: "itemLabel"
55+ color: Theme.palette.selected.backgroundText
56+ opacity: 0.8
57+ font.family: "Ubuntu"
58+ fontSize: "medium"
59+ anchors.verticalCenter: parent.verticalCenter
60+ visible: text != ""
61+ }
62+ }
63+
64+ onActionStateChanged: {
65+ if (actionState == undefined) {
66+ label = "";
67+ iconSource = "";
68+ enabled = false;
69+ return;
70+ }
71+
72+ label = actionState.label;
73+ iconSource = actionState.icon;
74+ enabled = actionState.visible;
75+ }
76+}
Panel/Indicators/MessagingIndicatorPage.qml+1−1
@@ -19,7 +19,7 @@
1919
2020import QtQuick 2.0
2121import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
2323
2424Indicators.IndicatorPage {
2525 anchors.fill: parent
Panel/Indicators/NetworkIndicatorPage.qml+18−14
@@ -18,28 +18,32 @@
1818 */
1919
2020import QtQuick 2.0
21−import Unity.Indicators 0.1 as Indicators
22−import Unity.Indicators.Network 0.1 as ICNetwork
21+import Unity.IndicatorsLegacy 0.1 as Indicators
22+import Unity.IndicatorsLegacy.Network 0.1 as ICNetwork
2323import Ubuntu.Components 0.1
2424
2525PageStack {
26− id: _network
26+ id: networkPage
27+ // Stops the search for a parent tree node.
28+ // We don't want it going up to the indicator tabs.
29+ // FIXME: need a better way of doing this.
30+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
31+ __propagated: null
2732
2833 property alias title: _mainPage.title
29− property alias emptyText: _pluginItem.emptyText
30− property alias busType: _pluginItem.busType
31− property alias busName: _pluginItem.busName
32− property alias actionsObjectPath : _pluginItem.actionsObjectPath
33− property alias menuObjectPaths : _pluginItem.menuObjectPaths
34+ property alias emptyText: pluginItem.emptyText
35+ property alias busType: pluginItem.busType
36+ property alias busName: pluginItem.busName
37+ property alias actionsObjectPath : pluginItem.actionsObjectPath
38+ property alias menuObjectPaths : pluginItem.menuObjectPaths
3439
35− __showHeader: false
3640 anchors.fill: parent
3741
3842 Page {
3943 id: _mainPage
4044
4145 Indicators.IndicatorPage {
42− id: _pluginItem
46+ id: pluginItem
4347 anchors.fill: parent
4448 }
4549 }
@@ -56,24 +60,24 @@ PageStack {
5660 id: networkAgent
5761
5862 onSecretRequested: {
59− _network.push(passwordPageComponent, {"token": token});
63+ networkPage.push(passwordPageComponent, {"token": token});
6064 }
6165 }
6266
6367 function start()
6468 {
6569 push(_mainPage);
66− _pluginItem.start();
70+ pluginItem.start();
6771 }
6872
6973 function stop()
7074 {
7175 clear();
72− _pluginItem.stop();
76+ pluginItem.stop();
7377 }
7478
7579 function reset()
7680 {
77− _pluginItem.reset();
81+ pluginItem.reset();
7882 }
7983}
Panel/Indicators/NetworkIndicatorPage2.qmlnew file+82
@@ -0,0 +1,82 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Unity.Indicators 0.1 as Indicators
22+import Unity.Indicators.Network 0.1 as ICNetwork
23+import Ubuntu.Components 0.1
24+
25+PageStack {
26+ id: networkPage
27+ // Stops the search for a parent tree node.
28+ // We don't want it going up to the indicator tabs.
29+ // FIXME: need a better way of doing this.
30+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
31+ __propagated: null
32+
33+ property alias title: _mainPage.title
34+ property alias emptyText: pluginItem.emptyText
35+ property alias busName: pluginItem.busName
36+ property alias actionsObjectPath : pluginItem.actionsObjectPath
37+ property alias menuObjectPaths : pluginItem.menuObjectPaths
38+
39+ anchors.fill: parent
40+
41+ Page {
42+ id: _mainPage
43+
44+ Indicators.IndicatorPage {
45+ id: pluginItem
46+ anchors.fill: parent
47+ }
48+ }
49+
50+ Component {
51+ id: passwordPageComponent
52+
53+ ICNetwork.PasswordPage {
54+ agent: networkAgent
55+ }
56+ }
57+
58+ ICNetwork.NetworkAgent {
59+ id: networkAgent
60+
61+ onSecretRequested: {
62+ networkPage.push(passwordPageComponent, {"token": token});
63+ }
64+ }
65+
66+ function start()
67+ {
68+ push(_mainPage);
69+ pluginItem.start();
70+ }
71+
72+ function stop()
73+ {
74+ clear();
75+ pluginItem.stop();
76+ }
77+
78+ function reset()
79+ {
80+ pluginItem.reset();
81+ }
82+}
Panel/Indicators/NetworkIndicatorWidget.qml+1−1
@@ -19,7 +19,7 @@
1919
2020import QtQuick 2.0
2121import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
2323
2424Indicators.IndicatorWidget {
2525 id: indicatorWidget
Panel/Indicators/client/IndicatorsList.qml+1−1
@@ -20,7 +20,7 @@
2020import QtQuick 2.0
2121import Ubuntu.Components 0.1
2222import Ubuntu.Components.ListItems 0.1 as ListItem
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
2424import "../.."
2525
2626Page {
Panel/Indicators/client/IndicatorsPage.qml+6−9
@@ -43,6 +43,12 @@ Page {
4343 Loader {
4444 id: pageLoader
4545 objectName: "pageLoader"
46+ clip:true
47+
48+ Rectangle {
49+ anchors.fill: pageLoader
50+ color: "#221e1c" // FIXME not in palette yet
51+ }
4652
4753 anchors {
4854 top: visualCheckItem.bottom
@@ -81,14 +87,5 @@ Page {
8187 text: "Back"
8288 onClicked: page.pageStack.reset()
8389 }
84− Button {
85− id: refresh
86− visible: !visualCheck.checked
87− anchors {
88− right: parent.right
89− }
90− text: "Refresh"
91− onClicked: pageLoader.item.refresh()
92− }
9390 }
9491}
Panel/Indicators/client/IndicatorsTree.qml+7−7
@@ -27,8 +27,7 @@ Page {
2727 id: page
2828 anchors.fill: parent
2929
30− property alias busType: menuModel.busType
31− property alias busName: menuModel.busName
30+ property string busName: unityModel.busName
3231 property string actionsObjectPath
3332 property var menuObjectPaths: undefined
3433 readonly property string device: "phone"
@@ -36,17 +35,18 @@ Page {
3635 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
3736
3837 function start() {
39− menuModel.start();
4038 }
4139
42− QDBusMenuModel {
43− id: menuModel
44− objectPath: page.deviceMenuObjectPath
40+ UnityMenuModel {
41+ id: unityModel
42+ busName: page.busName
43+ actions: { "indicator": page.actionsObjectPath }
44+ menuObjectPath: page.deviceMenuObjectPath
4545 }
4646
4747 Indicators.ModelPrinter {
4848 id: printer
49− model: menuModel
49+ model: unityModel
5050 }
5151
5252 Flickable {
Panel/IndicatorsDataModel.qml+29−11
@@ -27,7 +27,19 @@ Indicators.IndicatorsModel {
2727 Component.onCompleted: load()
2828
2929 indicatorData : {
30− 'indicator-messaging' : {
30+ 'indicator-location' : {
31+ 'title': 'Location',
32+ 'description' : '',
33+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
34+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
35+ },
36+ 'indicator-bluetooth' : {
37+ 'title': 'Bluetooth',
38+ 'description' : '',
39+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
40+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
41+ },
42+ 'indicator-messages' : {
3143 'title': 'Messaging',
3244 'description' : '',
3345 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/MessagingIndicatorWidget.qml',
@@ -36,26 +48,32 @@ Indicators.IndicatorsModel {
3648 'indicator-sound' : {
3749 'title': 'Sound',
3850 'description' : '',
39− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/SoundIndicatorWidget.qml',
40− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage.qml'
51+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
52+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
4153 },
4254 'indicator-network' : {
4355 'title': 'Networks',
4456 'description' : '',
45− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorWidget.qml',
46− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage.qml'
57+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
58+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/NetworkIndicatorPage2.qml'
4759 },
48− 'indicator-battery' : {
60+ 'indicator-power' : {
4961 'title': 'Battery',
5062 'description' : '',
51− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget.qml',
52− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage.qml'
63+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
64+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
65+ },
66+ 'indicator-session' : {
67+ 'title': 'Session',
68+ 'description' : '',
69+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
70+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
5371 },
54− 'indicator-time' : {
72+ 'indicator-datetime' : {
5573 'title': 'Date and Time',
5674 'description' : '',
57− 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DatetimeIndicatorWidget.qml',
58− 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DatetimeIndicatorPage.qml'
75+ 'widgetSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorWidget2.qml',
76+ 'pageSource' : ApplicationPaths.shellAppDirectory()+'/Panel/Indicators/DefaultIndicatorPage2.qml'
5977 }
6078 }
6179}
Panel/MenuContent.qml+78−97
@@ -19,20 +19,31 @@ import Ubuntu.Components 0.1
1919import Unity.Indicators 0.1 as Indicators
2020import "../Components"
2121
22−Rectangle {
22+// FIXME : We dont want to use MainView.
23+// Need a regular Item which can have tabs with local header.
24+// https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211704
25+MainView {
2326 id: content
2427
2528 property QtObject indicatorsModel: null
2629 property bool __contentActive: false
27− property alias currentMenuIndex : menus.currentIndex
28− property color backgroundColor: "#221e1c" // FIXME not in palette yet
30+ readonly property int currentMenuIndex : tabs.selectedTabIndex
31+ backgroundColor: "#221e1c" // FIXME not in palette yet
2932 property int contentReleaseInterval: 20000
33+ property bool activeHeader: false
3034
3135 width: units.gu(40)
3236 height: units.gu(42)
33− color: backgroundColor
3437
35− signal menuSelected(int index)
38+ function setCurrentMenuIndex(index) {
39+ if (currentMenuIndex !== index) {
40+ if (currentMenuIndex == -1) {
41+ tabs.tabBar.animate = false;
42+ }
43+ tabs.selectedTabIndex = index;
44+ tabs.tabBar.animate = true;
45+ }
46+ }
3647
3748 function activateContent() {
3849 contentReleaseTimer.stop();
@@ -40,95 +51,75 @@ Rectangle {
4051 }
4152
4253 function releaseContent() {
43− if (__contentActive)
54+ if (__contentActive) {
4455 contentReleaseTimer.restart();
56+ }
4557 }
4658
47− ListView {
48− id: menus
49− objectName: "menus"
50−
51− anchors.bottom: parent.bottom
52− anchors.top: header.bottom
53− width: parent.width
54−
55− spacing: units.gu(0.5)
56− enabled: opacity != 0
57− orientation: ListView.Horizontal
58− model: indicatorsModel
59− interactive: false
60− //FIXME: This will make sure that all plugins still alive
61− //check bug https://bugs.launchpad.net/manhattan/+bug/1088945 for more details
62− cacheBuffer: 2147483647
63−
64− delegate: Loader {
65− clip: true
66− property bool contentActive: content.__contentActive && menuActivator.content[index].active
67−
68− onContentActiveChanged: {
69− if (contentActive && item) {
70− item.start();
71− } else if (!contentActive && item) {
72− item.stop();
73− }
74− }
59+ onActiveHeaderChanged: {
60+ tabs.tabBar.selectionMode = activeHeader;
61+ tabs.tabBar.alwaysSelectionMode = activeHeader;
62+ }
7563
76− width: menus.width
77− height: menus.height
78− source: pageSource
79− onVisibleChanged: {
80− // Reset the indicator states
81− if (!visible && item && item["reset"]) {
82− item.reset();
83− }
64+ Tabs {
65+ id: tabs
66+ objectName: "tabs"
67+ anchors.fill: parent
68+ selectedTabIndex: -1
69+
70+ Repeater {
71+ id: repeater
72+ model: indicatorsModel
73+ objectName: "menus"
74+
75+ // FIXME: This is needed because tabs dont handle repeaters well.
76+ // Due to the child ordering happening after child insertion.
77+ // QTBUG-32438
78+ onItemAdded: {
79+ parent.childrenChanged();
8480 }
85− asynchronous: true
8681
87− onLoaded: {
88− for(var pName in indicatorProperties) {
89− if (item.hasOwnProperty(pName)) {
90− item[pName] = indicatorProperties[pName];
82+ Tab {
83+ id: tab
84+ objectName: model.identifier
85+ title: model.title
86+
87+ page: Page {
88+ Loader {
89+ clip: true
90+ anchors.fill: parent
91+ source: pageSource
92+ asynchronous: true
93+
94+ property bool contentActive: content.__contentActive
95+
96+ onContentActiveChanged: {
97+ if (contentActive && item) {
98+ item.start()
99+ } else if (!contentActive && item) {
100+ item.stop()
101+ }
102+ }
103+
104+ onVisibleChanged: {
105+ // Reset the indicator states
106+ if (!visible && item && item["reset"]) {
107+ item.reset()
108+ }
109+ }
110+
111+ onLoaded: {
112+ for(var pName in indicatorProperties) {
113+ if (item.hasOwnProperty(pName)) {
114+ item[pName] = indicatorProperties[pName]
115+ }
116+ }
117+ if (contentActive && menus.visible) {
118+ item.start()
119+ }
120+ }
91121 }
92122 }
93− if (contentActive && menus.visible) {
94− item.start();
95− }
96− }
97−
98− // Need to use a binding because the handle height changes.
99− // FIXME: Dont know why we're using handle height (introduces dep).. Check with design about bottom margin
100− Binding {
101− target: item
102− property: "anchors.bottomMargin"
103− value: handle.height
104− }
105− }
106− }
107−
108− Rectangle {
109− id: header
110− objectName: "header"
111−
112− property alias title: pageHeader.text
113−
114− color: backgroundColor
115− opacity: pageHeader.opacity
116− anchors {
117− left: parent.left
118− right: parent.right
119− }
120− height: childrenRect.height
121−
122− PageHeader {
123− id: pageHeader
124− anchors {
125− left: parent.left
126− right: parent.right
127− }
128− text: {
129− if (indicatorsModel && menus.currentIndex >= 0 && menus.currentIndex < indicatorsModel.count)
130− return indicatorsModel.data(menus.currentIndex, Indicators.IndicatorsModelRole.Title);
131− return "";
132123 }
133124 }
134125 }
@@ -137,16 +128,6 @@ Rectangle {
137128 id: contentReleaseTimer
138129
139130 interval: contentReleaseInterval
140− onTriggered: {
141− __contentActive = false;
142− menuActivator.clear();
143− }
144− }
145−
146− Indicators.MenuContentActivator {
147− id: menuActivator
148− running: content.__contentActive
149− baseIndex: content.currentMenuIndex
150− count: indicatorsModel.count
131+ onTriggered: content.__contentActive = false
151132 }
152133}
Shell.qml+50−18
@@ -15,6 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18+import GSettings 1.0
1819import Ubuntu.Application 0.1
1920import Ubuntu.Components 0.1
2021import Ubuntu.Gestures 0.1
@@ -41,8 +42,8 @@ FocusScope {
4142 height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
4243
4344 property real edgeSize: units.gu(2)
44− property url default_background: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
45− property url background: default_background
45+ property url defaultBackground: shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg"
46+ property url background
4647 readonly property real panelHeight: panel.panelHeight
4748
4849 property bool dashShown: dash.shown
@@ -115,6 +116,36 @@ FocusScope {
115116 }
116117 }
117118
119+ GSettings {
120+ id: backgroundSettings
121+ schema.id: "org.gnome.desktop.background"
122+ }
123+ property url gSettingsPicture: backgroundSettings.pictureUri != undefined && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : shell.defaultBackground
124+ onGSettingsPictureChanged: {
125+ shell.background = gSettingsPicture
126+ }
127+
128+ // This is a dummy image that is needed to determine if the picture url
129+ // in backgroundSettings points to a valid picture file.
130+ // We can't do this with the real background image because setting a
131+ // new source in onStatusChanged triggers a binding loop detection
132+ // inside Image, which causes it not to render even though a valid source
133+ // would be set. We don't mind about this image staying black and just
134+ // use it for verification to populate the source for the real
135+ // background image.
136+ Image {
137+ source: shell.background
138+ height: 0
139+ width: 0
140+ sourceSize.height: 0
141+ sourceSize.width: 0
142+ onStatusChanged: {
143+ if (status == Image.Error && source != shell.defaultBackground) {
144+ shell.background = defaultBackground
145+ }
146+ }
147+ }
148+
118149 VolumeControl {
119150 id: volumeControl
120151 }
@@ -130,21 +161,25 @@ FocusScope {
130161 // Whether the underlay is fully covered by opaque UI elements.
131162 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
132163
164+ readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
165+ || (sideStage.applications && sideStage.applications.count > 0))
166+
133167 // Whether the user should see the topmost application surface (if there's one at all).
134− property bool applicationSurfaceShouldBeSeen:
135− (mainStage.applications && mainStage.applications.count > 0)
136− && (!stages.fullyHidden && !mainStage.usingScreenshots)
168+ readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
169+ && !mainStage.usingScreenshots // but want sideStage animating over app surface
170+
171+
137172
138173 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
139174 // through the translucent parts of the shell surface.
140175 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
141176
142− Image {
177+ CrossFadeImage {
143178 id: backgroundImage
144− source: shell.background
145− sourceSize.width: parent.width
146− sourceSize.height: parent.height
179+ objectName: "backgroundImage"
180+
147181 anchors.fill: parent
182+ source: shell.background
148183 }
149184
150185 Rectangle {
@@ -189,7 +224,6 @@ FocusScope {
189224 }
190225 }
191226
192−
193227 Item {
194228 id: stagesOuterContainer
195229
@@ -254,7 +288,6 @@ FocusScope {
254288 ignoreUnknownSignals: true
255289 }
256290
257−
258291 Stage {
259292 id: mainStage
260293
@@ -404,6 +437,7 @@ FocusScope {
404437
405438 Greeter {
406439 id: greeter
440+ objectName: "greeter"
407441
408442 available: true
409443 hides: [launcher, panel.indicators, hud]
@@ -428,10 +462,6 @@ FocusScope {
428462 }
429463
430464 onUnlocked: greeter.hide()
431− onSelected: {
432− var bgPath = greeter.model.data(uid, LightDM.UserRoles.BackgroundPathRole)
433− shell.background = bgPath ? bgPath : default_background
434− }
435465
436466 onLeftTeaserPressedChanged: {
437467 if (leftTeaserPressed) {
@@ -471,11 +501,13 @@ FocusScope {
471501 }
472502 }
473503
474− onPowerStateChange: {
475− if (state == 0) { // suspend
504+ onDisplayPowerStateChange: {
505+ // We ignore any display-off signals when the proximity sensor
506+ // is active. This usually indicates something like a phone call.
507+ if (status == Powerd.Off && (flags & Powerd.UseProximity) == 0) {
476508 powerConnection.setFocused(false);
477509 greeter.show();
478− } else if (state == 1) { // active
510+ } else if (status == Powerd.On) {
479511 powerConnection.setFocused(true);
480512 }
481513 }
build+7−3
@@ -40,11 +40,14 @@ install_dependencies() {
4040 echo "Installing Unity 8 dependencies.."
4141 sudo apt-get install devscripts \
4242 equivs \
43+ gsettings-desktop-schemas \
4344 qmenumodel-qml \
44− qtdeclarative5-ubuntu-ui-toolkit-plugin \
4545 qtdeclarative5-dee-plugin \
46+ qtdeclarative5-gsettings1.0 \
47+ qtdeclarative5-ubuntu-ui-toolkit-plugin \
48+ unity-notifications-impl \
49+ qtdeclarative5-xmllistmodel-plugin \
4650 ubuntu-mobile-icons \
47− unity-notifications-impl-1 \
4851 unity-scope-home \
4952 unity-lens-applications \
5053 || exit 5
@@ -56,7 +59,8 @@ mk_build_deps() {
5659
5760if [ -f "/usr/bin/ninja" ] ; then
5861 GENERATOR="-G Ninja"
59− BUILD_COMMAND="ninja -j$NUM_JOBS"
62+ # Ninja does not need -j, it parallelizes automatically.
63+ BUILD_COMMAND="ninja"
6064else
6165 GENERATOR=
6266 BUILD_COMMAND="make -j$NUM_JOBS"
debian/changelog+309
@@ -1,3 +1,312 @@
1+unity8 (7.81.3+13.10.20130828.1-0ubuntu1) saucy; urgency=low
2+
3+ [ Andrea Cimitan ]
4+ * Hide the LauncherPanel when it's really hidden, by changing visible
5+ to false.
6+
7+ [ Gerry Boland ]
8+ * Fix sidestage applications - they were being ignored in the show-
9+ application-surface logic. (LP: #1217027, #1210079)
10+
11+ [ Michał Sawicz ]
12+ * Raise the exception if typing failed in autopilot.
13+
14+ [ Nick Dedekind ]
15+ * Added location indicator defaults.
16+
17+ [ Michael Zanetti ]
18+ * increase minimal dragging width for dismissing apps with left edge
19+ make it a configurable parameter and adjust animation to look like
20+ requested in the bug report . (LP: #1213153)
21+
22+ [ Ubuntu daily release ]
23+ * Automatic snapshot from revision 262
24+
25+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 28 Aug 2013 11:55:46 +0000
26+
27+unity8 (7.81.3+13.10.20130827.1-0ubuntu1) saucy; urgency=low
28+
29+ [ Michael Zanetti ]
30+ * unset model in quicklist before closing it.
31+
32+ [ Ubuntu daily release ]
33+ * Automatic snapshot from revision 256
34+
35+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 27 Aug 2013 10:49:17 +0000
36+
37+unity8 (7.81.3+13.10.20130827-0ubuntu1) saucy; urgency=low
38+
39+ [ Michael Zanetti ]
40+ * Theme the Quicklist Popover.
41+
42+ [ Albert Astals ]
43+ * Apply expandedIndex on delegate creation bug #1213033. (LP:
44+ #1213033)
45+
46+ [ Ubuntu daily release ]
47+ * Automatic snapshot from revision 254
48+
49+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 27 Aug 2013 02:10:43 +0000
50+
51+unity8 (7.81.3+13.10.20130826.5-0ubuntu1) saucy; urgency=low
52+
53+ [ Michael Zanetti ]
54+ * tweak launcher folding and visuals according to feedback from
55+ design: - increase foldingStartHeight - Introduces a
56+ foldingStopHeight. Folding only happens between the
57+ foldingStartHeight and the foldingStopHeight. - Only change
58+ brightness while folding - remove highlight of pressed icon -
59+ decrease launcher's width by half a grid unit.
60+
61+ [ Ubuntu daily release ]
62+ * Automatic snapshot from revision 251
63+
64+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 22:09:43 +0000
65+
66+unity8 (7.81.3+13.10.20130826.4-0ubuntu1) saucy; urgency=low
67+
68+ [ Michał Sawicz ]
69+ * Add module entry in HudClient's qmldir.
70+
71+ [ Jussi Pakkanen ]
72+ * Let Ninja parallelize itself.
73+
74+ [ Nick Dedekind ]
75+ * Abstraction of indicator menu item properties prior to move into
76+ common components library.
77+
78+ [ Christopher Lee ]
79+ * Added autopilot-tests for ephemeral, interactive and snap-decision
80+ notifications.
81+
82+ [ Mirco Müller ]
83+ * Added autopilot-tests for ephemeral, interactive and snap-decision
84+ notifications.
85+
86+ [ Michael Zanetti ]
87+ * only search visible children in findChild, add findInvisibleChild
88+ for others.
89+ * Added autopilot-tests for ephemeral, interactive and snap-decision
90+ notifications.
91+
92+ [ Thomi Richards ]
93+ * Added autopilot-tests for ephemeral, interactive and snap-decision
94+ notifications.
95+
96+ [ Ubuntu daily release ]
97+ * Automatic snapshot from revision 249
98+
99+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 18:34:44 +0000
100+
101+unity8 (7.81.3+13.10.20130826.2-0ubuntu1) saucy; urgency=low
102+
103+ [ Albert Astals ]
104+ * Don't include QtQML It includes LOTS of files we don't need.
105+
106+ [ Ubuntu daily release ]
107+ * Automatic snapshot from revision 243
108+
109+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 10:08:52 +0000
110+
111+unity8 (7.81.3+13.10.20130826.1-0ubuntu1) saucy; urgency=low
112+
113+ * Automatic snapshot from revision 241
114+
115+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 06:08:33 +0000
116+
117+unity8 (7.81.3+13.10.20130826-0ubuntu1) saucy; urgency=low
118+
119+ [ Michael Zanetti ]
120+ * delay move operations if the start dragging operation is running
121+ This prevents items to left in wrong places when transitions clash.
122+ * add empty setUser to allow compiling in jenkins again until the
123+ branch that matches unity-api is ready to land. .
124+ * add count and progress overlay information to real model too.
125+
126+ [ Pawel Stolowski ]
127+ * Changed the type of setActive argument from
128+ QVector<AbstractFilterOption *>::size_type to unsigned int.
129+
130+ [ Ubuntu daily release ]
131+ * Automatic snapshot from revision 240
132+
133+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 02:31:42 +0000
134+
135+unity8 (7.81.3+13.10.20130821.2-0ubuntu1) saucy; urgency=low
136+
137+ [ Michael Terry ]
138+ * Listen to display-power-changed rather than system-power-changed
139+ signals when showing the greeter; ignore such signals when the
140+ proximity sensor is active. (LP: #1214477)
141+
142+ [ Pawel Stolowski ]
143+ * Added a role for accessing progress-source property of categories.
144+
145+ [ Ubuntu daily release ]
146+ * Automatic snapshot from revision 233
147+
148+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 22:09:53 +0000
149+
150+unity8 (7.81.3+13.10.20130821.1-0ubuntu1) saucy; urgency=low
151+
152+ [ Michael Zanetti ]
153+ * allow testing at 11:13. Old code failed because the text actually
154+ says "11:13 AM".
155+ * add some checks if we actually clicked an item or in the spacing
156+ between them gets rid of some warnings printed by the launcher .
157+
158+ [ Michał Sawicz ]
159+ * Update runtime deps in the build script.
160+
161+ [ Nick Dedekind ]
162+ * Added/Updated legacy network indicator components to use with new
163+ indicator backend.
164+
165+ [ Albert Astals ]
166+ * Fix insertions/removals on the qlimitproxymodel . (LP: #1213959)
167+
168+ [ Ubuntu daily release ]
169+ * Automatic snapshot from revision 230
170+
171+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 15:04:59 +0000
172+
173+unity8 (7.81.3+13.10.20130821-0ubuntu1) saucy; urgency=low
174+
175+ [ Michael Zanetti ]
176+ * added support for count emblems and progress overlays on the
177+ launcher.
178+
179+ [ Pawel Stolowski ]
180+ * Add role for getting filter options model. Add method to activate
181+ option based on index or id.
182+
183+ [ Ted Gould ]
184+ * Mark indicators-client as providing an indicator-renderer.
185+
186+ [ Nick Dedekind ]
187+ * Added flatmenuproxymodel pass-through signals from qmenumodel.
188+ * Renamed indicator-messaging to indicator-messages.
189+
190+ [ Gerry Boland ]
191+ * Typo fix in FrequentlyUsedAppsModel.
192+
193+ [ Ubuntu daily release ]
194+ * Automatic snapshot from revision 224
195+
196+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 08:05:13 +0000
197+
198+unity8 (7.81.3+13.10.20130820.2-0ubuntu1) saucy; urgency=low
199+
200+ [ Michael Zanetti ]
201+ * initial support for quicklists For now they support pinning and
202+ removing of items.
203+
204+ [ Ubuntu daily release ]
205+ * Automatic snapshot from revision 217
206+
207+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 10:09:36 +0000
208+
209+unity8 (7.81.3+13.10.20130820-0ubuntu1) saucy; urgency=low
210+
211+ [ Andrea Cimitan ]
212+ * Implement background changing through gsettings. Make sure it does
213+ fallback to default background when needed.
214+
215+ [ Michael Zanetti ]
216+ * Implement background changing through gsettings. Make sure it does
217+ fallback to default background when needed.
218+ * Add Drag'n'drop support to Launcher As dragging an item pins it to
219+ the launcher this also contains initial quicklist and pinning
220+ support in the plugin part.
221+
222+ [ Michał Sawicz ]
223+ * Fix generic preview wrapping and force rich text parsing.
224+
225+ [ Michal Hruby ]
226+ * Remove the override for Apps available for download, click scope
227+ provides these now.
228+ * Hide rating widgets when scope provides rating set to < 0.0. Also
229+ fallback to regular preview image if there are no more_screenshots
230+ specified.
231+
232+ [ Albert Astals ]
233+ * Make sure minYExtent is updated before setting the new content
234+ height Otherwise bad things can happen in the positioning .
235+
236+ [ Ubuntu daily release ]
237+ * Automatic snapshot from revision 215
238+
239+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 02:08:42 +0000
240+
241+unity8 (7.81.3+13.10.20130816.3-0ubuntu1) saucy; urgency=low
242+
243+ [ Michal Hruby ]
244+ * Implement overrideResults() method, which allows us to seamlessly
245+ combine real scope data with mocked data.
246+
247+ [ Michał Sawicz ]
248+ * Wait for activeFocus before typing in autopilot tests. (LP:
249+ #1212580)
250+
251+ [ Nick Dedekind ]
252+ * Fixed network indicator password dialog not appearing. (LP:
253+ #1212730)
254+ * Remove time & battery indicator service info.
255+
256+ [ Albert Astals ]
257+ * Dash category expansion.
258+ * Fix crash in the shell test Give the item a parent, otherwise the
259+ qml engine decides to adopt it and when we do the deleteLater on
260+ them, they have been already deleted. Since we are the parents, we
261+ don't need to call the deleteLAter eiether they'll be properly
262+ deleted on our deletion.
263+
264+ [ Michael Zanetti ]
265+ * make entering text in lockscreen tests more robust. (LP: #1212580)
266+
267+ [ Michael Terry ]
268+ * Define the 'build' target as PHONY so make doesn't get confused by
269+ our 'build' script.
270+ * Add a test for the Powerd plugin shell support.
271+
272+ [ Pawel Stolowski ]
273+ * Bindings for filters.
274+
275+ [ Ubuntu daily release ]
276+ * Automatic snapshot from revision 208
277+
278+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 16 Aug 2013 14:11:35 +0000
279+
280+unity8 (7.81.3+13.10.20130814.3-0ubuntu1) saucy; urgency=low
281+
282+ [ Ted Gould ]
283+ * Upstart signals to control indicator services.
284+
285+ [ Nick Dedekind ]
286+ * Replaced indicator menu listView with tabs view.
287+ * Transition Unity.Indicators to UnityMenuModel.
288+
289+ [ Ubuntu daily release ]
290+ * Automatic snapshot from revision 197
291+
292+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 14 Aug 2013 14:33:39 +0000
293+
294+unity8 (7.81.3+13.10.20130814.1-0ubuntu1) saucy; urgency=low
295+
296+ [ Michał Sawicz ]
297+ * Add AppPreview.
298+
299+ [ Nick Dedekind ]
300+ * Use key from indicator service file to source indicator positions.
301+
302+ [ Diego Sarmentero ]
303+ * Add AppPreview.
304+
305+ [ Ubuntu daily release ]
306+ * Automatic snapshot from revision 193
307+
308+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 14 Aug 2013 06:33:14 +0000
309+
1310unity8 (7.81.3+13.10.20130813.1-0ubuntu1) saucy; urgency=low
2311
3312 [ Michael Zanetti ]
debian/control+11−4
@@ -10,15 +10,19 @@ Build-Depends: cmake,
1010 libgles2-mesa-dev[armhf],
1111 libhud-client2-dev,
1212 libjson-perl,
13+ libnih-dbus-dev,
14+ libnih-dev,
1315 libnm-glib-dev,
1416 libnm-util-dev,
1517 libpulse-dev,
16− libqmenumodel-dev,
18+ libqmenumodel-dev (>= 0.2.7),
1719 libqt5v8-5-private-dev,
20+ libsigc++-2.0-dev,
1821 libunity-api-dev (>= 7.80.3),
1922 libunity-core-6.0-dev (<< 7.80),
20− libunity-dev (>= 7.0.3),
23+ libunity-dev (>= 7.0.12),
2124 libunity-scopes-json-def-phone,
25+ libupstart-dev,
2226 libusermetricsoutput1-dev,
2327 libxcb1-dev,
2428 pkg-config,
@@ -46,7 +50,7 @@ Vcs-Browser: https://bazaar.launchpad.net/~unity-team/unity8/trunk
4650
4751Package: indicators-client
4852Architecture: amd64 armhf i386
49−Depends: qmenumodel-qml,
53+Depends: qmenumodel-qml (>= 0.2.7),
5054 qtdeclarative5-ubuntu-ui-toolkit-plugin | qt-components-ubuntu,
5155 unity8 (= ${binary:Version}),
5256 ${misc:Depends},
@@ -56,8 +60,11 @@ Description: Indicators client test application
5660
5761Package: unity8
5862Architecture: any
59−Depends: qmenumodel-qml,
63+Provides: indicator-renderer,
64+Depends: gsettings-desktop-schemas,
65+ qmenumodel-qml (>= 0.2.7),
6066 qtdeclarative5-dee-plugin,
67+ qtdeclarative5-gsettings1.0,
6168 qtdeclarative5-ubuntu-ui-toolkit-plugin,
6269 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
6370 qtdeclarative5-xmllistmodel-plugin,
debian/rules+2−1
@@ -8,7 +8,8 @@ export NO_PNG_PKG_MANGLE=1
88
99SCOPES_RECOMMENDS := $(shell perl debian/scopes-recommends-generator /usr/share/unity/client-scopes.json)
1010
11−%:
11+.PHONY: build
12+build %:
1213 dh $@ --parallel --fail-missing --with python2
1314
1415override_dh_auto_clean:
main.cpp−1
@@ -18,7 +18,6 @@
1818 */
1919
2020// Qt
21−#include <QtQml>
2221#include <QtQuick/QQuickView>
2322#include <QtGui/QIcon>
2423#include <QtGui/QGuiApplication>
plugins/HudClient/qmldir+1
@@ -1,2 +1,3 @@
1+module HudClient
12plugin HudClientQml
23typeinfo plugin.qmltypes
plugins/LightDM/plugin.cpp+1−1
@@ -25,7 +25,7 @@
2525#include <QLightDM/UsersModel>
2626
2727#include <QtCore/QAbstractItemModel>
28−#include <QtQml>
28+#include <QtQml/qqml.h>
2929
3030static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3131{
plugins/ListViewWithPageHeader/listviewwithpageheader.cpp+47−11
@@ -280,7 +280,7 @@ void ListViewWithPageHeader::setSectionDelegate(QQmlComponent *delegate)
280280
281281 m_sectionDelegate = delegate;
282282
283− m_topSectionItem = getSectionItem(-1, QString());
283+ m_topSectionItem = getSectionItem(QString());
284284 m_topSectionItem->setZ(3);
285285 QQuickItemPrivate::get(m_topSectionItem)->setCulled(true);
286286
@@ -380,20 +380,51 @@ void ListViewWithPageHeader::showHeader()
380380 }
381381}
382382
383+QQuickItem *ListViewWithPageHeader::item(int modelIndex) const
384+{
385+ ListItem *item = itemAtIndex(modelIndex);
386+ if (item)
387+ return item->m_item;
388+ else
389+ return nullptr;
390+}
391+
383392bool ListViewWithPageHeader::maximizeVisibleArea(int modelIndex)
384393{
385394 ListItem *listItem = itemAtIndex(modelIndex);
386− if (listItem)
387− {
395+ if (listItem) {
396+ return maximizeVisibleArea(listItem, listItem->height());
397+ }
398+
399+ return false;
400+}
401+
402+bool ListViewWithPageHeader::maximizeVisibleArea(int modelIndex, int itemHeight)
403+{
404+ if (itemHeight < 0)
405+ return false;
406+
407+ ListItem *listItem = itemAtIndex(modelIndex);
408+ if (listItem) {
409+ return maximizeVisibleArea(listItem, itemHeight + (listItem->m_sectionItem ? listItem->m_sectionItem->height() : 0));
410+ }
411+
412+ return false;
413+}
414+
415+bool ListViewWithPageHeader::maximizeVisibleArea(ListItem *listItem, int listItemHeight)
416+{
417+ if (listItem) {
418+ layout();
388419 const auto listItemY = m_clipItem->y() + listItem->y();
389− if (listItemY > contentY() && listItemY + listItem->height() > contentY() + height()) {
420+ if (listItemY > contentY() && listItemY + listItemHeight > contentY() + height()) {
390421 // we can scroll the list up to show more stuff
391− const auto to = qMin(listItemY, listItemY + listItem->height() - height());
422+ const auto to = qMin(listItemY, listItemY + listItemHeight - height());
392423 m_contentYAnimation->setTo(to);
393424 contentYAnimationType = ContentYAnimationMaximizeVisibleArea;
394425 m_contentYAnimation->start();
395− } else if ((listItemY < contentY() && listItemY + listItem->height() < contentY() + height()) ||
396− (m_topSectionItem && !listItem->m_sectionItem && listItemY - m_topSectionItem->height() < contentY() && listItemY + listItem->height() < contentY() + height()))
426+ } else if ((listItemY < contentY() && listItemY + listItemHeight < contentY() + height()) ||
427+ (m_topSectionItem && !listItem->m_sectionItem && listItemY - m_topSectionItem->height() < contentY() && listItemY + listItemHeight < contentY() + height()))
397428 {
398429 // we can scroll the list down to show more stuff
399430 auto realVisibleListItemY = listItemY;
@@ -406,7 +437,7 @@ bool ListViewWithPageHeader::maximizeVisibleArea(int modelIndex)
406437 realVisibleListItemY -= m_topSectionItem->height();
407438 }
408439 }
409− const auto to = qMax(realVisibleListItemY, listItemY + listItem->height() - height());
440+ const auto to = qMax(realVisibleListItemY, listItemY + listItemHeight - height());
410441 m_contentYAnimation->setTo(to);
411442 contentYAnimationType = ContentYAnimationMaximizeVisibleArea;
412443 m_contentYAnimation->start();
@@ -641,17 +672,17 @@ QQuickItem *ListViewWithPageHeader::getSectionItem(int modelIndex, bool alreadyI
641672 }
642673 }
643674
644− return getSectionItem(modelIndex, section);
675+ return getSectionItem(section);
645676}
646677
647−QQuickItem *ListViewWithPageHeader::getSectionItem(int modelIndex, const QString &sectionText)
678+QQuickItem *ListViewWithPageHeader::getSectionItem(const QString &sectionText)
648679{
649680 QQuickItem *sectionItem = nullptr;
650681
651682 QQmlContext *creationContext = m_sectionDelegate->creationContext();
652683 QQmlContext *context = new QQmlContext(creationContext ? creationContext : qmlContext(this));
653684 context->setContextProperty(QLatin1String("section"), sectionText);
654− context->setContextProperty(QLatin1String("delegateIndex"), modelIndex);
685+ context->setContextProperty(QLatin1String("delegateIndex"), -1);
655686 QObject *nobj = m_sectionDelegate->beginCreate(context);
656687 if (nobj) {
657688 QQml_setParent_noEvent(context, nobj);
@@ -786,6 +817,10 @@ ListViewWithPageHeader::ListItem *ListViewWithPageHeader::createItem(int modelIn
786817 m_firstVisibleIndex = modelIndex;
787818 polish();
788819 }
820+ if (listItem->m_sectionItem) {
821+ QQmlContext *context = QQmlEngine::contextForObject(listItem->m_sectionItem)->parentContext();
822+ context->setContextProperty(QLatin1String("delegateIndex"), modelIndex);
823+ }
789824 adjustMinYExtent();
790825 m_contentHeightDirty = true;
791826 }
@@ -1209,6 +1244,7 @@ void ListViewWithPageHeader::updatePolish()
12091244 }
12101245
12111246 m_contentHeightDirty = false;
1247+ adjustMinYExtent();
12121248 setContentHeight(contentHeight);
12131249 }
12141250}
plugins/ListViewWithPageHeader/listviewwithpageheader.h+5−1
@@ -88,11 +88,13 @@ public:
8888
8989 Q_INVOKABLE void positionAtBeginning();
9090 Q_INVOKABLE void showHeader();
91+ Q_INVOKABLE QQuickItem *item(int modelIndex) const;
9192
9293 // The index has to be created for this to try to do something
9394 // Created items are those visible and the precached ones
9495 // Returns if the item existed or not
9596 Q_INVOKABLE bool maximizeVisibleArea(int modelIndex);
97+ Q_INVOKABLE bool maximizeVisibleArea(int modelIndex, int itemHeight);
9698
9799Q_SIGNALS:
98100 void modelChanged();
@@ -141,6 +143,8 @@ private:
141143 QQuickItem *m_sectionItem;
142144 };
143145
146+ bool maximizeVisibleArea(ListItem *listItem, int listItemHeight);
147+
144148 void createDelegateModel();
145149
146150 void layout();
@@ -157,7 +161,7 @@ private:
157161 void reallyReleaseItem(ListItem *item);
158162 void updateWatchedRoles();
159163 QQuickItem *getSectionItem(int modelIndex, bool alreadyInserted);
160− QQuickItem *getSectionItem(int modelIndex, const QString &sectionText);
164+ QQuickItem *getSectionItem(const QString &sectionText);
161165
162166#if (QT_VERSION < QT_VERSION_CHECK(5, 1, 0))
163167 QQuickVisualDataModel *m_delegateModel;
plugins/Powerd/Powerd.cpp+2−2
@@ -30,7 +30,7 @@ Powerd::Powerd(QObject* parent)
3030 powerd->connection().connect("com.canonical.powerd",
3131 "/com/canonical/powerd",
3232 "com.canonical.powerd",
33− "SysPowerStateChange",
33+ "DisplayPowerStateChange",
3434 this,
35− SIGNAL(powerStateChange(int)));
35+ SIGNAL(displayPowerStateChange(int, unsigned int)));
3636}
plugins/Powerd/Powerd.h+15−1
@@ -26,12 +26,26 @@
2626class Powerd: public QObject
2727{
2828 Q_OBJECT
29+ Q_ENUMS(Status)
30+ Q_FLAGS(DisplayFlag DisplayFlags)
2931
3032public:
33+ enum DisplayFlag {
34+ UseProximity = 1, // Use proximity sensor to override screen state
35+ DisableAutoBrightness = 2, // Force autobrightness to be disabled
36+ Bright = 4, // Request the screen to stay bright
37+ };
38+ Q_DECLARE_FLAGS(DisplayFlags, DisplayFlag)
39+
40+ enum Status {
41+ Off,
42+ On,
43+ };
44+
3145 explicit Powerd(QObject *parent = 0);
3246
3347Q_SIGNALS:
34− void powerStateChange(int state);
48+ void displayPowerStateChange(int status, unsigned int flags);
3549
3650private:
3751 QDBusInterface *powerd;
plugins/Powerd/plugin.cpp+1−1
@@ -20,7 +20,7 @@
2020#include "plugin.h"
2121#include "Powerd.h"
2222
23−#include <QtQml/QtQml>
23+#include <QtQml/qqml.h>
2424
2525static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
2626{
plugins/Ubuntu/DownloadDaemonListener/plugin.cpp+1−1
@@ -22,7 +22,7 @@
2222#include "plugin.h"
2323#include "DownloadTracker.h"
2424
25−#include <QtQml>
25+#include <QtQml/qqml.h>
2626#include <QtQml/QQmlContext>
2727
2828
plugins/Unity/CMakeLists.txt+15−1
@@ -2,6 +2,7 @@
22include(Plugins)
33
44add_subdirectory(Indicators)
5+add_subdirectory(IndicatorsLegacy)
56
67# Dependencies
78include(FindPkgConfig)
@@ -17,11 +18,23 @@ include_directories(
1718)
1819
1920set(QMLPLUGIN_SRC
21+ abstractfilteroption.cpp
2022 scope.cpp
2123 scopes.cpp
2224 categories.cpp
25+ checkoptionfilter.cpp
26+ filter.cpp
27+ filteroption.cpp
28+ filters.cpp
29+ genericoptionsmodel.cpp
30+ ratingoptionsmodel.cpp
31+ unityoptionsmodel.cpp
2332 categoryresults.cpp
2433 plugin.cpp
34+ multirangefilter.cpp
35+ radiooptionfilter.cpp
36+ ratingsfilter.cpp
37+ ratingfilteroption.cpp
2538 bottombarvisibilitycommunicatorshell.cpp
2639 applicationpreview.cpp
2740 genericpreview.cpp
@@ -30,10 +43,11 @@ set(QMLPLUGIN_SRC
3043 preview.cpp
3144 previewaction.cpp
3245 previewinfohint.cpp
46+ signalslist.cpp
3347 socialpreview.cpp
3448 socialpreviewcomment.cpp
3549 variantutils.cpp
36− )
50+)
3751
3852add_library(Unity-qml MODULE
3953 ${QMLPLUGIN_SRC}
plugins/Unity/Indicators/CMakeLists.txt+13−10
@@ -3,22 +3,27 @@ include(Plugins)
33
44project(IndicatorsQml)
55
6+pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
7+pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
68pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
9+pkg_check_modules(LIBUPSTART REQUIRED libupstart libnih libnih-dbus dbus-1)
10+
711find_package(Qt5Core REQUIRED)
812find_package(Qt5Quick REQUIRED)
913
1014add_subdirectory(qml)
11−add_subdirectory(Messaging)
1215add_subdirectory(Network)
1316
1417include_directories(
1518 ${CMAKE_CURRENT_SOURCE_DIR}
1619 ${CMAKE_CURRENT_BINARY_DIR}
20+ ${GLIB_INCLUDE_DIRS}
21+ ${GIO_INCLUDE_DIRS}
1722 ${QMENUMODEL_INCLUDE_DIRS}
23+ ${LIBUPSTART_INCLUDE_DIRS}
1824)
1925
2026set(IndicatorsQML_SOURCES
21− flatmenuproxymodel.cpp
2227 indicator.cpp
2328 indicators.h
2429 indicatorsmanager.cpp
@@ -26,24 +31,22 @@ set(IndicatorsQML_SOURCES
2631 menucontentactivator.cpp
2732 modelprinter.cpp
2833 plugin.cpp
34+ rootactionstate.cpp
35+ unitymenumodelcache.cpp
36+ unitymenumodelstack.cpp
2937)
3038add_definitions(-DUNITYINDICATORS_LIBRARY)
3139
32−set(IndicatorsQML_RESOURCES
33− resources/indicators.qrc
34−)
35−
36−qt5_add_resources(IndicatorsQML_RESOURCES_RCC
37− ${IndicatorsQML_RESOURCES}
38−)
3940
4041add_library(IndicatorsQml SHARED
41− ${IndicatorsQML_RESOURCES_RCC}
4242 ${IndicatorsQML_SOURCES}
4343)
4444
4545target_link_libraries(IndicatorsQml
46+ ${GLIB_LIBRARIES}
47+ ${GIO_LIBRARIES}
4648 ${QMENUMODEL_LDFLAGS}
49+ ${LIBUPSTART_LIBRARIES}
4750)
4851
4952qt5_use_modules(IndicatorsQml Core Qml Quick)
plugins/Unity/Indicators/Messaging/qml/CMakeLists.txtdeleted−5
@@ -1,5 +0,0 @@
1−# export_qmlfiles macro
2−include(Plugins)
3−
4−# export the qmldir, *.qml files.
5−export_qmlfiles(Unity.Indicators.Messaging Unity/Indicators/Messaging)
plugins/Unity/Indicators/Network/plugin.cpp+1−1
@@ -17,7 +17,7 @@
1717 */
1818
1919// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
2121
2222// self
2323#include "plugin.h"
plugins/Unity/Indicators/Network/qml/AccessPoint.qml+19−15
@@ -22,16 +22,25 @@ import Ubuntu.Components 0.1
2222import Ubuntu.Components.ListItems 0.1 as ListItem
2323import Unity.Indicators 0.1 as Indicators
2424
25−Indicators.MenuItem {
25+Indicators.FramedMenuItem {
2626 id: accessPoint
27− property variant actionWifiApStrength : menu && actionGroup ? actionGroup.action(menu.extra.canonical_wifi_ap_strength_action) : null
28− property variant wifiApStrength : actionWifiApStrength && actionWifiApStrength.valid ? actionWifiApStrength.state : "0"
2927
30− function getNetworkIcon(data) {
28+ property bool checked: false
29+ property bool secure: false
30+ property bool adHoc: false
31+ property int signalStrength: 0
32+
33+ signal activate()
34+
35+ onCheckedChanged: {
36+ // Can't rely on binding. Checked is assigned on click.
37+ checkBoxActive.checked = checked;
38+ }
39+
40+ icon: {
3141 var imageName = "nm-signal-100"
32− var signalStrength = parseInt(wifiApStrength)
3342
34− if (data.extra.canonical_wifi_ap_is_adhoc) {
43+ if (adHoc) {
3544 imageName = "nm-adhoc";
3645 } else if (signalStrength == 0) {
3746 imageName = "nm-signal-00";
@@ -43,26 +52,21 @@ Indicators.MenuItem {
4352 imageName = "nm-signal-75";
4453 }
4554
46− if (data.extra.canonical_wifi_ap_is_secure) {
55+ if (secure) {
4756 imageName += "-secure";
4857 }
49−
5058 return "image://gicon/" + imageName;
5159 }
5260
53− icon: menu && wifiApStrength ? getNetworkIcon(menu) : "image://gicon/wifi-none"
5461 iconFrame: false
5562 control: CheckBox {
5663 id: checkBoxActive
5764 height: units.gu(4)
5865 width: units.gu(4)
5966 anchors.centerIn: parent
60− }
6167
62− Indicators.MenuActionBinding {
63− actionGroup: accessPoint.actionGroup
64− action: menu ? menu.action : ""
65− target: checkBoxActive
66− property: "checked"
68+ onClicked: {
69+ accessPoint.activate();
70+ }
6771 }
6872}
plugins/Unity/Indicators/Network/qml/PasswordPage.qml+3−3
@@ -38,10 +38,10 @@ Page {
3838 }
3939
4040 Indicators.SectionMenuItem {
41− label: "Authentication"
41+ text: "Authentication"
4242 }
4343
44− Indicators.MenuItem {
44+ Indicators.FramedMenuItem {
4545 implicitHeight: password.height + units.gu(1)
4646
4747 PasswordTextField {
@@ -55,7 +55,7 @@ Page {
5555 }
5656 }
5757
58− Indicators.MenuItem {
58+ Indicators.FramedMenuItem {
5959 Row {
6060 anchors {
6161 right: parent.right
plugins/Unity/Indicators/Network/qml/qmldir+1−2
@@ -5,5 +5,4 @@ typeinfo plugin.qmltypes
55NetworkPage 0.1 NetworkPage.qml
66AccessPoint 0.1 AccessPoint.qml
77PasswordPage 0.1 PasswordPage.qml
8−PasswordTextField 0.1 PasswordTextField.qml
9−WifiSection 0.1 WifiSection.qml
8+PasswordTextField 0.1 PasswordTextField.qml
plugins/Unity/Indicators/indicatorsmanager.cpp+40−1
@@ -21,6 +21,9 @@
2121#include <QSettings>
2222#include <QDebug>
2323
24+#include <upstart.h>
25+#include <nih/alloc.h>
26+
2427#include "paths.h"
2528
2629
@@ -31,7 +34,8 @@ public:
3134 : m_name(name)
3235 , m_fileInfo(fileInfo)
3336 , m_verified (true)
34− {}
37+ {
38+ }
3539
3640 QString m_name;
3741 QFileInfo m_fileInfo;
@@ -43,12 +47,34 @@ public:
4347IndicatorsManager::IndicatorsManager(QObject* parent)
4448 : QObject(parent)
4549 , m_loaded(false)
50+ , m_upstart(NULL)
4651{
52+ auto upstartsession = qgetenv("UPSTART_SESSION");
53+ if (!upstartsession.isNull()) {
54+ DBusConnection * conn = NULL;
55+ conn = dbus_connection_open(upstartsession.constData(), NULL);
56+ if (conn != NULL) {
57+ m_upstart = nih_dbus_proxy_new(NULL, conn,
58+ NULL,
59+ DBUS_PATH_UPSTART,
60+ NULL, NULL);
61+ dbus_connection_unref(conn);
62+ }
63+ }
64+
65+ if (m_upstart != NULL) {
66+ m_upstart->auto_start = FALSE;
67+ }
4768}
4869
4970IndicatorsManager::~IndicatorsManager()
5071{
5172 unload();
73+
74+ if (m_upstart != NULL) {
75+ nih_unref(m_upstart, NULL);
76+ m_upstart = NULL;
77+ }
5278}
5379
5480void IndicatorsManager::load()
@@ -208,6 +234,19 @@ void IndicatorsManager::setLoaded(bool loaded)
208234 {
209235 m_loaded = loaded;
210236 Q_EMIT loadedChanged(m_loaded);
237+
238+ if (m_upstart != NULL) {
239+ int event_sent = 0;
240+ if (m_loaded) {
241+ event_sent = upstart_emit_event_sync(NULL, m_upstart, "indicator-services-start", NULL, 0);
242+ } else {
243+ event_sent = upstart_emit_event_sync(NULL, m_upstart, "indicator-services-end", NULL, 0);
244+ }
245+
246+ if (event_sent != 0) {
247+ qWarning() << "Unable to send indicator event to Upstart";
248+ }
249+ }
211250 }
212251}
213252
plugins/Unity/Indicators/indicatorsmanager.h+3
@@ -28,6 +28,8 @@
2828#include <QHash>
2929#include <QSharedPointer>
3030
31+typedef struct nih_dbus_proxy NihDBusProxy;
32+
3133class UNITYINDICATORS_EXPORT IndicatorsManager : public QObject
3234{
3335 Q_OBJECT
@@ -69,6 +71,7 @@ private:
6971 QHash<QString, IndicatorData*> m_indicatorsData;
7072 QSharedPointer<QFileSystemWatcher> m_fsWatcher;
7173 bool m_loaded;
74+ NihDBusProxy * m_upstart;
7275};
7376
7477#endif // INDICATORS_MANAGER_H
plugins/Unity/Indicators/indicatorsmodel.cpp+2−2
@@ -229,9 +229,9 @@ QVariant IndicatorsModel::defaultData(Indicator::Ptr indicator, int role)
229229 case IndicatorsModelRole::Description:
230230 return "";
231231 case IndicatorsModelRole::WidgetSource:
232− return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorWidget.qml";
232+ return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorWidget2.qml";
233233 case IndicatorsModelRole::PageSource:
234− return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorPage.qml";
234+ return shellAppDirectory()+"/Panel/Indicators/DefaultIndicatorPage2.qml";
235235 }
236236 return QVariant();
237237}
plugins/Unity/Indicators/modelprinter.cpp+66−43
@@ -20,6 +20,8 @@
2020// self
2121#include "modelprinter.h"
2222
23+#include <unitymenumodel.h>
24+
2325// Qt
2426#include <QTextStream>
2527
@@ -29,7 +31,7 @@ ModelPrinter::ModelPrinter(QObject *parent)
2931{
3032}
3133
32−void ModelPrinter::setSourceModel(QAbstractItemModel * sourceModel)
34+void ModelPrinter::setSourceModel(UnityMenuModel * sourceModel)
3335{
3436 if (m_model != NULL) {
3537 disconnect(m_model);
@@ -40,75 +42,96 @@ void ModelPrinter::setSourceModel(QAbstractItemModel * sourceModel)
4042 Q_EMIT textChanged();
4143 }
4244 if (m_model != NULL) {
43− connect(m_model, SIGNAL(modelReset()), this, SIGNAL(textChanged()));
44− connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(textChanged()));
45− connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(textChanged()));
45+ connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(textChanged()));
46+ connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(textChanged()));
47+ connect(m_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SIGNAL(textChanged()));
4648 }
4749}
4850
49−QAbstractItemModel* ModelPrinter::sourceModel() const
51+UnityMenuModel* ModelPrinter::sourceModel() const
5052{
5153 return m_model;
5254}
5355
54−QString ModelPrinter::text(const QModelIndex& index) const
56+QString ModelPrinter::text()
5557{
56− return recurseString(index, 0);
58+ return getModelDataString(m_model, 0);
5759}
5860
5961QString tabify(int level) { QString str;
6062 for (int i = 0; i < level; i++) {
61− str += " ";
63+ str += " ";
6264 }
6365 return str;
6466}
6567
66−QString ModelPrinter::recurseString(const QModelIndex& index, int level) const
68+QString ModelPrinter::getModelDataString(UnityMenuModel* sourceModel, int level)
6769{
68− if (!m_model)
70+ if (!sourceModel)
6971 return "";
7072
7173 QString str;
7274 QTextStream stream(&str);
7375
74− QHash<int, QByteArray> roleNames = m_model->roleNames();
76+ int rowCount = sourceModel->rowCount();
77+ for (int row = 0; row < rowCount; row++) {
78+
79+ stream << getRowSring(sourceModel, row, level) << endl;
80+
81+ UnityMenuModel* childMenuModel = qobject_cast<UnityMenuModel*>(sourceModel->submenu(row));
82+ if (childMenuModel) {
83+
84+ if (!m_children.contains(childMenuModel)) {
85+ m_children << childMenuModel;
86+ connect(childMenuModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(textChanged()));
87+ connect(childMenuModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(textChanged()));
88+ connect(childMenuModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SIGNAL(textChanged()));
89+ }
90+ stream << getModelDataString(childMenuModel, level+1);
91+ }
92+ }
93+ return str;
94+}
95+
96+QString ModelPrinter::getRowSring(UnityMenuModel* sourceModel, int row, int depth) const
97+{
98+ QString str;
99+ QTextStream stream(&str);
100+
101+ // Print out this row
102+ QHash<int, QByteArray> roleNames = sourceModel->roleNames();
75103 QList<int> roles = roleNames.keys();
76104 qSort(roles);
105+
77106 Q_FOREACH(int role, roles) {
78− QVariant vData = m_model->data(index, role);
79− if (vData.canConvert(QMetaType::QVariantMap)) {
80− QMapIterator<QString, QVariant> iter(vData.toMap());
81− while (iter.hasNext()) {
82− iter.next();
83− stream << tabify(level)
84− << roleNames[role]
85− << "."
86− << iter.key()
87− << ": "
88− << iter.value().toString()
89− << endl;
90− }
91− }
92− else {
93− stream << tabify(level)
94− << roleNames[role]
95− << ": "
96− << vData.toString()
97− << endl;
98− }
107+ const QByteArray& roleName = roleNames[role];
108+ stream << tabify(depth) << getVariantString(roleName, sourceModel->get(row, roleName));
99109 }
110+ return str;
111+}
100112
101− int rowCount = m_model->rowCount(index);
102− stream << tabify(level)
103− << "child count"
104− << ": "
105− << rowCount
106− << endl
107− << endl;
108−
109− for (int i = 0; i < rowCount; i++) {
110− QModelIndex child = m_model->index(i, 0, index);
111− str += recurseString(child, level+1);
113+QString ModelPrinter::getVariantString(const QString& roleName, const QVariant vData ) const
114+{
115+ QString str;
116+ QTextStream stream(&str);
117+
118+ if (vData.canConvert(QMetaType::QVariantMap)) {
119+ QMapIterator<QString, QVariant> iter(vData.toMap());
120+ while (iter.hasNext()) {
121+ iter.next();
122+ stream << roleName
123+ << "."
124+ << iter.key()
125+ << ": "
126+ << iter.value().toString()
127+ << endl;
128+ }
129+ }
130+ else {
131+ stream << roleName
132+ << ": "
133+ << vData.toString()
134+ << endl;
112135 }
113136 return str;
114137}
plugins/Unity/Indicators/modelprinter.h+12−7
@@ -21,6 +21,7 @@
2121#define MODELPRINTER_H
2222
2323#include <QSortFilterProxyModel>
24+class UnityMenuModel;
2425
2526// This class acts as a namespace only, with the addition that its enums
2627// are registered to be exposed on the QML side.
@@ -28,23 +29,27 @@ class ModelPrinter : public QObject
2829{
2930 Q_OBJECT
3031
31− Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setSourceModel NOTIFY modelChanged)
32+ Q_PROPERTY(UnityMenuModel* model READ sourceModel WRITE setSourceModel NOTIFY modelChanged)
3233 Q_PROPERTY(QString text READ text NOTIFY textChanged)
3334public:
3435 ModelPrinter(QObject* parent=0);
3536
36− void setSourceModel(QAbstractItemModel* sourceModel);
37− QAbstractItemModel* sourceModel() const;
37+ void setSourceModel(UnityMenuModel* sourceModel);
38+ UnityMenuModel* sourceModel() const;
3839
39− Q_INVOKABLE QString text(const QModelIndex& index = QModelIndex()) const;
40+ Q_INVOKABLE QString text();
4041
4142Q_SIGNALS:
4243 void modelChanged();
4344 void textChanged();
4445
4546private:
46− QString recurseString(const QModelIndex& index, int level) const;
47− QAbstractItemModel* m_model;
47+ QString getModelDataString(UnityMenuModel* sourceModel, int level);
48+ QString getRowSring(UnityMenuModel* sourceModel, int index, int depth) const;
49+ QString getVariantString(const QString& roleName, const QVariant vData ) const;
50+
51+ UnityMenuModel* m_model;
52+ QList<UnityMenuModel*> m_children;
4853};
4954
50−#endif // MODELPRINTER_H
55+#endif // MODELPRINTER_H
plugins/Unity/Indicators/plugin.cpp+23−13
@@ -1,44 +1,54 @@
11/*
2− * Copyright (C) 2012 Canonical, Ltd.
2+ * Copyright 2013 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
5+ * it under the terms of the GNU Lesser General Public License as published by
66 * the Free Software Foundation; version 3.
77 *
88 * This program is distributed in the hope that it will be useful,
99 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
11+ * GNU Lesser General Public License for more details.
1212 *
13− * You should have received a copy of the GNU General Public License
13+ * You should have received a copy of the GNU Lesser General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 *
16− * Author: Nick Dedekind <nick.dedekind@canonical.com>
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
1718 */
1819
1920// Qt
20−#include <QtQml>
21+#include <QtQml/qqml.h>
2122
2223// self
2324#include "plugin.h"
2425
2526// local
26−#include "flatmenuproxymodel.h"
27+#include "indicators.h"
2728#include "indicatorsmanager.h"
2829#include "indicatorsmodel.h"
29−#include "indicators.h"
3030#include "menucontentactivator.h"
3131#include "modelprinter.h"
32+#include "rootactionstate.h"
33+#include "unitymenumodelcache.h"
34+#include "unitymenumodelstack.h"
3235
33−void IndicatorsPlugin::registerTypes(const char *uri)
34−{
35− Q_INIT_RESOURCE(indicators);
36+static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
37+ Q_UNUSED(engine);
38+ Q_UNUSED(scriptEngine);
39+ return new UnityMenuModelCache;
40+}
3641
42+void Indicators2Plugin::registerTypes(const char *uri)
43+{
3744 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
38− qmlRegisterType<FlatMenuProxyModel>(uri, 0, 1, "FlatMenuProxyModel");
3945 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
40− qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
4146 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
47+ qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
48+ qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
49+ qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
50+
51+ qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
4252
4353 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
4454 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
plugins/Unity/Indicators/plugin.h+10−9
@@ -1,27 +1,28 @@
11/*
2− * Copyright (C) 2012 Canonical, Ltd.
2+ * Copyright 2013 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
5+ * it under the terms of the GNU Lesser General Public License as published by
66 * the Free Software Foundation; version 3.
77 *
88 * This program is distributed in the hope that it will be useful,
99 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
11+ * GNU Lesser General Public License for more details.
1212 *
13− * You should have received a copy of the GNU General Public License
13+ * You should have received a copy of the GNU Lesser General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 *
16− * Author: Nick Dedekind <nick.dedekind@canonical.com>
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
1718 */
1819
19−#ifndef INDICATORS_PLUGIN_H
20−#define INDICATORS_PLUGIN_H
20+#ifndef INDICATORS2_PLUGIN_H
21+#define INDICATORS2_PLUGIN_H
2122
2223#include <QtQml/QQmlExtensionPlugin>
2324
24−class IndicatorsPlugin : public QQmlExtensionPlugin
25+class Indicators2Plugin : public QQmlExtensionPlugin
2526{
2627 Q_OBJECT
2728 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
@@ -29,4 +30,4 @@ public:
2930 void registerTypes(const char *uri);
3031};
3132
32−#endif // INDICATORS_PLUGIN_H
33+#endif // INDICATORS2_PLUGIN_H
plugins/Unity/Indicators/qml/BaseMenuItem.qmlnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
19+ */
20+
21+import QtQuick 2.0
22+import Ubuntu.Components.ListItems 0.1 as ListItem
23+
24+ListItem.Standard {
25+ id: baseMenu
26+
27+ property bool menuActivated: false
28+
29+ signal selectMenu()
30+ signal deselectMenu()
31+
32+ showDivider: false
33+ __foregroundColor: "#e8e1d0" // FIXME not in palette yet
34+
35+ backgroundIndicator: RemoveBackground {
36+ state: baseMenu.swipingState
37+ }
38+}
plugins/Unity/Indicators/qml/ButtonMenuItem.qml+9−12
@@ -15,33 +15,30 @@
1515 *
1616 * Authors:
1717 * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
1819 */
1920
2021import QtQuick 2.0
2122import Ubuntu.Components 0.1
23+import Unity.Indicators 0.1 as Indicators
2224
23−BasicMenuItem {
24− id: buttonMenu
25− color: "#221e1b"
25+BaseMenuItem {
26+ id: menuItem
2627 implicitHeight: units.gu(7)
2728
29+ property alias text: button.text
30+
31+ signal activate()
32+
2833 Button {
2934 id: button
30− enabled: menuAction.valid
31− text: menu ? menu.label : ""
3235 anchors.centerIn: parent
3336 height: units.gu(4)
3437 width: units.gu(16)
3538 color: "#1b1817"
3639
3740 onClicked: {
38− menuAction.activate()
41+ menuItem.activate();
3942 }
4043 }
41−
42− MenuAction {
43− id: menuAction
44− actionGroup: buttonMenu.actionGroup
45− action: menu ? menu.action : ""
46− }
4744}
plugins/Unity/Indicators/qml/CachedUnityMenuModel.qmlnew file+60
@@ -0,0 +1,60 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import QMenuModel 0.1 as QMenuModel
22+import Unity.Indicators 0.1 as Indicators
23+
24+// Make sure we don't duplicate models.
25+Item {
26+ id: cachedModel
27+ property string busName: ""
28+ property string actionsObjectPath: ""
29+ property string menuObjectPath: ""
30+
31+ readonly property bool ready: busName!=="" && actionsObjectPath!=="" && menuObjectPath!==""
32+
33+ property QtObject model: {
34+ if (!ready) {
35+ return null;
36+ }
37+
38+ var component = Indicators.UnityMenuModelCache.model(menuObjectPath);
39+ if (!component) {
40+ component = modelComponent.createObject(cachedModel);
41+ return component;
42+ }
43+ return component;
44+ }
45+
46+ Component {
47+ id: modelComponent
48+
49+ QMenuModel.UnityMenuModel {
50+ id: unityModel
51+ busName: cachedModel.busName
52+ actions: { "indicator": cachedModel.actionsObjectPath }
53+ menuObjectPath: cachedModel.menuObjectPath
54+
55+ Component.onCompleted: {
56+ Indicators.UnityMenuModelCache.registerModel(cachedModel.menuObjectPath, unityModel);
57+ }
58+ }
59+ }
60+}
plugins/Unity/Indicators/qml/DivMenuItem.qml+1−1
@@ -21,6 +21,6 @@ import QtQuick 2.0
2121import Ubuntu.Components 0.1
2222
2323Rectangle {
24− color: "#181514"
24+ color: "#181514" // FIXME not in palette yet
2525 implicitHeight: units.gu(1)
2626}
plugins/Unity/Indicators/qml/FramedMenuItem.qmlnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
19+ */
20+
21+import QtQuick 2.0
22+import Ubuntu.Components 0.1
23+import Ubuntu.Components.ListItems 0.1 as ListItem
24+
25+BaseMenuItem {
26+ implicitHeight: units.gu(7)
27+
28+ iconFrame: false
29+
30+ HLine {
31+ anchors.top: parent.top
32+ color: "#403b37" // FIXME not in palette yet
33+ }
34+
35+ HLine {
36+ anchors.bottom: parent.bottom
37+ color: "#060606" // FIXME not in palette yet
38+ }
39+}
plugins/Unity/Indicators/qml/IndicatorBase.qml+13−19
@@ -14,46 +14,40 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 *
1616 * Authors:
17− * Renato Araujo Oliveira Filho <renato@canonical.com>
1817 * Nick Dedekind <nick.dedekind@canonical.com>
1918 */
2019
2120import QtQuick 2.0
22−import QMenuModel 0.1
21+import QMenuModel 0.1 as QMenuModel
2322import Unity.Indicators 0.1 as Indicators
2423
2524Item {
2625 id: indicatorItem
2726
27+ // FIXME : should be disabled until bus available when we have desktop indicators
28+ // for now, disable when we dont habe the correct profile.
29+ enabled: menuObjectPaths.hasOwnProperty(device)
30+
2831 //const
2932 property string title
30− property int busType
3133 property string busName
3234 property string actionsObjectPath
3335 property var menuObjectPaths: undefined
3436 readonly property string device: "phone"
37+ property string rootMenuType: "com.canonical.indicator.root"
38+ property bool active: false
3539
3640 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
3741
3842 signal actionGroupUpdated()
3943 signal modelUpdated()
4044
41− property var actionGroup: QDBusActionGroup {
42− busType: indicatorItem.busType
43− busName: indicatorItem.busName
44− objectPath: indicatorItem.actionsObjectPath
45−
46− onActionAppear: indicatorItem.actionGroupUpdated();
47− }
48−
49− property var proxyModel: Indicators.FlatMenuProxyModel {
50− busType: indicatorItem.busType
51− busName: indicatorItem.busName
52− objectPath: indicatorItem.deviceMenuObjectPath
45+ property alias menuModel: cachedModel.model
5346
54− onStatusChanged: indicatorItem.modelUpdated();
55− onRowsInserted: indicatorItem.modelUpdated();
56− onRowsRemoved: indicatorItem.modelUpdated();
57− onDataChanged: indicatorItem.modelUpdated();
47+ CachedUnityMenuModel {
48+ id: cachedModel
49+ busName: active ? indicatorItem.busName : ""
50+ actionsObjectPath: active ? indicatorItem.actionsObjectPath : ""
51+ menuObjectPath: active ? indicatorItem.deviceMenuObjectPath : ""
5852 }
5953}
plugins/Unity/Indicators/qml/IndicatorPage.qml+43−79
@@ -20,6 +20,7 @@
2020
2121import QtQuick 2.0
2222import Ubuntu.Components 0.1 as Components
23+import Unity.Indicators 0.1 as Indicators
2324
2425IndicatorBase {
2526 id: main
@@ -27,14 +28,16 @@ IndicatorBase {
2728 //const
2829 property alias emptyText: emptyLabel.text
2930 property alias highlightFollowsCurrentItem : mainMenu.highlightFollowsCurrentItem
30− property bool __active: false
31+
32+ Indicators.UnityMenuModelStack {
33+ id: menuStack
34+ head: main.menuModel
35+ }
3136
3237 ListView {
3338 id: mainMenu
39+ model: menuStack.tail ? menuStack.tail : null
3440
35− property int visibleItems: 0
36−
37− model: proxyModel
3841 anchors {
3942 fill: parent
4043 bottomMargin: Qt.inputMethod.visible ? (Qt.inputMethod.keyboardRectangle.height - main.anchors.bottomMargin) : 0
@@ -59,95 +62,60 @@ IndicatorBase {
5962
6063 currentIndex: -1
6164 delegate: Item {
62− id: item
63− property bool ready: false
64− property alias empty: factory.empty
65+ id: menuDelegate
6566
6667 anchors {
6768 left: parent.left
6869 right: parent.right
6970 }
70− height: div.height + factory.implicitHeight
71+ height: loader.height
7172 visible: height > 0
7273
73− Component.onCompleted: {
74− if (!item.empty) {
75− mainMenu.visibleItems += 1;
76− }
77− ready = true;
78− }
79− Component.onDestruction: {
80− if (!item.empty) {
81− mainMenu.visibleItems -= 1;
82− }
83− ready = false;
84− }
74+ Loader {
75+ id: loader
76+ asynchronous: true
8577
86− onEmptyChanged: {
87− if (!ready) {
88− return;
89− }
90− if (empty) {
91− mainMenu.visibleItems -= 1;
92− } else {
93− mainMenu.visibleItems += 1;
94− }
95− }
78+ property int modelIndex: index
9679
97− ListView.onRemove: {
98− if (!highlightFollowsCurrentItem) {
99− mainMenu.currentIndex = -1;
80+ anchors {
81+ left: parent.left
82+ right: parent.right
10083 }
101− }
10284
103− Column {
104− id: contents
105−
106− anchors.fill: parent
107−
108− DivMenuItem {
109− id: div
110−
111− anchors.left: parent.left
112− anchors.right: parent.right
113− // only visible if is a root or a section header element and the factory is visible
114− height: {
115− // skipe first item
116− if (item.y == 0) {
117− return 0;
118− }
119− if (hasSection || ((depth == 0) && (!factory.empty))) {
120− return implicitHeight;
121− } else {
122− return 0;
123− }
124− }
125− }
85+ sourceComponent: factory.load(model)
12686
127− MenuItemFactory {
128− id: factory
129−
130− actionGroup: main.actionGroup
131− isCurrentItem: item.ListView.isCurrentItem
132− anchors {
133− left: parent.left
134− right: parent.right
87+ onLoaded: {
88+ if (model.type === rootMenuType) {
89+ menuStack.push(mainMenu.model.submenu(index));
13590 }
136− menu: model
13791
138− Connections {
139− target : factory
140− onActivated: item.ListView.view.currentIndex = index
141− onDeactivated: item.ListView.view.currentIndex = -1
92+ if (item.hasOwnProperty("menuActivated")) {
93+ item.menuActivated = Qt.binding(function() { return ListView.isCurrentItem; });
94+ item.selectMenu.connect(function() { ListView.view.currentIndex = index });
95+ item.deselectMenu.connect(function() { ListView.view.currentIndex = -1 });
96+ }
97+ if (item.hasOwnProperty("menu")) {
98+ item.menu = Qt.binding(function() { return model; });
14299 }
143100 }
101+
102+ Binding {
103+ target: item ? item : null
104+ property: "objectName"
105+ value: model.action
106+ }
144107 }
145108 }
146109 }
147110
111+ MenuItemFactory {
112+ id: factory
113+ model: mainMenu.model
114+ }
115+
148116 Components.Label {
149117 id: emptyLabel
150− visible: (mainMenu.visibleItems === 0)
118+ visible: mainMenu.count == 0
151119 anchors {
152120 top: parent.top
153121 left: parent.left
@@ -167,19 +135,15 @@ IndicatorBase {
167135 function start()
168136 {
169137 reset()
170− if (!__active) {
171− proxyModel.start();
172− actionGroup.start();
173− __active = true;
138+ if (!active) {
139+ active = true;
174140 }
175141 }
176142
177143 function stop()
178144 {
179− if (__active) {
180− __active = false;
181− proxyModel.stop();
182− actionGroup.stop();
145+ if (active) {
146+ active = false;
183147 }
184148 }
185149
plugins/Unity/Indicators/qml/IndicatorWidget.qml+4−39
@@ -23,46 +23,11 @@ import Unity.Indicators 0.1 as Indicators
2323IndicatorBase {
2424 id: indicatorWidget
2525
26−// enabled: false // FIXME : should be disabled until bus available when we have desktop indicators
27− property var action: undefined
28− property bool started: (busType != 0) && (busName != "") && (deviceMenuObjectPath != "")
26+ active: true
2927 property int iconSize: height - units.gu(0.5)
30− property string rootMenuType: "com.canonical.indicator.root"
3128
32− signal actionStateChanged()
33−
34− onStartedChanged: {
35− if (started) {
36− proxyModel.start();
37− actionGroup.start();
38− } else {
39− proxyModel.stop();
40− actionGroup.stop();
41− }
42− }
43− onActionChanged: actionStateChanged()
44− onActionGroupUpdated: update_state()
45− onModelUpdated: update_state()
46−
47−
48− function update_state() {
49− var extra = proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Extra);
50− if (extra == undefined) {
51− return;
52− }
53−
54− if (!extra.hasOwnProperty("canonical_type")) {
55− return;
56− }
57−
58− if (extra.canonical_type === rootMenuType) {
59− action = actionGroup.action(proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Action));
60− }
61− }
62−
63− Connections {
64− target: action == undefined ? null : action
65− onStateChanged: actionStateChanged()
66− onValidChanged: actionStateChanged()
29+ property var actionState: Indicators.RootActionState {
30+ menu: menuModel
31+ onUpdated: indicatorWidget.actionStateChanged()
6732 }
6833}
plugins/Unity/Indicators/qml/MenuItemFactory.qml+158−56
@@ -19,22 +19,13 @@
1919
2020import QtQuick 2.0
2121import Unity.Indicators 0.1 as Indicators
22−import Unity.Indicators.Messaging 0.1 as ICMessaging
2322import Unity.Indicators.Network 0.1 as ICNetwork
23+import QMenuModel 0.1 as QMenuModel
2424
2525Item {
2626 id: menuFactory
2727
28− property QtObject menu
29− property QtObject listView
30− property QtObject actionGroup
31− property bool isCurrentItem
32− readonly property bool empty: (loader.item !== undefined && loader.status == Loader.Ready) ? loader.item.state === "EMPTY" : true
33−
34− implicitHeight: loader.height
35−
36− signal activated()
37− signal deactivated()
28+ property var model: null
3829
3930 property var _map: {
4031 "unity.widgets.systemsettings.tablet.volumecontrol" : sliderMenu,
@@ -47,64 +38,175 @@ Item {
4738 "com.canonical.indicator.slider" : sliderMenu,
4839 "com.canonical.indicator.switch" : switchMenu,
4940
50− "com.canonical.indicator.messages.messageitem" : messageItem,
51− "com.canonical.indicator.messages.snapdecision" : snapDecision,
52− "com.canonical.indicator.messages.sourceitem" : groupedMessage,
41+ "com.canonical.unity.slider" : sliderMenu,
42+ "com.canonical.unity.switch" : switchMenu,
5343
54− "unity.widget.systemsettings.tablet.sectiontitle" : wifiSection,
5544 "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
5645 "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
5746 }
5847
59− Component { id: sliderMenu; Indicators.SliderMenuItem {} }
60− Component { id: switchMenu; Indicators.SwitchMenuItem {} }
61− Component { id: buttonMenu; Indicators.ButtonMenuItem {} }
62− Component { id: divMenu; Indicators.DivMenuItem {} }
63− Component { id: sectionMenu; Indicators.SectionMenuItem {} }
64− Component { id: progressMenu; Indicators.ProgressMenuItem {} }
65− Component { id: messageItem; ICMessaging.MessageMenuItem {} }
66− Component { id: snapDecision; ICMessaging.SnapDecision {} }
67− Component { id: groupedMessage; ICMessaging.GroupedMessage {} }
68− Component { id: wifiSection; ICNetwork.WifiSection {} }
69− Component { id: accessPoint; ICNetwork.AccessPoint {} }
70− Component { id: indicatorMenu; Indicators.MenuItem {} }
71−
72− Loader {
73− id: loader
74− asynchronous: true
75−
76− anchors {
77− left: parent.left
78− right: parent.right
79− }
80−
81− sourceComponent: {
82− if (!menuFactory.menu || !menuFactory.menu.extra) {
83− return undefined;
48+ Component {
49+ id: sliderMenu;
50+ Indicators.SliderMenuItem {
51+ property QtObject menu: null
52+
53+ text: menu && menu.label ? menu.label : ""
54+ icon: menu ? menu.icon : ""
55+ minIcon: menu.ext.minIcon
56+ maxIcon: menu.ext.maxIcon
57+
58+ minimumValue: menu.ext.minValue ? menu.ext.minValue : 0.0
59+ maximumValue: {
60+ var maximum = menu.ext.maxValue ? menu.ext.maxValue : 1.0
61+ if (maximum <= minimumValue) {
62+ return minimumValue + 1;
63+ }
64+ return maximum;
65+ }
66+ value: menu ? menu.actionState : 0.0
67+ enabled: menu ? menu.sensitive : false
68+
69+ Component.onCompleted: {
70+ model.loadExtendedAttributes(modelIndex, {'min-value': 'double',
71+ 'max-value': 'double',
72+ 'min-icon': 'icon',
73+ 'max-icon': 'icon'});
8474 }
8575
86− var widgetType = menuFactory.menu.extra.canonical_type;
87− if (widgetType) {
88− return _map[widgetType];
89− } else {
90− if (widgetType === "com.canonical.indicator.root") {
91− return undefined;
92− // Try discovery the item based on the basic properties
93− } else if (menu.hasSection) {
94− return sectionMenu;
76+ // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
77+ // reaching the boundaries (0.0 or 1.0), the value is converted
78+ // to an integer when automatically wrapped in a variant when
79+ // passed to QStateAction::updateState(…). The server chokes on
80+ // those values, complaining that they’re not of the right type…
81+ onChangeState: {
82+ if (value == Math.round(value)) {
83+ if (value >= maximumValue) {
84+ model.changeState(modelIndex, maximumValue - 0.000001);
85+ } else if (value <= minimumValue) {
86+ model.changeState(modelIndex, minimumValue + 0.000001);
87+ } else {
88+ model.changeState(modelIndex, value * 1.000001);
89+ }
9590 } else {
96− return indicatorMenu;
91+ model.changeState(modelIndex, value);
9792 }
9893 }
9994 }
95+ }
96+
97+ Component { id: divMenu; Indicators.DivMenuItem {} }
98+
99+ Component {
100+ id: buttonMenu;
101+ Indicators.ButtonMenuItem {
102+ property QtObject menu: null
103+
104+ text: menu && menu.label ? menu.label : ""
105+ enabled: menu ? menu.sensitive : false
106+
107+ onActivate: model.activate(modelIndex);
108+ }
109+ }
110+ Component {
111+ id: sectionMenu;
112+ Indicators.SectionMenuItem {
113+ property QtObject menu: null
114+
115+ text: menu && menu.label ? menu.label : ""
116+ }
117+ }
118+
119+ Component {
120+ id: progressMenu;
121+ Indicators.ProgressMenuItem {
122+ property QtObject menu: null
123+
124+ text: menu && menu.label ? menu.label : ""
125+ icon: menu ? menu.icon : ""
126+ value : menu ? menu.actionState : 0.0
127+ }
128+ }
100129
101− onLoaded: {
102− item.menuActivated = Qt.binding(function() { return isCurrentItem; });
103− item.actionGroup = Qt.binding(function() { return menuFactory.actionGroup; });
104− item.menu = Qt.binding(function() { return menuFactory.menu; });
130+ Component {
131+ id: standardMenu;
132+ Indicators.StandardMenuItem {
133+ property QtObject menu: null
105134
106− item.activateMenu.connect(function() { menuFactory.activated(); });
107− item.deactivateMenu.connect(function() { menuFactory.deactivated(); });
135+ text: menu && menu.label ? menu.label : ""
136+ icon: menu ? menu.icon : ""
137+ checkable: menu ? (menu.isCheck || menu.isRadio) : false
138+ checked: checkable ? menu.isToggled : false
139+ enabled: menu ? menu.sensitive : false
140+
141+ onActivate: model.activate(modelIndex);
142+ }
143+ }
144+
145+ Component {
146+ id: switchMenu;
147+ Indicators.SwitchMenuItem {
148+ property QtObject menu: null
149+
150+ text: menu && menu.label ? menu.label : ""
151+ icon: menu ? menu.icon : ""
152+ checked: menu ? menu.isToggled : false
153+ enabled: menu ? menu.sensitive : false
154+
155+ onActivate: model.activate(modelIndex);
156+ }
157+ }
158+
159+ Component {
160+ id: wifiSection;
161+ Indicators.SectionMenuItem {
162+ property QtObject menu: null
163+
164+ text: menu && menu.label ? menu.label : ""
165+ busy: menu ? menu.ext.xCanonicalBusyAction : false
166+
167+ Component.onCompleted: {
168+ model.loadExtendedAttributes(modelIndex, {'x-canonical-busy-action': 'bool'});
169+ }
170+ }
171+ }
172+
173+ Component {
174+ id: accessPoint;
175+ ICNetwork.AccessPoint {
176+ property QtObject menu: null
177+// property var strenthAction: QMenuModel.UnityMenuAction {
178+// model: menuFactory.model ? menuFactory.model : null
179+// name: menu ? menu.ext.xCanonicalWifiApStrengthAction : ""
180+// }
181+
182+ text: menu && menu.label ? menu.label : ""
183+ icon: menu ? menu.icon : ""
184+ secure: menu ? menu.ext.xCanonicalWifiApIsSecure : false
185+ adHoc: menu ? menu.ext.xCanonicalWifiApIsAdhoc : false
186+ checked: menu ? menu.isToggled : false
187+// signalStrength: strenthAction.valid ? strenthAction.state : 0
188+ enabled: menu ? menu.sensitive : false
189+
190+ Component.onCompleted: {
191+ model.loadExtendedAttributes(modelIndex, {'x-canonical-wifi-ap-is-adhoc': 'bool',
192+ 'x-canonical-wifi-ap-is-secure': 'bool',
193+ 'x-canonical-wifi-ap-strength-action': 'string'});
194+ }
195+ onActivate: model.activate(modelIndex);
196+ }
197+ }
198+
199+ function load(modelData) {
200+ if (modelData.type !== undefined) {
201+ var component = _map[modelData.type];
202+ if (component !== undefined) {
203+ return component;
204+ }
205+ } else {
206+ if (modelData.isSeparator) {
207+ return divMenu;
208+ }
108209 }
210+ return standardMenu;
109211 }
110212}
plugins/Unity/Indicators/qml/ProgressMenuItem.qml+11−38
@@ -19,47 +19,20 @@
1919
2020import QtQuick 2.0
2121import Ubuntu.Components 0.1
22+import Unity.Indicators 0.1 as Indicators
2223
23−MenuItem {
24− id: progressMenu
25− property alias value : progressBar.value
24+FramedMenuItem {
25+ id: menuItem
2626
27− // TODO: Replace this with the official ProgressBar component as soon as
28− // it is available. For now, this rebuilds the Mockup as close as possible.
29− UbuntuShape {
30− id: progressBar
31− anchors {
32− right: parent.right
33− rightMargin: units.gu(2)
34− left: parent.left
35− leftMargin: units.gu(2)
36− }
37− height: units.gu(4)
38− anchors.verticalCenter: parent.verticalCenter
39− color: "transparent"
40−
41− property int minimumValue: 0
42− property int maximumValue: 100
43− property int value: 50
44−
45− UbuntuShape {
46− anchors.fill: parent
47− anchors.rightMargin: parent.width - (parent.width * parent.value / parent.maximumValue)
48− color: "#c94212"
49− }
27+ property int value : 0.0
5028
51− Label {
52− anchors.centerIn: parent
53− text: parent.value + " %"
54− fontSize: "medium"
55− color: "#e8e1d0"
56− }
57− }
29+ control: Label {
30+ id: progress
31+ anchors.verticalCenter: parent.verticalCenter
32+ anchors.right: parent.right
5833
59− MenuActionBinding {
60− actionGroup: progressMenu.actionGroup
61− action: menu ? menu.action : ""
62− target: progressBar
63− property: "value"
34+ text: menuItem.value + " %"
35+ fontSize: "medium"
36+ color: "#e8e1d0"
6437 }
6538}
plugins/Unity/Indicators/qml/SectionMenuItem.qml+5−8
@@ -21,19 +21,16 @@ import QtQuick 2.0
2121import Ubuntu.Components 0.1
2222import Ubuntu.Components.ListItems 0.1 as ListItem
2323
24−BasicMenuItem {
25− property string label: menu && menu.label ? menu.label : ""
24+BaseMenuItem {
25+ id: menuItem
26+ property alias text: header.text
2627 property bool busy: false
27− property int count: menu && menu.linkSection ? menu.linkSection.count : 0
2828
29− color: "#221e1b"
30− implicitHeight: label !== "" ? __header.height : 0
31− state: count === 0 ? "EMPTY" : ""
29+ implicitHeight: text !== "" ? header.height : 0
3230
3331 ListItem.Header {
34− id: __header
32+ id: header
3533
36− text: label
3734 height: units.gu(4)
3835 anchors {
3936 left: parent.left
plugins/Unity/Indicators/qml/SliderMenuItem.qml+75−34
@@ -15,50 +15,91 @@
1515 *
1616 * Authors:
1717 * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
1819 */
1920
2021import QtQuick 2.0
21−import Ubuntu.Components 0.1
22+import Ubuntu.Components 0.1 as Components
23+import Unity.Indicators 0.1 as Indicators
2224
23−MenuItem {
24− id: _sliderMenu
25+FramedMenuItem {
26+ id: menuItem
2527
2628 property alias minimumValue: slider.minimumValue
2729 property alias maximumValue: slider.maximumValue
28− property alias value: slider.value
29−
30− control: Slider {
31− id: slider
32− enabled: menuAction.valid
33− width: _sliderMenu.text ? units.gu(20) : _sliderMenu.width - units.gu(4)
34− anchors.verticalCenter: parent.verticalCenter
35− live: false
36− // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
37− // reaching the boundaries (0.0 or 1.0), the value is converted
38− // to an integer when automatically wrapped in a variant when
39− // passed to QStateAction::updateState(…). The server chokes on
40− // those values, complaining that they’re not of the right type…
41− minimumValue: menu.extra.canonical_min ? menu.extra.canonical_min * 1.000001 : 0.0000001
42− maximumValue: {
43− var maximum = menu.extra.canonical_max ? menu.extra.canonical_max * 1.000001 : 0.9999999
44− if (maximum <= minimumValue) {
45− return minimumValue + 1;
46− }
47− return maximum;
30+ property double value: 0.0
31+
32+ property alias minIcon: leftImage.source
33+ property alias maxIcon: rightImage.source
34+
35+ property QtObject d: QtObject {
36+ property bool enableValueConnection: true
37+ }
38+
39+ signal changeState(real value)
40+
41+ onValueChanged: {
42+ // TODO: look into adding a component to manage bi-directional bindings.
43+ var oldEnable = d.enableValueConnection
44+ d.enableValueConnection = false;
45+
46+ // Can't rely on binding. Slider value is assigned by user slide.
47+ slider.value = value;
48+
49+ d.enableValueConnection = oldEnable;
50+ }
51+
52+ control: Item {
53+ id: row
54+ width: sliderMenu.text ? units.gu(20) : menuItem.width - (2 * menuItem.__contentsMargins)
55+ height: slider.height
56+
57+ Image {
58+ id: leftImage
59+ visible: source != ""
60+ anchors.left: row.left
61+ anchors.verticalCenter: row.verticalCenter
62+ height: units.gu(4)
63+ width: height
4864 }
4965
50− // FIXME - to be deprecated in Ubuntu.Components.
51− // Use this to disable the label, since there is not way to do it on the component.
52− function formatValue(v) {
53− return "";
66+ Components.Slider {
67+ id: slider
68+ anchors {
69+ left: leftImage.visible ? leftImage.right : row.left
70+ right: rightImage.visible ? rightImage.left : row.right
71+ leftMargin: leftImage.visible ? units.gu(0.5) : 0
72+ rightMargin: rightImage.visible ? units.gu(0.5) : 0
73+ }
74+ live: false
75+
76+ Component.onCompleted: {
77+ value = menuItem.value
78+ }
79+
80+ minimumValue: 0.0
81+ maximumValue: 0.1
82+
83+ // FIXME - to be deprecated in Ubuntu.Components.
84+ // Use this to disable the label, since there is not way to do it on the component.
85+ function formatValue(v) {
86+ return "";
87+ }
88+
89+ Connections {
90+ target: d.enableValueConnection ? slider : null
91+ onValueChanged: {
92+ menuItem.changeState(slider.value);
93+ }
94+ }
5495 }
55− }
5696
57− MenuActionBinding {
58− id: menuAction
59− actionGroup: _sliderMenu.actionGroup
60− action: menu ? menu.action : ""
61− target: slider
62− property: "value"
97+ Image {
98+ id: rightImage
99+ anchors.right: row.right
100+ anchors.verticalCenter: row.verticalCenter
101+ height: units.gu(4)
102+ width: height
103+ }
63104 }
64105}
plugins/Unity/Indicators/qml/StandardMenuItem.qmlnew file+71
@@ -0,0 +1,71 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1 as Components
22+import Unity.Indicators 0.1 as Indicators
23+
24+FramedMenuItem {
25+ id: menuItem
26+
27+ property bool checkable: false
28+ property bool checked: false
29+
30+ signal activate()
31+
32+ // FIXME : need a radio button from sdk
33+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1211866
34+ onCheckableChanged: {
35+ if (checkable) {
36+ icon = checkComponent.createObject(menuItem);
37+ }
38+ }
39+
40+ onCheckedChanged: {
41+ // Can't rely on binding. Checked is assigned on click.
42+ if (icon) {
43+ icon.checked = checked;
44+ }
45+ }
46+
47+ onClicked: {
48+ if (checkable && icon) {
49+ icon.clicked();
50+ } else {
51+ menuItem.activate();
52+ }
53+ }
54+
55+ property var checkComponent: Component {
56+ Components.CheckBox {
57+ anchors.verticalCenter: parent.verticalCenter
58+ anchors.left: parent.left
59+
60+ Component.onCompleted: {
61+ checked = menuItem.checked;
62+ }
63+
64+ // FIXME : should use Checkbox.toggled signal
65+ // lp:~nick-dedekind/ubuntu-ui-toolkit/checkbox.toggled
66+ onClicked: {
67+ menuItem.activate();
68+ }
69+ }
70+ }
71+}
plugins/Unity/Indicators/qml/SwitchMenuItem.qml+21−11
@@ -15,29 +15,39 @@
1515 *
1616 * Authors:
1717 * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Nick Dedekind <nick.dedekind@canonical.com>
1819 */
1920
2021import QtQuick 2.0
2122import Ubuntu.Components 0.1
23+import Unity.Indicators 0.1 as Indicators
2224
23−MenuItem {
24− id: switchMenu
25+FramedMenuItem {
26+ id: menuItem
2527
26− property alias checked: switcher.checked
28+ property bool checked: false
29+
30+ signal activate()
31+
32+ onCheckedChanged: {
33+ // Can't rely on binding. Checked is assigned on click.
34+ switcher.checked = checked;
35+ }
2736
2837 control: Switch {
2938 id: switcher
30− enabled: menuAction.valid
39+
40+ Component.onCompleted: {
41+ checked = menuItem.checked;
42+ }
3143
3244 anchors.verticalCenter: parent.verticalCenter
3345 anchors.right: parent.right
34− }
3546
36− MenuActionBinding {
37− id: menuAction
38− actionGroup: switchMenu.actionGroup
39− action: menu ? menu.action : ""
40− target: switcher
41− property: "checked"
47+ // FIXME : should use Checkbox.toggled signal
48+ // lp:~nick-dedekind/ubuntu-ui-toolkit/checkbox.toggled
49+ onClicked: {
50+ menuItem.activate();
51+ }
4252 }
4353}
plugins/Unity/Indicators/qml/qmldir+5−3
@@ -2,20 +2,22 @@ module Unity.Indicators
22plugin IndicatorsQml
33typeinfo plugin.qmltypes
44
5−BasicMenuItem 0.1 BasicMenuItem.qml
5+BaseMenuItem 0.1 BaseMenuItem.qml
66ButtonMenuItem 0.1 ButtonMenuItem.qml
7+CachedUnityMenuModel 0.1 CachedUnityMenuModel.qml
78DivMenuItem 0.1 DivMenuItem.qml
9+FramedMenuItem 0.1 FramedMenuItem.qml
810HLine 0.1 HLine.qml
911IndicatorBase 0.1 IndicatorBase.qml
1012IndicatorPage 0.1 IndicatorPage.qml
1113IndicatorWidget 0.1 IndicatorWidget.qml
12−MenuItem 0.1 MenuItem.qml
13−MenuAction 0.1 MenuAction.qml
1414MenuActionBinding 0.1 MenuActionBinding.qml
1515MenuItemFactory 0.1 MenuItemFactory.qml
1616ProgressMenuItem 0.1 ProgressMenuItem.qml
1717SectionMenuItem 0.1 SectionMenuItem.qml
1818SliderMenuItem 0.1 SliderMenuItem.qml
19+StandardMenuItem 0.1 StandardMenuItem.qml
1920SwitchMenuItem 0.1 SwitchMenuItem.qml
2021
22+
2123internal RemoveBackground RemoveBackground.qml
plugins/Unity/Indicators/rootactionstate.cppnew file+249
@@ -0,0 +1,249 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "rootactionstate.h"
21+#include "indicators.h"
22+
23+#include <unitymenumodel.h>
24+#include <QVariant>
25+#include <QIcon>
26+
27+extern "C" {
28+#include <glib.h>
29+#include <gio/gio.h>
30+}
31+
32+RootActionState::RootActionState(QObject *parent)
33+ : ActionStateParser(parent),
34+ m_menu(NULL)
35+{
36+}
37+
38+RootActionState::~RootActionState()
39+{
40+}
41+
42+UnityMenuModel* RootActionState::menu() const
43+{
44+ return m_menu;
45+}
46+
47+void RootActionState::setMenu(UnityMenuModel* menu)
48+{
49+ if (m_menu != menu) {
50+ if (m_menu) {
51+ m_menu->disconnect(this);
52+ }
53+ m_menu = menu;
54+
55+ if (m_menu) {
56+ connect(m_menu, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(onModelRowsAdded(const QModelIndex&, int, int)));
57+ connect(m_menu, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(onModelRowsRemoved(const QModelIndex&, int, int)));
58+ connect(m_menu, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SLOT(onModelDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
59+
60+ m_menu->setActionStateParser(this);
61+ }
62+ updateActionState();
63+ Q_EMIT menuChanged();
64+ }
65+}
66+
67+void RootActionState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
68+{
69+ Q_UNUSED(parent);
70+ if (start == 0 && end >= 0) {
71+ updateActionState();
72+ }
73+}
74+
75+void RootActionState::onModelRowsRemoved(const QModelIndex& parent, int start, int end)
76+{
77+ Q_UNUSED(parent);
78+ if (start == 0 && end >= 0) {
79+ updateActionState();
80+ }
81+}
82+
83+void RootActionState::onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
84+{
85+ Q_UNUSED(roles);
86+ if (!topLeft.isValid() || !bottomRight.isValid()) {
87+ return;
88+ }
89+
90+ if (topLeft.row() <= 0 && bottomRight.row() >= 0) {
91+ updateActionState();
92+ }
93+}
94+
95+void RootActionState::updateActionState()
96+{
97+ if (m_menu && m_menu->rowCount() > 0) {
98+ m_cachedState = m_menu->get(0, "actionState").toMap();
99+ } else {
100+ m_cachedState.clear();
101+ }
102+ Q_EMIT updated();
103+}
104+
105+
106+bool RootActionState::isValid() const
107+{
108+ return m_menu && m_menu->rowCount() > 0;
109+}
110+
111+QString RootActionState::label() const
112+{
113+ if (!isValid()) return QString();
114+
115+ return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
116+}
117+
118+QString RootActionState::icon() const
119+{
120+ if (!isValid()) return QString();
121+
122+ return m_cachedState.value("icon", QVariant::fromValue(QString())).toString();
123+}
124+
125+QString RootActionState::accessibleName() const
126+{
127+ if (!isValid()) return QString();
128+
129+ return m_cachedState.value("accessible-name", QVariant::fromValue(QString())).toString();
130+}
131+
132+bool RootActionState::isVisible() const
133+{
134+ if (!isValid()) return false;
135+
136+ return m_cachedState.value("visible", QVariant::fromValue(true)).toBool();
137+}
138+
139+static QString iconUri(GIcon *icon)
140+{
141+ QString uri;
142+
143+ if (G_IS_THEMED_ICON (icon)) {
144+
145+ const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon));
146+ guint index = 0;
147+ while(iconNames[index] != NULL) {
148+ if (QIcon::hasThemeIcon(iconNames[index])) {
149+ uri = QString("image://theme/%1").arg(iconNames[index]);
150+ break;
151+ }
152+ index++;
153+ }
154+ }
155+ else if (G_IS_FILE_ICON (icon)) {
156+ GFile *file;
157+
158+ file = g_file_icon_get_file (G_FILE_ICON (icon));
159+ if (g_file_is_native (file)) {
160+ gchar *fileuri;
161+
162+ fileuri = g_file_get_path (file);
163+ uri = QString(fileuri);
164+
165+ g_free (fileuri);
166+ }
167+ }
168+ else if (G_IS_BYTES_ICON (icon)) {
169+ gsize size;
170+ gconstpointer data;
171+ gchar *base64;
172+
173+ data = g_bytes_get_data (g_bytes_icon_get_bytes (G_BYTES_ICON (icon)), &size);
174+ base64 = g_base64_encode ((const guchar *) data, size);
175+
176+ uri = QString("data://");
177+ uri.append (base64);
178+
179+ g_free (base64);
180+ }
181+
182+ return uri;
183+}
184+
185+QVariant RootActionState::toQVariant(GVariant* state) const
186+{
187+ if (!state) {
188+ return QVariant();
189+ }
190+
191+ if (g_variant_is_of_type(state, G_VARIANT_TYPE_VARDICT)) {
192+ GVariantIter iter;
193+ GVariant *vvalue;
194+ gchar *key;
195+ QVariantMap qmap;
196+
197+ g_variant_iter_init (&iter, state);
198+ while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
199+ {
200+ QString str = QString::fromUtf8(key);
201+ if (str == "icon") {
202+ // FIXME - should be sending a url.
203+ GIcon *icon = g_icon_deserialize (vvalue);
204+ if (icon) {
205+ qmap.insert(str, iconUri(icon));
206+ g_object_unref (icon);
207+ }
208+ else {
209+ qmap.insert(str, QVariant(""));
210+ }
211+ } else {
212+ qmap.insert(str, ActionStateParser::toQVariant(vvalue));
213+ }
214+ }
215+
216+ return QVariant::fromValue(qmap);
217+
218+ } else if (g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)"))) {
219+ QVariantMap qmap;
220+
221+ char* label;
222+ char* icon;
223+ char* accessible_name;
224+ gboolean visible;
225+ GIcon *gicon;
226+
227+ g_variant_get(state, "(sssb)", &label,
228+ &icon,
229+ &accessible_name,
230+ &visible);
231+
232+ qmap["label"] = label ? QString::fromUtf8(label) : "";
233+ qmap["accessible-name"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
234+ qmap["visible"] = visible;
235+
236+ gicon = g_icon_new_for_string (icon, NULL);
237+ if (gicon) {
238+ qmap["icon"] = iconUri(gicon);
239+ g_object_unref (gicon);
240+ }
241+
242+ if (label) g_free(label);
243+ if (icon) g_free(icon);
244+ if (accessible_name) g_free(accessible_name);
245+
246+ return QVariant::fromValue(qmap);
247+ }
248+ return ActionStateParser::toQVariant(state);
249+}
plugins/Unity/Indicators/rootactionstate.hnew file+82
@@ -0,0 +1,82 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef ROOTACTIONSTATE_H
21+#define ROOTACTIONSTATE_H
22+
23+#include "unityindicatorsglobal.h"
24+
25+#include <actionstateparser.h>
26+
27+class UnityMenuModel;
28+
29+class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
30+{
31+ Q_OBJECT
32+ Q_PROPERTY(UnityMenuModel* menu READ menu WRITE setMenu NOTIFY menuChanged)
33+
34+ Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
35+ Q_PROPERTY(QString label READ label NOTIFY labelChanged)
36+ Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
37+ Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
38+ Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
39+public:
40+ RootActionState(QObject *parent = 0);
41+ virtual ~RootActionState();
42+
43+ UnityMenuModel* menu() const;
44+ void setMenu(UnityMenuModel* menu);
45+
46+ int index() const;
47+ void setIndex(int index);
48+
49+ bool isValid() const;
50+ QString label() const;
51+ QString icon() const;
52+ QString accessibleName() const;
53+ bool isVisible() const;
54+
55+ // from ActionStateParser
56+ virtual QVariant toQVariant(GVariant* state) const;
57+
58+Q_SIGNALS:
59+ void updated();
60+
61+ void menuChanged();
62+ void indexChanged();
63+
64+ void validChanged();
65+ void labelChanged();
66+ void iconChanged();
67+ void accessibleNameChanged();
68+ void visibleChanged();
69+
70+private Q_SLOTS:
71+ void onModelRowsAdded(const QModelIndex& parent, int start, int end);
72+ void onModelRowsRemoved(const QModelIndex& parent, int start, int end);
73+ void onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>&);
74+
75+private:
76+ void updateActionState();
77+
78+ UnityMenuModel* m_menu;
79+ QVariantMap m_cachedState;
80+};
81+
82+#endif // ROOTACTIONSTATE_H
plugins/Unity/Indicators/unitymenumodelcache.cppnew file+42
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "unitymenumodelcache.h"
21+#include <unitymenumodel.h>
22+
23+UnityMenuModelCache::UnityMenuModelCache(QObject* parent)
24+ : QObject(parent)
25+{
26+}
27+
28+UnityMenuModelCache::~UnityMenuModelCache()
29+{
30+}
31+
32+
33+UnityMenuModel* UnityMenuModelCache::model(const QString& path) const
34+{
35+ return m_menuModels.value(path, NULL);
36+}
37+
38+void UnityMenuModelCache::registerModel(const QString& path, UnityMenuModel* menuModel)
39+{
40+ menuModel->setParent(this);
41+ m_menuModels[path] = menuModel;
42+}
plugins/Unity/Indicators/unitymenumodelcache.hnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef UNITYMENUMODELCACHE_H
21+#define UNITYMENUMODELCACHE_H
22+
23+#include "unityindicatorsglobal.h"
24+
25+#include <QObject>
26+#include <QHash>
27+
28+class UnityMenuModel;
29+
30+class UNITYINDICATORS_EXPORT UnityMenuModelCache : public QObject
31+{
32+ Q_OBJECT
33+public:
34+ UnityMenuModelCache(QObject*parent=NULL);
35+ ~UnityMenuModelCache();
36+
37+ Q_INVOKABLE UnityMenuModel* model(const QString& path) const;
38+ Q_INVOKABLE void registerModel(const QString& path, UnityMenuModel* menuModel);
39+
40+private:
41+ QHash<QString, UnityMenuModel*> m_menuModels;
42+};
43+
44+#endif // UNITYMENUMODELCACHE_H
plugins/Unity/Indicators/unitymenumodelstack.cppnew file+69
@@ -0,0 +1,69 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#include "unitymenumodelstack.h"
21+
22+UnityMenuModelStack::UnityMenuModelStack(QObject* parent)
23+ : QObject(parent)
24+{
25+}
26+
27+UnityMenuModelStack::~UnityMenuModelStack()
28+{
29+}
30+
31+UnityMenuModel* UnityMenuModelStack::head() const
32+{
33+ return !m_menuModels.isEmpty() ? m_menuModels.first() : NULL;
34+}
35+
36+void UnityMenuModelStack::setHead(UnityMenuModel* model)
37+{
38+ if (head() != model) {
39+ m_menuModels.clear();
40+ push(model);
41+ Q_EMIT headChanged(model);
42+ }
43+}
44+
45+UnityMenuModel* UnityMenuModelStack::tail() const
46+{
47+ return !m_menuModels.isEmpty() ? m_menuModels.last() : NULL;
48+}
49+
50+void UnityMenuModelStack::push(UnityMenuModel* model)
51+{
52+ m_menuModels << model;
53+ Q_EMIT tailChanged(model);
54+}
55+
56+UnityMenuModel* UnityMenuModelStack::pop()
57+{
58+ if (m_menuModels.isEmpty()) {
59+ return NULL;
60+ }
61+ UnityMenuModel* model = m_menuModels.takeLast();
62+
63+ Q_EMIT tailChanged(tail());
64+ if (m_menuModels.isEmpty()) {
65+ Q_EMIT headChanged(NULL);
66+ }
67+
68+ return model;
69+}
plugins/Unity/Indicators/unitymenumodelstack.hnew file+60
@@ -0,0 +1,60 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef UNITYMENUMODELSTACK_H
21+#define UNITYMENUMODELSTACK_H
22+
23+#include "unityindicatorsglobal.h"
24+
25+#include <QObject>
26+#include <QList>
27+
28+class UnityMenuModel;
29+
30+// A LIFO queue for storing the current submenu of a UnityMenuModel.
31+// The root menu model is set as the head, and each subsiquent submenu that is
32+// opened can be pushed onto the queue.
33+// The tail is set to the last item on the queue
34+// Popping the queue will remove the last entry, and the tail be updated to the last item.
35+class UNITYINDICATORS_EXPORT UnityMenuModelStack : public QObject
36+{
37+ Q_OBJECT
38+ Q_PROPERTY(UnityMenuModel* head READ head WRITE setHead NOTIFY headChanged)
39+ Q_PROPERTY(UnityMenuModel* tail READ tail NOTIFY tailChanged)
40+public:
41+ UnityMenuModelStack(QObject*parent=NULL);
42+ ~UnityMenuModelStack();
43+
44+ UnityMenuModel* head() const;
45+ void setHead(UnityMenuModel* model);
46+
47+ UnityMenuModel* tail() const;
48+
49+ Q_INVOKABLE void push(UnityMenuModel* model);
50+ Q_INVOKABLE UnityMenuModel* pop();
51+
52+Q_SIGNALS:
53+ void headChanged(UnityMenuModel* head);
54+ void tailChanged(UnityMenuModel* tail);
55+
56+private:
57+ QList<UnityMenuModel*> m_menuModels;
58+};
59+
60+#endif // UNITYMENUMODELSTACK_H
plugins/Unity/IndicatorsLegacy/CMakeLists.txtnew file+49
@@ -0,0 +1,49 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+project(IndicatorsLegacyQml)
5+
6+pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
7+find_package(Qt5Core REQUIRED)
8+find_package(Qt5Quick REQUIRED)
9+
10+add_subdirectory(qml)
11+add_subdirectory(Messaging)
12+add_subdirectory(Network)
13+
14+include_directories(
15+ ${CMAKE_CURRENT_SOURCE_DIR}
16+ ${CMAKE_CURRENT_BINARY_DIR}
17+ ${QMENUMODEL_INCLUDE_DIRS}
18+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators
19+)
20+
21+set(IndicatorsLegacyQML_SOURCES
22+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
23+ flatmenuproxymodel.cpp
24+ modelprinter.cpp
25+ plugin.cpp
26+)
27+add_definitions(-DUNITYINDICATORS_LIBRARY)
28+
29+set(IndicatorsLegacyQML_RESOURCES
30+ resources/indicators.qrc
31+)
32+
33+qt5_add_resources(IndicatorsLegacyQML_RESOURCES_RCC
34+ ${IndicatorsLegacyQML_RESOURCES}
35+)
36+
37+add_library(IndicatorsLegacyQml SHARED
38+ ${IndicatorsLegacyQML_RESOURCES_RCC}
39+ ${IndicatorsLegacyQML_SOURCES}
40+)
41+
42+target_link_libraries(IndicatorsLegacyQml
43+ ${QMENUMODEL_LDFLAGS}
44+)
45+
46+qt5_use_modules(IndicatorsLegacyQml Core Qml Quick)
47+
48+# export the qmltypes and plugin files
49+export_qmlplugin(Unity.IndicatorsLegacy 0.1 Unity/IndicatorsLegacy TARGETS IndicatorsLegacyQml)
plugins/Unity/Indicators/Messaging/CMakeLists.txt → plugins/Unity/IndicatorsLegacy/Messaging/CMakeLists.txtrenamed

The content is the same.

plugins/Unity/Indicators/Messaging/qml/ActionButton.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/ActionButton.qmlrenamed+1−1
@@ -20,7 +20,7 @@
2020
2121import QtQuick 2.0
2222import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
2424
2525Button {
2626 id: button
plugins/Unity/Indicators/Messaging/qml/ActionTextField.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/ActionTextField.qmlrenamed

The content is the same.

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

The content is the same.

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

The content is the same.

plugins/Unity/Indicators/Messaging/qml/SnapDecision.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/SnapDecision.qmlrenamed+1−1
@@ -20,7 +20,7 @@
2020
2121import QtQuick 2.0
2222import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
2424import "utils.js" as Utils
2525
2626HeroMessage {
plugins/Unity/Indicators/Messaging/qml/TextMessage.qml → plugins/Unity/IndicatorsLegacy/Messaging/qml/TextMessage.qmlrenamed+1−1
@@ -20,7 +20,7 @@
2020
2121import QtQuick 2.0
2222import Ubuntu.Components 0.1
23−import Unity.Indicators 0.1 as Indicators
23+import Unity.IndicatorsLegacy 0.1 as Indicators
2424
2525SimpleTextMessage {
2626 id: simpleMessage
plugins/Unity/Indicators/Messaging/qml/qmldir → plugins/Unity/IndicatorsLegacy/Messaging/qml/qmldirrenamed+1−1
@@ -1,4 +1,4 @@
1−module Unity.Indicators.Messaging
1+module Unity.IndicatorsLegacy.Messaging
22
33ActionButton 0.1 ActionButton.qml
44ActionTextField 0.1 ActionTextField.qml
plugins/Unity/Indicators/Messaging/qml/utils.js → plugins/Unity/IndicatorsLegacy/Messaging/qml/utils.jsrenamed

The content is the same.

plugins/Unity/IndicatorsLegacy/Network/CMakeLists.txtnew file+45
@@ -0,0 +1,45 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+project(IndicatorsLegacyNetworkQml)
5+
6+pkg_check_modules(GLIB glib-2.0>=2.32)
7+pkg_check_modules(LIBNM_GLIB libnm-glib>=0.9.6)
8+pkg_check_modules(LIBNM_UTIL libnm-util>=0.9.6)
9+
10+if(LIBNM_GLIB_FOUND AND LIBNM_UTIL_FOUND AND GLIB_FOUND)
11+ set(INDICATORS_LEGACY_NETWORK_SRC
12+ networkagent.cpp
13+ plugin.cpp
14+ secret-agent.c
15+ )
16+
17+ add_library(IndicatorsLegacyNetworkQml MODULE
18+ ${INDICATORS_LEGACY_NETWORK_SRC}
19+ )
20+
21+ qt5_use_modules(IndicatorsLegacyNetworkQml Core Qml Widgets)
22+
23+ set_target_properties(IndicatorsLegacyNetworkQml PROPERTIES COMPILE_FLAGS -fPIC)
24+
25+ include_directories(
26+ ${CMAKE_CURRENT_SOURCE_DIR}
27+ ${GLIB_INCLUDE_DIRS}
28+ ${LIBNM_GLIB_INCLUDE_DIRS}
29+ ${LIBNM_UTIL_INCLUDE_DIRS}
30+ )
31+
32+ target_link_libraries(IndicatorsLegacyNetworkQml
33+ ${GLIB_LIBRARIES}
34+ ${LIBNM_GLIB_LIBRARIES}
35+ ${LIBNM_UTIL_LIBRARIES}
36+ )
37+
38+ # export the qmltypes and plugin files
39+ export_qmlplugin(Unity.IndicatorsLegacy.Network 0.1 Unity/IndicatorsLegacy/Network TARGETS IndicatorsLegacyNetworkQml)
40+
41+else()
42+ message(WARNING "Missing glib or libnm dependency for the network plugin, not building")
43+endif()
44+
45+add_subdirectory(qml)
plugins/Unity/IndicatorsLegacy/Network/networkagent.cppnew file+146
@@ -0,0 +1,146 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#include "networkagent.h"
22+
23+#include <QVariant>
24+#include <QApplication>
25+#include <QDebug>
26+
27+class NetworkAgentToken
28+{
29+public:
30+ NMConnection *connection;
31+ NMSettingWirelessSecurity *wisec;
32+ guint id;
33+ QByteArray keyMgmt;
34+
35+ NetworkAgentToken(NMConnection *connection,
36+ NMSettingWirelessSecurity *wisec,
37+ guint id,
38+ QByteArray keyMgmt)
39+ : connection(connection),
40+ wisec(wisec),
41+ id(id),
42+ keyMgmt(keyMgmt)
43+ {
44+ if (connection) {
45+ g_object_ref(connection);
46+ } else {
47+ qWarning() << "invalid connection object";
48+ }
49+ }
50+
51+ ~NetworkAgentToken()
52+ {
53+ if (connection) {
54+ g_object_unref(connection);
55+ }
56+ }
57+};
58+
59+NetworkAgent::NetworkAgent(QObject *parent)
60+ : QObject(parent)
61+{
62+ m_agent = unity_settings_secret_agent_new();
63+ g_signal_connect(G_OBJECT(m_agent),
64+ UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
65+ G_CALLBACK(onSecretRequested),
66+ this);
67+
68+ g_signal_connect(G_OBJECT(m_agent),
69+ UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
70+ G_CALLBACK(onSecretRequestCancelled),
71+ this);
72+}
73+
74+NetworkAgent::~NetworkAgent()
75+{
76+ nm_secret_agent_unregister(NM_SECRET_AGENT(m_agent));
77+ g_object_unref(m_agent);
78+}
79+
80+void NetworkAgent::authenticate(const QVariant &token, const QString &key)
81+{
82+ NetworkAgentToken *pToken = (NetworkAgentToken *) token.value<void *>();
83+ if (pToken == NULL) {
84+ return;
85+ }
86+
87+ if ((pToken->keyMgmt == "wpa-none") || (pToken->keyMgmt == "wpa-psk")) {
88+ g_object_set(G_OBJECT(pToken->wisec),
89+ NM_SETTING_WIRELESS_SECURITY_PSK, qPrintable(key),
90+ NULL);
91+ } else if (pToken->keyMgmt == "none") {
92+ g_object_set(G_OBJECT(pToken->wisec),
93+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, qPrintable(key),
94+ NULL);
95+ }
96+
97+ GHashTable *settings = nm_connection_to_hash(pToken->connection,
98+ NM_SETTING_HASH_FLAG_ALL);
99+
100+ unity_settings_secret_agent_provide_secret(m_agent,
101+ pToken->id, settings);
102+ g_hash_table_unref(settings);
103+ delete pToken;
104+}
105+
106+void NetworkAgent::cancel(const QVariant &token)
107+{
108+ NetworkAgentToken *pToken = (NetworkAgentToken *) token.value<void *>();
109+ if (pToken != NULL) {
110+ unity_settings_secret_agent_cancel_request(m_agent, pToken->id);
111+ delete pToken;
112+ }
113+}
114+
115+void NetworkAgent::onSecretRequested(UnitySettingsSecretAgent* /*agent*/,
116+ guint id,
117+ NMConnection* connection,
118+ const char* /*setting_name*/,
119+ const char** /*hints*/,
120+ NMSecretAgentGetSecretsFlags /*flags*/,
121+ NetworkAgent* self)
122+{
123+ NetworkAgentToken *token = new NetworkAgentToken(connection,
124+ NULL,
125+ id,
126+ NULL);
127+
128+ token->wisec = nm_connection_get_setting_wireless_security(connection);
129+ if (token->wisec) {
130+ token->keyMgmt = nm_setting_wireless_security_get_key_mgmt(token->wisec);
131+ }
132+
133+ QVariant varToken = qVariantFromValue((void *) token);
134+ Q_EMIT self->secretRequested(varToken);
135+}
136+
137+void NetworkAgent::onSecretRequestCancelled(UnitySettingsSecretAgent* /*agent*/,
138+ guint /*id*/,
139+ NMConnection* /*connection*/,
140+ const char* /*setting_name*/,
141+ const char** /*hints*/,
142+ NMSecretAgentGetSecretsFlags /*flags*/,
143+ NetworkAgent* /*self*/)
144+{
145+ //Do nothing for now
146+}
plugins/Unity/IndicatorsLegacy/Network/networkagent.hnew file+65
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#ifndef NETWORKAGENT_H
22+#define NETWORKAGENT_H
23+
24+#include "secret-agent.h"
25+
26+#include <QObject>
27+
28+class NetworkAgentToken;
29+
30+class NetworkAgent : public QObject
31+{
32+ Q_OBJECT
33+public:
34+ NetworkAgent(QObject *parent=0);
35+ ~NetworkAgent();
36+
37+ Q_INVOKABLE void authenticate(const QVariant &token, const QString &key);
38+ Q_INVOKABLE void cancel(const QVariant &token);
39+
40+Q_SIGNALS:
41+ void secretRequested(const QVariant &token);
42+ void secretRequestCancelled();
43+
44+private:
45+ UnitySettingsSecretAgent *m_agent;
46+
47+ static void onSecretRequested(UnitySettingsSecretAgent *agent,
48+ guint id,
49+ NMConnection *connection,
50+ const char *setting_name,
51+ const char **hints,
52+ NMSecretAgentGetSecretsFlags flags,
53+ NetworkAgent *self);
54+
55+ static void onSecretRequestCancelled(UnitySettingsSecretAgent *agent,
56+ guint id,
57+ NMConnection *connection,
58+ const char *setting_name,
59+ const char **hints,
60+ NMSecretAgentGetSecretsFlags flags,
61+ NetworkAgent *self);
62+
63+};
64+
65+#endif
plugins/Unity/IndicatorsLegacy/Network/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2012 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+// Qt
20+#include <QtQml/qqml.h>
21+
22+// self
23+#include "plugin.h"
24+
25+// local
26+#include "networkagent.h"
27+
28+void IndicatorsNetworkPlugin::registerTypes(const char *uri)
29+{
30+ qmlRegisterType<NetworkAgent>(uri, 0, 1, "NetworkAgent");
31+}
plugins/Unity/IndicatorsLegacy/Network/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2012 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Nick Dedekind <nick.dedekind@canonical.com>
17+ */
18+
19+#ifndef INDICATORS_NETWORK_PLUGIN_H
20+#define INDICATORS_NETWORK_PLUGIN_H
21+
22+#include <QtQml/QQmlExtensionPlugin>
23+
24+class IndicatorsNetworkPlugin : public QQmlExtensionPlugin
25+{
26+ Q_OBJECT
27+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28+public:
29+ void registerTypes(const char *uri);
30+};
31+
32+#endif // INDICATORS_NETWORK_PLUGIN_H
plugins/Unity/IndicatorsLegacy/Network/qml/AccessPoint.qmlnew file+68
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+import Ubuntu.Components.ListItems 0.1 as ListItem
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24+
25+Indicators.MenuItem {
26+ id: accessPoint
27+ property variant actionWifiApStrength : menu && actionGroup ? actionGroup.action(menu.extra.canonical_wifi_ap_strength_action) : null
28+ property variant wifiApStrength : actionWifiApStrength && actionWifiApStrength.valid ? actionWifiApStrength.state : "0"
29+
30+ function getNetworkIcon(data) {
31+ var imageName = "nm-signal-100"
32+ var signalStrength = parseInt(wifiApStrength)
33+
34+ if (data.extra.canonical_wifi_ap_is_adhoc) {
35+ imageName = "nm-adhoc";
36+ } else if (signalStrength == 0) {
37+ imageName = "nm-signal-00";
38+ } else if (signalStrength <= 25) {
39+ imageName = "nm-signal-25";
40+ } else if (signalStrength <= 50) {
41+ imageName = "nm-signal-50";
42+ } else if (signalStrength <= 75) {
43+ imageName = "nm-signal-75";
44+ }
45+
46+ if (data.extra.canonical_wifi_ap_is_secure) {
47+ imageName += "-secure";
48+ }
49+
50+ return "image://gicon/" + imageName;
51+ }
52+
53+ icon: menu && wifiApStrength ? getNetworkIcon(menu) : "image://gicon/wifi-none"
54+ iconFrame: false
55+ control: CheckBox {
56+ id: checkBoxActive
57+ height: units.gu(4)
58+ width: units.gu(4)
59+ anchors.centerIn: parent
60+ }
61+
62+ Indicators.MenuActionBinding {
63+ actionGroup: accessPoint.actionGroup
64+ action: menu ? menu.action : ""
65+ target: checkBoxActive
66+ property: "checked"
67+ }
68+}
plugins/Unity/IndicatorsLegacy/Network/qml/CMakeLists.txtnew file+5
@@ -0,0 +1,5 @@
1+# export_qmlplugin macro
2+include(Plugins)
3+
4+# export the qmldir, *.qml files.
5+export_qmlfiles(Unity.IndicatorsLegacy.Network Unity/IndicatorsLegacy/Network)
plugins/Unity/IndicatorsLegacy/Network/qml/PasswordPage.qmlnew file+88
@@ -0,0 +1,88 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+import Ubuntu.Components.ListItems 0.1 as ListItem
23+import Unity.IndicatorsLegacy 0.1 as Indicators
24+
25+Page {
26+ id: pagePassword
27+
28+ property variant agent
29+ property variant token
30+
31+ anchors.fill: parent
32+ title: "Network Authentication"
33+
34+ Column {
35+ anchors {
36+ left: parent.left
37+ right: parent.right
38+ }
39+
40+ Indicators.SectionMenuItem {
41+ label: "Authentication"
42+ }
43+
44+ Indicators.MenuItem {
45+ implicitHeight: password.height + units.gu(1)
46+
47+ PasswordTextField {
48+ id: password
49+ anchors {
50+ left: parent.left
51+ right: parent.right
52+ margins: units.gu(3)
53+ verticalCenter: parent.verticalCenter
54+ }
55+ }
56+ }
57+
58+ Indicators.MenuItem {
59+ Row {
60+ anchors {
61+ right: parent.right
62+ verticalCenter: parent.verticalCenter
63+ margins: units.gu(3)
64+ }
65+
66+ spacing: units.gu(1)
67+
68+ Button {
69+ text: "Cancel"
70+ width: units.gu(10)
71+ onClicked: {
72+ agent.cancel(token);
73+ pageStack.pop();
74+ }
75+ }
76+
77+ Button {
78+ text: "OK"
79+ width: units.gu(10)
80+ onClicked: {
81+ agent.authenticate(token, password.text);
82+ pageStack.pop();
83+ }
84+ }
85+ }
86+ }
87+ }
88+}
plugins/Unity/IndicatorsLegacy/Network/qml/PasswordTextField.qmlnew file+68
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ */
19+
20+import QtQuick 2.0
21+import Ubuntu.Components 0.1
22+
23+Item {
24+ id: textMenu
25+
26+ property alias text: textField.text
27+
28+ height: contentColumn.height
29+
30+ Column {
31+ id: contentColumn
32+ spacing: units.gu(0.5)
33+ anchors {
34+ left: parent.left
35+ right: parent.right
36+ }
37+
38+ TextField {
39+ id: textField
40+
41+ anchors {
42+ left: parent.left
43+ right: parent.right
44+ }
45+
46+ placeholderText: "Password"
47+ echoMode: showPassword.checked ? TextInput.Normal : TextInput.Password
48+ }
49+
50+ Row {
51+ anchors {
52+ left: parent.left
53+ right: parent.right
54+ }
55+
56+ spacing: units.gu(1)
57+
58+ CheckBox {
59+ id: showPassword
60+ }
61+
62+ Label {
63+ text: "Show password"
64+ anchors.verticalCenter: showPassword.verticalCenter
65+ }
66+ }
67+ }
68+}
plugins/Unity/Indicators/Network/qml/WifiSection.qml → plugins/Unity/IndicatorsLegacy/Network/qml/WifiSection.qmlrenamed+1−1
@@ -19,7 +19,7 @@
1919
2020import QtQuick 2.0
2121import Ubuntu.Components 0.1
22−import Unity.Indicators 0.1 as Indicators
22+import Unity.IndicatorsLegacy 0.1 as Indicators
2323
2424Indicators.SectionMenuItem {
2525 id: wifiSectionMenu
plugins/Unity/IndicatorsLegacy/Network/qml/qmldirnew file+9
@@ -0,0 +1,9 @@
1+module Unity.IndicatorsLegacy.Network
2+plugin IndicatorsLegacyNetworkQml
3+typeinfo plugin.qmltypes
4+
5+NetworkPage 0.1 NetworkPage.qml
6+AccessPoint 0.1 AccessPoint.qml
7+PasswordPage 0.1 PasswordPage.qml
8+PasswordTextField 0.1 PasswordTextField.qml
9+WifiSection 0.1 WifiSection.qml
plugins/Unity/IndicatorsLegacy/Network/secret-agent.cnew file+382
@@ -0,0 +1,382 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+
22+#include <glib.h>
23+#include <glib-object.h>
24+#include <nm-secret-agent.h>
25+#include "secret-agent.h"
26+
27+#define UNITY_SETTINGS_TYPE_SECRET_AGENT (unity_settings_secret_agent_get_type ())
28+#define UNITY_SETTINGS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgent))
29+#define UNITY_SETTINGS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
30+#define UNITY_SETTINGS_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT))
31+#define UNITY_SETTINGS_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT))
32+#define UNITY_SETTINGS_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
33+#define UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentPrivate))
34+
35+#define AGENT_ID "com.canonical.settings.network.nm-agent"
36+
37+static gpointer unity_settings_secret_agent_parent_class = NULL;
38+
39+typedef struct _UnitySettingsSecretAgentPrivate UnitySettingsSecretAgentPrivate;
40+
41+struct _UnitySettingsSecretAgentPrivate {
42+ GQueue *requests;
43+};
44+
45+typedef struct _SecretRequest {
46+ gint id;
47+ NMSecretAgent *agent;
48+ NMConnection *connection;
49+ const char *connection_path;
50+ const char *setting_name;
51+ const char **hints;
52+ NMSecretAgentGetSecretsFlags flags;
53+ NMSecretAgentGetSecretsFunc callback;
54+ gpointer callback_data;
55+} SecretRequest;
56+
57+GType unity_settings_secret_agent_get_type (void) G_GNUC_CONST;
58+enum {
59+ UNITY_SETTINGS_SECRET_AGENT_DUMMY_PROPERTY
60+};
61+
62+enum {
63+ SECRET_REQUESTED,
64+ REQUEST_CANCELLED,
65+ LAST_SIGNAL
66+};
67+
68+static guint signals[LAST_SIGNAL] = { 0 };
69+
70+UnitySettingsSecretAgent* unity_settings_secret_agent_new (void);
71+UnitySettingsSecretAgent* unity_settings_secret_agent_construct (GType object_type);
72+
73+int
74+secret_request_find (SecretRequest *req,
75+ guint *id)
76+{
77+ if (req->id > *id)
78+ return -1;
79+
80+ if (req->id < *id)
81+ return 1;
82+
83+ return 0;
84+}
85+
86+void
87+unity_settings_secret_agent_provide_secret (UnitySettingsSecretAgent *agent,
88+ guint request,
89+ GHashTable *secrets)
90+{
91+ GList *iter;
92+ SecretRequest *req;
93+ UnitySettingsSecretAgentPrivate *priv = agent->priv;
94+
95+ iter = g_queue_find_custom (priv->requests,
96+ &request,
97+ (GCompareFunc)secret_request_find);
98+
99+ if (iter == NULL || iter->data == NULL)
100+ {
101+ g_warning ("Secret request with id <%d> was not found", (int)request);
102+ return;
103+ }
104+
105+ req = iter->data;
106+
107+ req->callback (NM_SECRET_AGENT (agent),
108+ req->connection,
109+ secrets,
110+ NULL,
111+ req->callback_data);
112+
113+ g_queue_remove_all (priv->requests, req);
114+ g_free (req);
115+ return;
116+}
117+
118+void
119+free_request (SecretRequest *req)
120+{
121+ g_object_unref (req->connection);
122+ g_free (req);
123+}
124+
125+void
126+unity_settings_secret_agent_cancel_request (UnitySettingsSecretAgent *agent,
127+ guint request)
128+{
129+ GList *iter;
130+ SecretRequest *req;
131+ UnitySettingsSecretAgentPrivate *priv = agent->priv;
132+ GError *error;
133+
134+ iter = g_queue_find_custom (priv->requests,
135+ &request,
136+ (GCompareFunc)secret_request_find);
137+
138+ if (iter == NULL || iter->data == NULL)
139+ {
140+ g_warning ("Secret request with id <%d> was not found", (int)request);
141+ return;
142+ }
143+
144+ req = iter->data;
145+ error = g_error_new (NM_SECRET_AGENT_ERROR,
146+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
147+ "This secret request was canceled by the user.");
148+
149+ req->callback (NM_SECRET_AGENT (agent),
150+ req->connection,
151+ NULL,
152+ error,
153+ req->callback_data);
154+
155+ g_queue_remove_all (priv->requests, req);
156+ free_request (req);
157+ return;
158+}
159+
160+static void
161+delete_secrets (NMSecretAgent *agent,
162+ NMConnection *connection,
163+ const char *connection_path,
164+ NMSecretAgentDeleteSecretsFunc callback,
165+ gpointer callback_data)
166+{
167+ g_debug ("delete secrets");
168+}
169+
170+/* If it returns G_MAXUINT it's considered an error */
171+static guint
172+find_available_id (UnitySettingsSecretAgentPrivate *priv)
173+{
174+ guint i = 0;
175+ guint candidate = 0;
176+
177+ if (g_queue_get_length (priv->requests) == G_MAXUINT)
178+ return G_MAXUINT;
179+
180+ while (i < g_queue_get_length (priv->requests))
181+ {
182+ SecretRequest *req = (SecretRequest*)g_queue_peek_nth (priv->requests, i);
183+
184+ if (req->id == candidate)
185+ {
186+ candidate++;
187+ i = 0;
188+ }
189+ else
190+ {
191+ i++;
192+ }
193+ }
194+
195+ return i;
196+}
197+
198+static void
199+get_secrets (NMSecretAgent *agent,
200+ NMConnection *connection,
201+ const char *connection_path,
202+ const char *setting_name,
203+ const char **hints,
204+ NMSecretAgentGetSecretsFlags flags,
205+ NMSecretAgentGetSecretsFunc callback,
206+ gpointer callback_data)
207+{
208+ guint id;
209+ UnitySettingsSecretAgentPrivate *priv = UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE (agent);
210+ SecretRequest *req = NULL;
211+
212+ if (flags == NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE)
213+ {
214+ GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
215+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
216+ "No password found for this connection.");
217+ callback (agent, connection, NULL, error, callback_data);
218+ g_error_free (error);
219+ return;
220+ }
221+
222+ id = find_available_id (priv);
223+ if (id == G_MAXUINT)
224+ {
225+ GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
226+ NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
227+ "Reached maximum number of requests.");
228+ callback (agent, connection, NULL, error, callback_data);
229+ g_error_free (error);
230+ return;
231+ }
232+
233+ /* Adding a request */
234+ req = (SecretRequest*) g_malloc0 (sizeof (SecretRequest));
235+ *req = ((SecretRequest)
236+ { id,
237+ agent,
238+ connection,
239+ connection_path,
240+ setting_name,
241+ hints,
242+ flags,
243+ callback,
244+ callback_data });
245+
246+ g_object_ref (connection);
247+
248+ g_queue_push_tail (priv->requests, req);
249+
250+ g_signal_emit_by_name (agent,
251+ UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
252+ id,
253+ connection,
254+ setting_name,
255+ hints,
256+ flags);
257+}
258+
259+static void
260+save_secrets (NMSecretAgent *agent,
261+ NMConnection *connection,
262+ const char *connection_path,
263+ NMSecretAgentSaveSecretsFunc callback,
264+ gpointer callback_data)
265+{
266+ g_debug ("save secrets");
267+}
268+
269+static void
270+cancel_get_secrets (NMSecretAgent *agent,
271+ const char *connection_path,
272+ const char *setting_name)
273+{
274+ g_debug ("cancel get secrets");
275+}
276+
277+UnitySettingsSecretAgent*
278+unity_settings_secret_agent_construct (GType object_type)
279+{
280+ UnitySettingsSecretAgent * self = NULL;
281+ self = (UnitySettingsSecretAgent*) g_object_new (object_type,
282+ NM_SECRET_AGENT_IDENTIFIER, AGENT_ID,
283+ NULL);
284+ return self;
285+}
286+
287+
288+UnitySettingsSecretAgent*
289+unity_settings_secret_agent_new (void)
290+{
291+ return unity_settings_secret_agent_construct (UNITY_SETTINGS_TYPE_SECRET_AGENT);
292+}
293+
294+static void
295+destroy_pending_request (gpointer data)
296+{
297+ SecretRequest* req = (SecretRequest*)data;
298+ /* Reporting the cancellation of all pending requests */
299+ g_signal_emit_by_name (req->agent,
300+ UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
301+ req->id);
302+
303+ free_request (req);
304+}
305+
306+static void
307+unity_settings_secret_agent_finalize (GObject *agent)
308+{
309+ UnitySettingsSecretAgentPrivate *priv = UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE (agent);
310+
311+ g_queue_free_full (priv->requests, destroy_pending_request);
312+}
313+
314+static void
315+unity_settings_secret_agent_class_init (UnitySettingsSecretAgentClass *klass)
316+{
317+ unity_settings_secret_agent_parent_class = g_type_class_peek_parent (klass);
318+ NMSecretAgentClass *parent_class = NM_SECRET_AGENT_CLASS (klass);
319+ parent_class->get_secrets = get_secrets;
320+ parent_class->save_secrets = save_secrets;
321+ parent_class->delete_secrets = delete_secrets;
322+ parent_class->cancel_get_secrets = cancel_get_secrets;
323+
324+ g_type_class_add_private (klass, sizeof(UnitySettingsSecretAgentPrivate));
325+ G_OBJECT_CLASS (klass)->finalize = unity_settings_secret_agent_finalize;
326+
327+
328+ signals[SECRET_REQUESTED] = g_signal_new (UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED,
329+ G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
330+ G_SIGNAL_RUN_FIRST,
331+ G_STRUCT_OFFSET (UnitySettingsSecretAgentClass, secret_requested),
332+ NULL, NULL, NULL,
333+ G_TYPE_NONE, 5,
334+ G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT);
335+
336+ signals[REQUEST_CANCELLED] = g_signal_new (UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED,
337+ G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
338+ G_SIGNAL_RUN_FIRST,
339+ G_STRUCT_OFFSET (UnitySettingsSecretAgentClass, request_cancelled),
340+ NULL, NULL, NULL,
341+ G_TYPE_NONE, 1,
342+ G_TYPE_UINT);
343+}
344+
345+
346+static void
347+unity_settings_secret_agent_instance_init (UnitySettingsSecretAgent *self)
348+{
349+ self->priv = UNITY_SETTINGS_SECRET_AGENT_GET_PRIVATE (self);
350+ self->priv->requests = g_queue_new ();
351+}
352+
353+GType
354+unity_settings_secret_agent_get_type (void)
355+{
356+ static volatile gsize unity_settings_secret_agent_type_id__volatile = 0;
357+ if (g_once_init_enter (&unity_settings_secret_agent_type_id__volatile))
358+ {
359+ static const GTypeInfo g_define_type_info =
360+ {
361+ sizeof (UnitySettingsSecretAgentClass),
362+ (GBaseInitFunc) NULL,
363+ (GBaseFinalizeFunc) NULL,
364+ (GClassInitFunc) unity_settings_secret_agent_class_init,
365+ (GClassFinalizeFunc) NULL,
366+ NULL,
367+ sizeof (UnitySettingsSecretAgent),
368+ 0,
369+ (GInstanceInitFunc) unity_settings_secret_agent_instance_init,
370+ NULL
371+ };
372+ GType unity_settings_secret_agent_type_id;
373+ unity_settings_secret_agent_type_id = g_type_register_static (NM_TYPE_SECRET_AGENT,
374+ "UnitySettingsSecretAgent",
375+ &g_define_type_info,
376+ 0);
377+ g_once_init_leave (&unity_settings_secret_agent_type_id__volatile,
378+ unity_settings_secret_agent_type_id);
379+ }
380+
381+ return unity_settings_secret_agent_type_id__volatile;
382+}
plugins/Unity/IndicatorsLegacy/Network/secret-agent.hnew file+102
@@ -0,0 +1,102 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Alberto Ruiz <alberto.ruiz@canonical.com>
18+ * Renato Araujo Oliveira Filho <renato@canonical.com>
19+ */
20+
21+#ifndef __SECRET_AGENT_H__
22+#define __SECRET_AGENT_H__
23+
24+#include <glib.h>
25+#include <glib-object.h>
26+#include <nm-secret-agent.h>
27+
28+/*
29+ * This class is a basic implementation of the NetworkManager SecretAgent base class.
30+ *
31+ * The purpose of this class is to handle credential requests from the network,
32+ * for example, from a WiFi hotspot or a VPN network.
33+ *
34+ * It queues requests objects (SecretRequest) on a GQueue in the private struct
35+ * of the class. And notifies the consumer of the class through the "secret-request"
36+ * and the "request-cancelled" signals with the following callback prototypes:
37+ *
38+ * void (*secret_requested) (UnitySettingsSecretAgent *self,
39+ * guint id,
40+ * NMConnection *connection,
41+ * const char *setting_name,
42+ * const char **hints,
43+ * NMSecretAgentGetSecretsFlags flags,
44+ * gpointer user_data);
45+ *
46+ * void (*request_cancelled) (UnitySettingsSecretAgent *self,
47+ * guint id,
48+ * gpointer user_data);
49+ *
50+ */
51+
52+
53+G_BEGIN_DECLS
54+
55+
56+#define UNITY_SETTINGS_TYPE_SECRET_AGENT (unity_settings_secret_agent_get_type ())
57+#define UNITY_SETTINGS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgent))
58+#define UNITY_SETTINGS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
59+#define UNITY_SETTINGS_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT))
60+#define UNITY_SETTINGS_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UNITY_SETTINGS_TYPE_SECRET_AGENT))
61+#define UNITY_SETTINGS_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UNITY_SETTINGS_TYPE_SECRET_AGENT, UnitySettingsSecretAgentClass))
62+
63+#define UNITY_SETTINGS_SECRET_AGENT_SECRET_REQUESTED "secret-requested"
64+#define UNITY_SETTINGS_SECRET_AGENT_REQUEST_CANCELLED "request-cancelled"
65+
66+typedef struct _UnitySettingsSecretAgent UnitySettingsSecretAgent;
67+typedef struct _UnitySettingsSecretAgentClass UnitySettingsSecretAgentClass;
68+typedef struct _UnitySettingsSecretAgentPrivate UnitySettingsSecretAgentPrivate;
69+
70+struct _UnitySettingsSecretAgent {
71+ NMSecretAgent parent_instance;
72+ UnitySettingsSecretAgentPrivate * priv;
73+};
74+
75+struct _UnitySettingsSecretAgentClass {
76+ NMSecretAgentClass parent_class;
77+
78+ void (*secret_requested) (UnitySettingsSecretAgent *self,
79+ guint id,
80+ NMConnection *connection,
81+ const char *setting_name,
82+ const char **hints,
83+ NMSecretAgentGetSecretsFlags flags);
84+
85+ void (*request_cancelled) (UnitySettingsSecretAgent *self,
86+ guint id);
87+};
88+
89+
90+GType unity_settings_secret_agent_get_type (void) G_GNUC_CONST;
91+UnitySettingsSecretAgent* unity_settings_secret_agent_new (void);
92+UnitySettingsSecretAgent* unity_settings_secret_agent_construct (GType object_type);
93+
94+void unity_settings_secret_agent_provide_secret (UnitySettingsSecretAgent *agent,
95+ guint request,
96+ GHashTable *secrets);
97+void unity_settings_secret_agent_cancel_request (UnitySettingsSecretAgent *agent,
98+ guint request);
99+
100+G_END_DECLS
101+
102+#endif
plugins/Unity/Indicators/flatmenuproxymodel.cpp → plugins/Unity/IndicatorsLegacy/flatmenuproxymodel.cpprenamed+13
@@ -80,6 +80,19 @@ void FlatMenuProxyModel::setSourceModel(QAbstractItemModel *source)
8080 SIGNAL(modelReset()),
8181 SLOT(onModelReset()), Qt::QueuedConnection);
8282
83+ //Connection args
84+ connect(source,
85+ SIGNAL(busTypeChanged(DBusEnums::BusType)),
86+ SIGNAL(busTypeChanged()));
87+
88+ connect(source,
89+ SIGNAL(busNameChanged(const QString&)),
90+ SIGNAL(busNameChanged()));
91+
92+ connect(source,
93+ SIGNAL(objectPathChanged(const QString&)),
94+ SIGNAL(objectPathChanged()));
95+
8396 connect(source,
8497 SIGNAL(statusChanged(DBusEnums::ConnectionStatus)),
8598 SIGNAL(statusChanged()), Qt::QueuedConnection);
plugins/Unity/Indicators/flatmenuproxymodel.h → plugins/Unity/IndicatorsLegacy/flatmenuproxymodel.hrenamed

The content is the same.

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

The content is the same.

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

The content is the same.

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

The content is the same.

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

The content is the same.

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

The content is the same.

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

Binary file not shown.

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

Binary file not shown.

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

The content is the same.

plugins/Unity/Launcher/backend/launcherbackend.cpp+1−1
@@ -101,7 +101,7 @@ bool LauncherBackend::isPinned(const QString &appId) const
101101{
102102 // TODO: return app's pinned state from settings
103103 Q_UNUSED(appId)
104− return true;
104+ return false;
105105}
106106
107107void LauncherBackend::setPinned(const QString &appId, bool pinned)
plugins/Unity/Launcher/common/quicklistentry.cpp+5
@@ -52,3 +52,8 @@ void QuickListEntry::setIcon(const QString &icon)
5252{
5353 m_icon = icon;
5454}
55+
56+bool QuickListEntry::clickable() const
57+{
58+ return !m_actionId.isEmpty();
59+}
plugins/Unity/Launcher/common/quicklistentry.h+2
@@ -35,6 +35,8 @@ public:
3535 QString icon() const;
3636 void setIcon(const QString &icon);
3737
38+ bool clickable() const;
39+
3840private:
3941 QString m_actionId;
4042 QString m_text;
plugins/Unity/Launcher/launcheritem.cpp+11−1
@@ -33,7 +33,13 @@ LauncherItem::LauncherItem(const QString &appId, const QString &desktopFile, con
3333 m_count(0),
3434 m_quickList(new QuickListModel(this))
3535{
36−
36+ QuickListEntry nameAction;
37+ nameAction.setText(m_name);
38+ m_quickList->appendAction(nameAction);
39+ QuickListEntry pinningAction;
40+ pinningAction.setActionId("pin_item");
41+ pinningAction.setText("Pin to Launcher");
42+ m_quickList->appendAction(pinningAction);
3743}
3844
3945QString LauncherItem::appId() const
@@ -65,6 +71,10 @@ void LauncherItem::setPinned(bool pinned)
6571{
6672 if (m_pinned != pinned) {
6773 m_pinned = pinned;
74+ QuickListEntry entry;
75+ entry.setActionId("pin_item");
76+ entry.setText(pinned ? "Remove from Launcher": "Pin to Launcher");
77+ m_quickList->updateAction(entry);
6878 Q_EMIT pinnedChanged(pinned);
6979 }
7080}
plugins/Unity/Launcher/launchermodel.cpp+68−4
@@ -25,6 +25,9 @@ LauncherModel::LauncherModel(QObject *parent):
2525 LauncherModelInterface(parent),
2626 m_backend(new LauncherBackend(this))
2727{
28+ connect(m_backend, SIGNAL(countChanged(QString,int)), SLOT(countChanged(QString,int)));
29+ connect(m_backend, SIGNAL(progressChanged(QString,int)), SLOT(progressChanged(QString,int)));
30+
2831 Q_FOREACH (const QString &entry, m_backend->storedApplications()) {
2932 LauncherItem *item = new LauncherItem(entry,
3033 m_backend->desktopFile(entry),
@@ -67,6 +70,10 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
6770 return item->icon();
6871 case RolePinned:
6972 return item->pinned();
73+ case RoleCount:
74+ return item->count();
75+ case RoleProgress:
76+ return item->progress();
7077 }
7178
7279 return QVariant();
@@ -82,8 +89,25 @@ unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) con
8289
8390void LauncherModel::move(int oldIndex, int newIndex)
8491{
85− // Perform the move in our list
86− beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
92+ // Make sure its not moved outside the lists
93+ if (newIndex < 0) {
94+ newIndex = 0;
95+ }
96+ if (newIndex >= m_list.count()) {
97+ newIndex = m_list.count()-1;
98+ }
99+
100+ // Nothing to do?
101+ if (oldIndex == newIndex) {
102+ return;
103+ }
104+
105+ // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
106+ // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
107+ // adjust the model's index by +1 in case we're moving upwards
108+ int newModelIndex = newIndex > oldIndex ? newIndex+1 : newIndex;
109+
110+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
87111 m_list.move(oldIndex, newIndex);
88112 endMoveRows();
89113
@@ -140,13 +164,32 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
140164 return;
141165 }
142166
143− QuickListModel *model = qobject_cast<QuickListModel*>(m_list.at(index)->quickList());
167+ LauncherItem *item = m_list.at(index);
168+ QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
144169 if (model) {
145170 QString actionId = model->get(actionIndex).actionId();
146− m_backend->triggerQuickListAction(appId, actionId);
171+
172+ // Check if this is one of the launcher actions we handle ourselves
173+ if (actionId == "pin_item") {
174+ if (item->pinned()) {
175+ requestRemove(appId);
176+ } else {
177+ pin(appId);
178+ }
179+
180+ // Nope, we don't know this action, let the backend forward it to the application
181+ } else {
182+ m_backend->triggerQuickListAction(appId, actionId);
183+ }
147184 }
148185}
149186
187+void LauncherModel::setUser(const QString &username)
188+{
189+ Q_UNUSED(username)
190+ // TODO: Implement this...
191+}
192+
150193void LauncherModel::storeAppList()
151194{
152195 QStringList appIds;
@@ -168,3 +211,24 @@ int LauncherModel::findApplication(const QString &appId)
168211 }
169212 return -1;
170213}
214+
215+void LauncherModel::progressChanged(const QString &appId, int progress)
216+{
217+ int idx = findApplication(appId);
218+ if (idx >= 0) {
219+ LauncherItem *item = m_list.at(idx);
220+ item->setProgress(progress);
221+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
222+ }
223+}
224+
225+
226+void LauncherModel::countChanged(const QString &appId, int count)
227+{
228+ int idx = findApplication(appId);
229+ if (idx >= 0) {
230+ LauncherItem *item = m_list.at(idx);
231+ item->setCount(count);
232+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
233+ }
234+}
plugins/Unity/Launcher/launchermodel.h+5
@@ -48,11 +48,16 @@ public:
4848 Q_INVOKABLE void pin(const QString &appId, int index = -1);
4949 Q_INVOKABLE void requestRemove(const QString &appId);
5050 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
51+ Q_INVOKABLE void setUser(const QString &username);
5152
5253private:
5354 void storeAppList();
5455 int findApplication(const QString &appId);
5556
57+private Q_SLOTS:
58+ void progressChanged(const QString &appId, int progress);
59+ void countChanged(const QString &appId, int count);
60+
5661private:
5762 QList<LauncherItem*> m_list;
5863 LauncherBackend *m_backend;
plugins/Unity/Launcher/plugin.cpp+3−1
@@ -18,7 +18,7 @@
1818 */
1919
2020// Qt
21−#include <QtQml>
21+#include <QtQml/qqml.h>
2222
2323// self
2424#include "plugin.h"
@@ -41,7 +41,9 @@ void UnityLauncherPlugin::registerTypes(const char *uri)
4141
4242 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
4343 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
44+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListInterface", "Abstract Interface. Cannot be instantiated.");
4445
4546 qmlRegisterSingletonType<LauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
4647 qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", "Can't create new Launcher Items in QML. Get them from the LauncherModel.");
48+ qmlRegisterUncreatableType<QuickListModel>(uri, 0, 1, "QuickListModel", "Can't create a QuickListModel in QML. Get them from the LauncherItems.");
4749}
plugins/Unity/Launcher/quicklistmodel.cpp+13
@@ -37,6 +37,17 @@ void QuickListModel::appendAction(const QuickListEntry &entry)
3737 endInsertRows();
3838}
3939
40+void QuickListModel::updateAction(const QuickListEntry &entry)
41+{
42+ for (int i = 0; i < m_list.count(); ++i) {
43+ if (m_list.at(i).actionId() == entry.actionId()) {
44+ m_list.replace(i, entry);
45+ Q_EMIT dataChanged(index(i), index(i));
46+ return;
47+ }
48+ }
49+}
50+
4051QuickListEntry QuickListModel::get(int index) const
4152{
4253 return m_list.at(index);
@@ -55,6 +66,8 @@ QVariant QuickListModel::data(const QModelIndex &index, int role) const
5566 return m_list.at(index.row()).text();
5667 case RoleIcon:
5768 return m_list.at(index.row()).icon();
69+ case RoleClickable:
70+ return m_list.at(index.row()).clickable();
5871 }
5972 return QVariant();
6073}
plugins/Unity/Launcher/quicklistmodel.h+10
@@ -36,6 +36,16 @@ public:
3636 ~QuickListModel();
3737
3838 void appendAction(const QuickListEntry &entry);
39+
40+ /**
41+ * @brief Update an existing action
42+ * @param entry The new, updated entry
43+ *
44+ * This will only do something if entry.actionId is found in the model.
45+ * To add a new entry, use appendAction().
46+ */
47+ void updateAction(const QuickListEntry &entry);
48+
3949 QuickListEntry get(int index) const;
4050
4151 int rowCount(const QModelIndex &parent = QModelIndex()) const;
plugins/Unity/abstractfilteroption.cppnew file+25
@@ -0,0 +1,25 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#include "abstractfilteroption.h"
21+
22+AbstractFilterOption::AbstractFilterOption(QObject *parent)
23+ : QObject(parent)
24+{
25+}
plugins/Unity/abstractfilteroption.hnew file+58
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef ABSTRACTFILTEROPTION_H
21+#define ABSTRACTFILTEROPTION_H
22+
23+// Qt
24+#include <QObject>
25+#include <QMetaType>
26+
27+class Q_DECL_EXPORT AbstractFilterOption : public QObject
28+{
29+ Q_OBJECT
30+
31+ Q_PROPERTY(QString id READ id NOTIFY idChanged)
32+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
33+ Q_PROPERTY(QString iconHint READ iconHint NOTIFY iconHintChanged)
34+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
35+
36+public:
37+ /* getters */
38+ virtual QString id() const = 0;
39+ virtual QString name() const = 0;
40+ virtual QString iconHint() const = 0;
41+ virtual bool active() const = 0;
42+
43+ /* setters */
44+ virtual void setActive(bool active) = 0;
45+
46+protected:
47+ AbstractFilterOption(QObject *parent);
48+
49+Q_SIGNALS:
50+ void idChanged(const QString &);
51+ void nameChanged(const QString &);
52+ void iconHintChanged(const QString &);
53+ void activeChanged(bool);
54+};
55+
56+Q_DECLARE_METATYPE(AbstractFilterOption*)
57+
58+#endif // ABSTRACTFILTEROPTION_H
plugins/Unity/categories.cpp+72
@@ -39,6 +39,7 @@ Categories::Categories(QObject* parent)
3939 m_roles[Categories::RoleIcon] = "icon";
4040 m_roles[Categories::RoleRenderer] = "renderer";
4141 m_roles[Categories::RoleContentType] = "contentType";
42+ m_roles[Categories::RoleProgressSource] = "progressSource";
4243 m_roles[Categories::RoleHints] = "hints";
4344 m_roles[Categories::RoleResults] = "results";
4445 m_roles[Categories::RoleCount] = "count";
@@ -104,6 +105,25 @@ Categories::onCountChanged()
104105 }
105106}
106107
108+void Categories::onRowCountChanged()
109+{
110+ QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>(sender());
111+ // find the corresponding category index
112+ for (auto iter = m_overriddenCategories.begin(); iter != m_overriddenCategories.end(); ++iter) {
113+ if (iter.value() == model) {
114+ for (int i = 0; i < rowCount(); i++) {
115+ auto id = data(index(i), RoleCategoryId).toString();
116+ if (id != iter.key()) continue;
117+ QVector<int> roles;
118+ roles.append(RoleCount);
119+ QModelIndex changedIndex = index(i);
120+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
121+ break;
122+ }
123+ }
124+ }
125+}
126+
107127void
108128Categories::onEmitCountChanged()
109129{
@@ -123,6 +143,41 @@ Categories::roleNames() const
123143 return m_roles;
124144}
125145
146+void Categories::onOverrideModelDestroyed()
147+{
148+ QObject* model = sender();
149+ auto iter = m_overriddenCategories.begin();
150+ while (iter != m_overriddenCategories.end()) {
151+ if (iter.value() == model) {
152+ iter = m_overriddenCategories.erase(iter);
153+ continue;
154+ }
155+ ++iter;
156+ }
157+}
158+
159+void Categories::overrideResults(const QString& categoryId, QAbstractItemModel* model)
160+{
161+ m_overriddenCategories[categoryId] = model;
162+ // watch the model
163+ connect(model, &QObject::destroyed, this, &Categories::onOverrideModelDestroyed);
164+ connect(model, &QAbstractItemModel::rowsInserted, this, &Categories::onRowCountChanged);
165+ connect(model, &QAbstractItemModel::rowsRemoved, this, &Categories::onRowCountChanged);
166+ connect(model, &QAbstractItemModel::modelReset, this, &Categories::onRowCountChanged);
167+
168+ // emit the dataChanged signal if the category is already in the model
169+ for (int i = 0; i < rowCount(); i++) {
170+ auto id = data(index(i), RoleCategoryId).toString();
171+ if (id != categoryId) continue;
172+ QVector<int> roles;
173+ roles.append(RoleCount);
174+ roles.append(RoleResults);
175+ QModelIndex changedIndex = index(i);
176+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
177+ break;
178+ }
179+}
180+
126181QVariant
127182Categories::data(const QModelIndex& index, int role) const
128183{
@@ -144,11 +199,28 @@ Categories::data(const QModelIndex& index, int role) const
144199 auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
145200 return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
146201 }
202+ case RoleProgressSource:
203+ {
204+ auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
205+ return hints.contains("progress-source") ? hints["progress-source"] : QVariant();
206+ }
147207 case RoleHints:
148208 return DeeListModel::data(index, CategoryColumn::HINTS);
149209 case RoleResults:
210+ if (m_overriddenCategories.size() > 0)
211+ {
212+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
213+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
214+ return QVariant::fromValue(m_overriddenCategories[id]);
215+ }
150216 return QVariant::fromValue(getResults(index.row()));
151217 case RoleCount:
218+ if (m_overriddenCategories.size() > 0)
219+ {
220+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
221+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
222+ return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
223+ }
152224 return QVariant::fromValue(getResults(index.row())->rowCount());
153225 case RoleCategoryIndex:
154226 return QVariant::fromValue(index.row());
plugins/Unity/categories.h+6
@@ -47,12 +47,15 @@ public:
4747 RoleIcon,
4848 RoleRenderer,
4949 RoleContentType,
50+ RoleProgressSource,
5051 RoleHints,
5152 RoleResults,
5253 RoleCount,
5354 RoleCategoryIndex
5455 };
5556
57+ Q_INVOKABLE void overrideResults(const QString& categoryId, QAbstractItemModel* model);
58+
5659 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
5760
5861 QHash<int, QByteArray> roleNames() const;
@@ -62,7 +65,9 @@ public:
6265
6366private Q_SLOTS:
6467 void onCountChanged();
68+ void onRowCountChanged();
6569 void onEmitCountChanged();
70+ void onOverrideModelDestroyed();
6671
6772private:
6873 void onCategoriesModelChanged(unity::glib::Object<DeeModel> model);
@@ -73,6 +78,7 @@ private:
7378 QTimer m_timer;
7479 QSet<int> m_updatedCategories;
7580 QHash<int, QByteArray> m_roles;
81+ QMap<QString, QAbstractItemModel*> m_overriddenCategories;
7682 mutable QMap<int, DeeListModel*> m_results;
7783 sigc::connection m_categoriesChangedConnection;
7884};
plugins/Unity/checkoptionfilter.cppnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 2011 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// Self
21+#include "checkoptionfilter.h"
22+
23+// local
24+#include "unityoptionsmodel.h"
25+
26+CheckOptionFilter::CheckOptionFilter(QObject *parent) :
27+ Filter(parent),
28+ m_unityCheckOptionFilter(nullptr),
29+ m_options(nullptr)
30+{
31+}
32+
33+GenericOptionsModel* CheckOptionFilter::options() const
34+{
35+ return m_options;
36+}
37+
38+void CheckOptionFilter::setUnityFilter(unity::dash::Filter::Ptr filter)
39+{
40+ Filter::setUnityFilter(filter);
41+ m_unityCheckOptionFilter = std::dynamic_pointer_cast<unity::dash::CheckOptionFilter>(m_unityFilter);
42+ onOptionsChanged(m_unityCheckOptionFilter->options);
43+}
44+
45+void CheckOptionFilter::onOptionsChanged(unity::dash::CheckOptionFilter::CheckOptions options)
46+{
47+ if (m_options != nullptr) {
48+ m_signals.disconnectAll();
49+ delete m_options;
50+ m_options = nullptr;
51+ }
52+
53+ m_options = new UnityOptionsModel(this, options,
54+ m_unityCheckOptionFilter->option_added,
55+ m_unityCheckOptionFilter->option_removed);
56+
57+ connect(m_options, SIGNAL(activeChanged(AbstractFilterOption *)), m_options, SLOT(ensureTheOnlyActive(AbstractFilterOption *)));
58+
59+ /* Property change signals */
60+ m_signals << m_unityCheckOptionFilter->options.changed.connect(sigc::mem_fun(this, &CheckOptionFilter::onOptionsChanged));
61+
62+ Q_EMIT optionsChanged();
63+}
plugins/Unity/checkoptionfilter.hnew file+57
@@ -0,0 +1,57 @@
1+/*
2+ * Copyright (C) 2011 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef CHECKOPTIONFILTER_H
21+#define CHECKOPTIONFILTER_H
22+
23+// Qt
24+#include <QObject>
25+#include <QMetaType>
26+
27+// libunity-core
28+#include <UnityCore/CheckOptionFilter.h>
29+
30+// Local
31+#include "filter.h"
32+#include "signalslist.h"
33+
34+class Q_DECL_EXPORT CheckOptionFilter : public Filter
35+{
36+ Q_OBJECT
37+
38+public:
39+ explicit CheckOptionFilter(QObject *parent = nullptr);
40+
41+ /* getters */
42+ GenericOptionsModel* options() const override;
43+
44+protected:
45+ void setUnityFilter(unity::dash::Filter::Ptr filter) override;
46+
47+private:
48+ void onOptionsChanged(unity::dash::CheckOptionFilter::CheckOptions filter);
49+
50+ unity::dash::CheckOptionFilter::Ptr m_unityCheckOptionFilter;
51+ SignalsList m_signals;
52+ GenericOptionsModel* m_options;
53+};
54+
55+Q_DECLARE_METATYPE(CheckOptionFilter*)
56+
57+#endif // CHECKOPTIONFILTER_H
plugins/Unity/filter.cppnew file+129
@@ -0,0 +1,129 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "filter.h"
23+
24+// local
25+#include "ratingsfilter.h"
26+#include "radiooptionfilter.h"
27+#include "checkoptionfilter.h"
28+#include "multirangefilter.h"
29+
30+// libunity-core
31+#include <UnityCore/Filter.h>
32+#include <UnityCore/RatingsFilter.h>
33+#include <UnityCore/RadioOptionFilter.h>
34+#include <UnityCore/CheckOptionFilter.h>
35+#include <UnityCore/MultiRangeFilter.h>
36+
37+// Qt
38+#include <QDebug>
39+
40+Filter::Filter(QObject *parent) :
41+ QObject(parent)
42+{
43+}
44+
45+QString Filter::id() const
46+{
47+ if (!m_unityFilter)
48+ return QString::Null();
49+ return QString::fromStdString(m_unityFilter->id());
50+}
51+
52+QString Filter::name() const
53+{
54+ if (!m_unityFilter)
55+ return QString::Null();
56+ return QString::fromStdString(m_unityFilter->name());
57+}
58+
59+QString Filter::iconHint() const
60+{
61+ if (!m_unityFilter)
62+ return QString::Null();
63+ return QString::fromStdString(m_unityFilter->icon_hint());
64+}
65+
66+QString Filter::rendererName() const
67+{
68+ if (!m_unityFilter)
69+ return QString::Null();
70+ return QString::fromStdString(m_unityFilter->renderer_name());
71+}
72+
73+bool Filter::visible() const
74+{
75+ if (!m_unityFilter)
76+ return false;
77+ return m_unityFilter->visible();
78+}
79+
80+bool Filter::collapsed() const
81+{
82+ if (!m_unityFilter)
83+ return false;
84+ return m_unityFilter->collapsed();
85+}
86+
87+bool Filter::filtering() const
88+{
89+ if (!m_unityFilter)
90+ return false;
91+ return m_unityFilter->filtering();
92+}
93+
94+void Filter::clear()
95+{
96+ if (m_unityFilter)
97+ m_unityFilter->Clear();
98+}
99+
100+void Filter::setUnityFilter(unity::dash::Filter::Ptr unityFilter)
101+{
102+ m_unityFilter = unityFilter;
103+}
104+
105+Filter* Filter::newFromUnityFilter(unity::dash::Filter::Ptr unityFilter)
106+{
107+ Filter* filter;
108+
109+ if (typeid(*unityFilter) == typeid(unity::dash::RatingsFilter)) {
110+ filter = new RatingsFilter;
111+ } else if (typeid(*unityFilter) == typeid(unity::dash::CheckOptionFilter)) {
112+ filter = new CheckOptionFilter;
113+ } else if (typeid(*unityFilter) == typeid(unity::dash::RadioOptionFilter)) {
114+ filter = new RadioOptionFilter;
115+ } else if (typeid(*unityFilter) == typeid(unity::dash::MultiRangeFilter)) {
116+ filter = new MultiRangeFilter;
117+ } else {
118+ qWarning() << "Filter of unknown type: " << typeid(*unityFilter).name();
119+ return nullptr;
120+ }
121+
122+ filter->setUnityFilter(unityFilter);
123+ return filter;
124+}
125+
126+bool Filter::hasUnityFilter(unity::dash::Filter::Ptr unityFilter) const
127+{
128+ return m_unityFilter == unityFilter;
129+}
plugins/Unity/filter.hnew file+74
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef FILTER_H
22+#define FILTER_H
23+
24+// Qt
25+#include <QObject>
26+#include <QMetaType>
27+
28+// libunity-core
29+#include <UnityCore/Filter.h>
30+
31+class GenericOptionsModel;
32+
33+class Q_DECL_EXPORT Filter : public QObject
34+{
35+ Q_OBJECT
36+
37+ Q_PROPERTY(QString id READ id CONSTANT)
38+ Q_PROPERTY(QString name READ name CONSTANT)
39+ Q_PROPERTY(QString iconHint READ iconHint CONSTANT)
40+ Q_PROPERTY(QString rendererName READ rendererName CONSTANT)
41+ Q_PROPERTY(bool visible READ visible CONSTANT)
42+ Q_PROPERTY(bool collapsed READ collapsed CONSTANT)
43+ Q_PROPERTY(bool filtering READ filtering CONSTANT)
44+ Q_PROPERTY(GenericOptionsModel* options READ options NOTIFY optionsChanged)
45+
46+public:
47+ explicit Filter(QObject *parent = nullptr);
48+
49+ /* getters */
50+ QString id() const;
51+ QString name() const;
52+ QString iconHint() const;
53+ QString rendererName() const;
54+ bool visible() const;
55+ bool collapsed() const;
56+ bool filtering() const;
57+ virtual GenericOptionsModel* options() const = 0;
58+
59+ Q_INVOKABLE void clear();
60+
61+ static Filter* newFromUnityFilter(unity::dash::Filter::Ptr unityFilter);
62+ bool hasUnityFilter(unity::dash::Filter::Ptr unityFilter) const;
63+
64+Q_SIGNALS:
65+ void optionsChanged();
66+
67+protected:
68+ unity::dash::Filter::Ptr m_unityFilter;
69+ virtual void setUnityFilter(unity::dash::Filter::Ptr unityFilter);
70+};
71+
72+Q_DECLARE_METATYPE(Filter*)
73+
74+#endif // FILTER_H
plugins/Unity/filteroption.cppnew file+91
@@ -0,0 +1,91 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "filteroption.h"
23+
24+// libunity-core
25+#include <UnityCore/Filter.h>
26+
27+FilterOption::FilterOption(unity::dash::FilterOption::Ptr unityFilterOption, QObject *parent) :
28+ AbstractFilterOption(parent), m_unityFilterOption(nullptr)
29+{
30+ setUnityFilterOption(unityFilterOption);
31+}
32+
33+QString FilterOption::id() const
34+{
35+ return QString::fromStdString(m_unityFilterOption->id());
36+}
37+
38+QString FilterOption::name() const
39+{
40+ return QString::fromStdString(m_unityFilterOption->name());
41+}
42+
43+QString FilterOption::iconHint() const
44+{
45+ return QString::fromStdString(m_unityFilterOption->icon_hint());
46+}
47+
48+bool FilterOption::active() const
49+{
50+ return m_unityFilterOption->active();
51+}
52+
53+void FilterOption::setActive(bool active)
54+{
55+ m_unityFilterOption->active = active;
56+}
57+
58+void FilterOption::setUnityFilterOption(unity::dash::FilterOption::Ptr unityFilterOption)
59+{
60+ if (m_unityFilterOption != nullptr) {
61+ m_signals.disconnectAll();
62+ }
63+
64+ m_unityFilterOption = unityFilterOption;
65+
66+ /* Property change signals */
67+ m_signals << m_unityFilterOption->id.changed.connect(sigc::mem_fun(this, &FilterOption::onIdChanged))
68+ << m_unityFilterOption->name.changed.connect(sigc::mem_fun(this, &FilterOption::onNameChanged))
69+ << m_unityFilterOption->icon_hint.changed.connect(sigc::mem_fun(this, &FilterOption::onIconHintChanged))
70+ << m_unityFilterOption->active.changed.connect(sigc::mem_fun(this, &FilterOption::onActiveChanged));
71+}
72+
73+void FilterOption::onIdChanged(const std::string &id)
74+{
75+ Q_EMIT idChanged(QString::fromStdString(id));
76+}
77+
78+void FilterOption::onNameChanged(const std::string &name)
79+{
80+ Q_EMIT nameChanged(QString::fromStdString(name));
81+}
82+
83+void FilterOption::onIconHintChanged(const std::string &iconHint)
84+{
85+ Q_EMIT iconHintChanged(QString::fromStdString(iconHint));
86+}
87+
88+void FilterOption::onActiveChanged(bool active)
89+{
90+ Q_EMIT activeChanged(active);
91+}
plugins/Unity/filteroption.hnew file+61
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef FILTEROPTION_H
22+#define FILTEROPTION_H
23+
24+// Local
25+#include "abstractfilteroption.h"
26+#include "signalslist.h"
27+
28+// libunity-core
29+#include <UnityCore/Filter.h>
30+
31+class Q_DECL_EXPORT FilterOption : public AbstractFilterOption
32+{
33+ Q_OBJECT
34+
35+public:
36+ explicit FilterOption(unity::dash::FilterOption::Ptr unityFilterOption, QObject *parent = nullptr);
37+
38+ /* getters */
39+ QString id() const override;
40+ QString name() const override;
41+ QString iconHint() const override;
42+ bool active() const override;
43+
44+ /* setters */
45+ void setActive(bool active) override;
46+
47+private:
48+ void onIdChanged(const std::string &id);
49+ void onNameChanged(const std::string &name);
50+ void onIconHintChanged(const std::string &iconHint);
51+ void onActiveChanged(bool active);
52+
53+ void setUnityFilterOption(unity::dash::FilterOption::Ptr unityFilterOption);
54+ SignalsList m_signals;
55+
56+ unity::dash::FilterOption::Ptr m_unityFilterOption;
57+};
58+
59+Q_DECLARE_METATYPE(FilterOption*)
60+
61+#endif // FILTEROPTION_H
plugins/Unity/filters.cppnew file+165
@@ -0,0 +1,165 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "filters.h"
23+
24+// Local
25+#include "filter.h"
26+#include "genericoptionsmodel.h"
27+
28+// Qt
29+#include <QDebug>
30+
31+Filters::Filters(unity::dash::Filters::Ptr unityFilters, QObject *parent) :
32+ QAbstractListModel(parent), m_unityFilters(unityFilters)
33+{
34+ for (unsigned int i=0; i<m_unityFilters->count(); i++) {
35+ unity::dash::Filter::Ptr unityFilter = m_unityFilters->FilterAtIndex(i);
36+ addFilter(unityFilter, i);
37+ }
38+ m_unityFilters->filter_added.connect(sigc::mem_fun(this, &Filters::onFilterAdded));
39+ m_unityFilters->filter_changed.connect(sigc::mem_fun(this, &Filters::onFilterChanged));
40+ m_unityFilters->filter_removed.connect(sigc::mem_fun(this, &Filters::onFilterRemoved));
41+}
42+
43+Filters::~Filters()
44+{
45+ while (!m_filters.isEmpty()) {
46+ delete m_filters.takeFirst();
47+ }
48+}
49+
50+int Filters::rowCount(const QModelIndex& parent) const
51+{
52+ Q_UNUSED(parent)
53+
54+ return m_unityFilters->count();
55+}
56+
57+QVariant Filters::data(const QModelIndex& index, int role) const
58+{
59+ if (!index.isValid()) {
60+ return QVariant();
61+ }
62+
63+ Filter* filter = m_filters.at(index.row());
64+ switch (role)
65+ {
66+ case Filters::RoleId:
67+ return filter->id();
68+ case Filters::RoleName:
69+ case Qt::DisplayRole:
70+ return filter->name();
71+ case Filters::RoleIconHint:
72+ return filter->iconHint();
73+ case Filters::RoleRendererName:
74+ return filter->rendererName();
75+ case Filters::RoleVisible:
76+ return filter->visible();
77+ case Filters::RoleCollapsed:
78+ return filter->collapsed();
79+ case Filters::RoleFiltering:
80+ return filter->filtering();
81+ case Filters::RoleOptions:
82+ return QVariant::fromValue(filter->options());
83+ default:
84+ break;
85+ }
86+ return QVariant();
87+}
88+
89+QHash<int, QByteArray> Filters::roleNames() const
90+{
91+ QHash<int, QByteArray> roles;
92+ roles[Filters::RoleId] = "id";
93+ roles[Filters::RoleName] = "name";
94+ roles[Filters::RoleIconHint] = "iconHint";
95+ roles[Filters::RoleRendererName] = "rendererName";
96+ roles[Filters::RoleVisible] = "visible";
97+ roles[Filters::RoleCollapsed] = "collapsed";
98+ roles[Filters::RoleFiltering] = "filtering";
99+ roles[Filters::RoleOptions] = "options";
100+ return roles;
101+}
102+
103+Filter* Filters::getFilter(const QString& id) const
104+{
105+ Q_FOREACH (Filter* filter, m_filters) {
106+ if (filter->id() == id) {
107+ return filter;
108+ }
109+ }
110+ return nullptr;
111+}
112+
113+void Filters::onFilterAdded(unity::dash::Filter::Ptr unityFilter)
114+{
115+ if (unityFilter == nullptr) {
116+ return;
117+ }
118+
119+ int index = m_filters.count();
120+ addFilter(unityFilter, index);
121+}
122+
123+void Filters::onFilterChanged(unity::dash::Filter::Ptr unityFilter)
124+{
125+ if (unityFilter == nullptr) {
126+ return;
127+ }
128+
129+ QModelIndex filterIndex = index(indexForFilter(unityFilter));
130+ Q_EMIT dataChanged(filterIndex, filterIndex);
131+}
132+
133+void Filters::onFilterRemoved(unity::dash::Filter::Ptr unityFilter)
134+{
135+ removeFilter(indexForFilter(unityFilter));
136+}
137+
138+void Filters::addFilter(unity::dash::Filter::Ptr unityFilter, int index)
139+{
140+ beginInsertRows(QModelIndex(), index, index);
141+ Filter* filter = Filter::newFromUnityFilter(unityFilter);
142+ if (filter != nullptr) {
143+ m_filters.insert(index, filter);
144+ }
145+ endInsertRows();
146+}
147+
148+void Filters::removeFilter(int index)
149+{
150+ beginRemoveRows(QModelIndex(), index, index);
151+ Filter* filter = m_filters.takeAt(index);
152+ delete filter;
153+ endRemoveRows();
154+}
155+
156+int Filters::indexForFilter(unity::dash::Filter::Ptr unityFilter)
157+{
158+ for (int index=0; index<m_filters.count(); index++) {
159+ if (m_filters[index]->hasUnityFilter(unityFilter)) {
160+ return index;
161+ }
162+ }
163+ qWarning() << "Filter" << QString::fromStdString(unityFilter->name()) << "not found in local cache.";
164+ return -1;
165+}
plugins/Unity/filters.hnew file+77
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef FILTERS_H
22+#define FILTERS_H
23+
24+// Qt
25+#include <QAbstractListModel>
26+#include <QList>
27+
28+// libunity-core
29+#include <UnityCore/Filters.h>
30+#include <UnityCore/Filter.h>
31+
32+class Filter;
33+
34+class Filters : public QAbstractListModel
35+{
36+ Q_OBJECT
37+
38+ Q_ENUMS(Roles)
39+
40+public:
41+ explicit Filters(unity::dash::Filters::Ptr unityFilters, QObject *parent = nullptr);
42+ ~Filters();
43+
44+ enum Roles {
45+ RoleId = Qt::UserRole,
46+ RoleName,
47+ RoleIconHint,
48+ RoleRendererName,
49+ RoleVisible,
50+ RoleCollapsed,
51+ RoleFiltering,
52+ RoleOptions
53+ };
54+
55+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
56+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
57+ QHash<int, QByteArray> roleNames() const override;
58+
59+ Filter* getFilter(const QString& id) const;
60+
61+private:
62+ void onFilterAdded(unity::dash::Filter::Ptr unityFilter);
63+ void onFilterChanged(unity::dash::Filter::Ptr unityFilter);
64+ void onFilterRemoved(unity::dash::Filter::Ptr unityFilter);
65+
66+ void addFilter(unity::dash::Filter::Ptr unityFilter, int index);
67+ void removeFilter(int index);
68+
69+ int indexForFilter(unity::dash::Filter::Ptr unityFilter);
70+
71+ unity::dash::Filters::Ptr m_unityFilters;
72+ QList<Filter*> m_filters;
73+};
74+
75+Q_DECLARE_METATYPE(Filters*)
76+
77+#endif // FILTERS_H
plugins/Unity/genericoptionsmodel.cppnew file+170
@@ -0,0 +1,170 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// self
21+#include "genericoptionsmodel.h"
22+
23+// local
24+#include "abstractfilteroption.h"
25+
26+GenericOptionsModel::GenericOptionsModel(QObject *parent)
27+ : QAbstractListModel(parent)
28+{
29+}
30+
31+GenericOptionsModel::~GenericOptionsModel()
32+{
33+ Q_FOREACH(auto opt, m_options)
34+ {
35+ opt->deleteLater();
36+ }
37+}
38+
39+QHash<int, QByteArray> GenericOptionsModel::roleNames() const
40+{
41+ QHash<int, QByteArray> roles;
42+ roles[GenericOptionsModel::RoleId] = "id";
43+ roles[GenericOptionsModel::RoleName] = "name";
44+ roles[GenericOptionsModel::RoleIconHint] = "iconHint";
45+ roles[GenericOptionsModel::RoleActive] = "active";
46+ return roles;
47+}
48+
49+QVariant GenericOptionsModel::data(const QModelIndex& index, int role) const
50+{
51+ if (!index.isValid()) {
52+ return QVariant();
53+ }
54+
55+ auto filterOption = m_options[index.row()];
56+ switch (role)
57+ {
58+ case GenericOptionsModel::RoleId:
59+ return filterOption->id();
60+ case GenericOptionsModel::RoleName:
61+ case Qt::DisplayRole:
62+ return filterOption->name();
63+ case GenericOptionsModel::RoleIconHint:
64+ return filterOption->iconHint();
65+ case GenericOptionsModel::RoleActive:
66+ return filterOption->active();
67+ default:
68+ break;
69+ }
70+
71+ return QVariant();
72+}
73+
74+int GenericOptionsModel::rowCount(const QModelIndex& /* parent */) const
75+{
76+ return m_options.size ();
77+}
78+
79+void GenericOptionsModel::setActive(int idx, bool value)
80+{
81+ if (idx >= 0 && idx < m_options.size())
82+ {
83+ m_options[idx]->setActive(value);
84+ }
85+}
86+
87+void GenericOptionsModel::setActive(const QString &id, bool value)
88+{
89+ Q_FOREACH(auto opt, m_options) {
90+ if (opt->id() == id) {
91+ opt->setActive(value);
92+ break;
93+ }
94+ }
95+}
96+
97+AbstractFilterOption* GenericOptionsModel::getRawOption(QVector<AbstractFilterOption *>::size_type idx) const
98+{
99+ if (idx < m_options.size())
100+ {
101+ return m_options[idx];
102+ }
103+ return nullptr;
104+}
105+
106+void GenericOptionsModel::ensureTheOnlyActive(AbstractFilterOption *activeOption)
107+{
108+ if (activeOption->active()) {
109+ // disable all other options
110+ Q_FOREACH(auto opt, m_options) {
111+ if (opt != activeOption && opt->active()) {
112+ opt->setActive(false);
113+ }
114+ }
115+ }
116+}
117+
118+void GenericOptionsModel::addOption(AbstractFilterOption *option, int index)
119+{
120+ m_options.insert(index, option);
121+
122+ connect(option, SIGNAL(idChanged(const QString &)), this, SLOT(onOptionChanged()));
123+ connect(option, SIGNAL(nameChanged(const QString &)), this, SLOT(onOptionChanged()));
124+ connect(option, SIGNAL(iconHintChanged(const QString &)), this, SLOT(onOptionChanged()));
125+ connect(option, SIGNAL(activeChanged(bool)), this, SLOT(onOptionChanged()));
126+ connect(option, SIGNAL(activeChanged(bool)), this, SLOT(onActiveChanged()));
127+}
128+
129+void GenericOptionsModel::removeOption(int index)
130+{
131+ m_options[index]->deleteLater();
132+ m_options.remove(index);
133+}
134+
135+int GenericOptionsModel::indexOf(const QString &option_id)
136+{
137+ int row = 0;
138+ Q_FOREACH(auto opt, m_options)
139+ {
140+ if (opt->id() == option_id)
141+ {
142+ return row;
143+ }
144+ ++row;
145+ }
146+ return -1;
147+}
148+
149+void GenericOptionsModel::onOptionChanged()
150+{
151+ AbstractFilterOption *option = dynamic_cast<AbstractFilterOption*>(QObject::sender());
152+ if (option)
153+ {
154+ int idx = indexOf(option->id());
155+ if (idx >= 0)
156+ {
157+ QModelIndex optionIndex = index(idx);
158+ Q_EMIT dataChanged(optionIndex, optionIndex);
159+ }
160+ }
161+}
162+
163+void GenericOptionsModel::onActiveChanged()
164+{
165+ AbstractFilterOption *option = dynamic_cast<AbstractFilterOption*>(QObject::sender());
166+ if (option)
167+ {
168+ Q_EMIT activeChanged(option);
169+ }
170+}
plugins/Unity/genericoptionsmodel.hnew file+77
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef GENERICOPTIONSMODEL_H
21+#define GENERICOPTIONSMODEL_H
22+
23+// Qt
24+#include <QObject>
25+#include <QAbstractListModel>
26+#include <QHash>
27+#include <QVector>
28+
29+class AbstractFilterOption;
30+
31+class Q_DECL_EXPORT GenericOptionsModel : public QAbstractListModel
32+{
33+ Q_OBJECT
34+
35+ Q_ENUMS(Roles)
36+
37+public:
38+ GenericOptionsModel(QObject *parent = nullptr);
39+ ~GenericOptionsModel();
40+
41+ enum Roles {
42+ RoleId = Qt::UserRole,
43+ RoleName,
44+ RoleIconHint,
45+ RoleActive
46+ };
47+
48+ QHash<int, QByteArray> roleNames() const override;
49+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
50+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
51+
52+ Q_INVOKABLE void setActive(int idx, bool value);
53+ Q_INVOKABLE void setActive(const QString &id, bool value);
54+
55+ AbstractFilterOption* getRawOption(QVector<AbstractFilterOption *>::size_type idx) const;
56+
57+public Q_SLOTS:
58+ virtual void ensureTheOnlyActive(AbstractFilterOption *activeOption);
59+
60+Q_SIGNALS:
61+ void activeChanged(AbstractFilterOption *option);
62+
63+protected Q_SLOTS:
64+ void onOptionChanged();
65+ void onActiveChanged();
66+
67+protected:
68+ void addOption(AbstractFilterOption *option, int index);
69+ void removeOption(int index);
70+ int indexOf(const QString &option_id);
71+
72+ QVector<AbstractFilterOption *> m_options;
73+};
74+
75+Q_DECLARE_METATYPE(GenericOptionsModel*)
76+
77+#endif // GENERICOPTIONSMODEL_H
plugins/Unity/multirangefilter.cppnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "multirangefilter.h"
23+
24+// local
25+#include "unityoptionsmodel.h"
26+
27+MultiRangeFilter::MultiRangeFilter(QObject *parent) :
28+ Filter(parent), m_unityMultiRangeFilter(nullptr), m_options(nullptr)
29+{
30+}
31+
32+GenericOptionsModel* MultiRangeFilter::options() const
33+{
34+ return m_options;
35+}
36+
37+void MultiRangeFilter::setUnityFilter(unity::dash::Filter::Ptr filter)
38+{
39+ Filter::setUnityFilter(filter);
40+ m_unityMultiRangeFilter = std::dynamic_pointer_cast<unity::dash::MultiRangeFilter>(m_unityFilter);
41+
42+ onOptionsChanged(m_unityMultiRangeFilter->options);
43+}
44+
45+void MultiRangeFilter::onOptionsChanged(unity::dash::MultiRangeFilter::Options options)
46+{
47+ if (m_options != nullptr) {
48+ m_signals.disconnectAll();
49+ delete m_options;
50+ m_options = nullptr;
51+ }
52+
53+ m_options = new UnityOptionsModel(this, options,
54+ m_unityMultiRangeFilter->option_added,
55+ m_unityMultiRangeFilter->option_removed);
56+
57+ connect(m_options, SIGNAL(activeChanged(AbstractFilterOption *)), m_options, SLOT(ensureTheOnlyActive(AbstractFilterOption *)));
58+
59+ /* Property change signals */
60+ m_signals << m_unityMultiRangeFilter->options.changed.connect(sigc::mem_fun(this, &MultiRangeFilter::onOptionsChanged));
61+
62+ Q_EMIT optionsChanged();
63+}
plugins/Unity/multirangefilter.hnew file+60
@@ -0,0 +1,60 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef MULTIRANGEFILTER_H
22+#define MULTIRANGEFILTER_H
23+
24+// Qt
25+#include <QObject>
26+#include <QMetaType>
27+
28+// libunity-core
29+#include <UnityCore/MultiRangeFilter.h>
30+
31+// Local
32+#include "filter.h"
33+#include "signalslist.h"
34+
35+class GenericOptionsModel;
36+
37+class Q_DECL_EXPORT MultiRangeFilter : public Filter
38+{
39+ Q_OBJECT
40+
41+public:
42+ explicit MultiRangeFilter(QObject *parent = nullptr);
43+
44+ /* getters */
45+ GenericOptionsModel* options() const override;
46+
47+protected:
48+ void setUnityFilter(unity::dash::Filter::Ptr filter) override;
49+
50+private:
51+ unity::dash::MultiRangeFilter::Ptr m_unityMultiRangeFilter;
52+ SignalsList m_signals;
53+ GenericOptionsModel* m_options;
54+
55+ void onOptionsChanged(unity::dash::MultiRangeFilter::Options);
56+};
57+
58+Q_DECLARE_METATYPE(MultiRangeFilter*)
59+
60+#endif // MULTIRANGEFILTER_H
plugins/Unity/plugin.cpp+4−1
@@ -17,8 +17,9 @@
1717 */
1818
1919// Qt
20−#include <QtQml>
2120#include <QDBusConnection>
21+#include <QQmlContext>
22+#include <qqml.h>
2223
2324// self
2425#include "plugin.h"
@@ -33,6 +34,7 @@
3334#include "categories.h"
3435#include "categoryresults.h"
3536#include "bottombarvisibilitycommunicatorshell.h"
37+#include "genericoptionsmodel.h"
3638
3739// libqtdee
3840#include "deelistmodel.h"
@@ -47,6 +49,7 @@ void UnityPlugin::registerTypes(const char *uri)
4749 qmlRegisterUncreatableType<PreviewAction>(uri, 0, 1, "PreviewAction", "Can't create PreviewAction object in QML.");
4850 qmlRegisterUncreatableType<PreviewInfoHint>(uri, 0, 1, "PreviewInfoHint", "Can't create PreviewInfoHint object in QML.");
4951 qmlRegisterUncreatableType<SocialPreviewComment>(uri, 0, 1, "SocialPreviewComment", "Can't create SocialPreviewComment object in QML.");
52+ qmlRegisterUncreatableType<GenericOptionsModel>(uri, 0, 1, "GenericOptionsModel", "Can't create options model in QML.");
5053 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
5154 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
5255 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
plugins/Unity/radiooptionfilter.cppnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "radiooptionfilter.h"
23+
24+// local
25+#include "unityoptionsmodel.h"
26+
27+RadioOptionFilter::RadioOptionFilter(QObject *parent) :
28+ Filter(parent), m_unityRadioOptionFilter(nullptr), m_options(nullptr)
29+{
30+}
31+
32+GenericOptionsModel* RadioOptionFilter::options() const
33+{
34+ return m_options;
35+}
36+
37+void RadioOptionFilter::setUnityFilter(unity::dash::Filter::Ptr filter)
38+{
39+ Filter::setUnityFilter(filter);
40+ m_unityRadioOptionFilter = std::dynamic_pointer_cast<unity::dash::RadioOptionFilter>(m_unityFilter);
41+
42+ onOptionsChanged(m_unityRadioOptionFilter->options);
43+}
44+
45+void RadioOptionFilter::onOptionsChanged(unity::dash::RadioOptionFilter::RadioOptions)
46+{
47+ if (m_options != nullptr) {
48+ m_signals.disconnectAll();
49+ delete m_options;
50+ m_options = nullptr;
51+ }
52+ m_options = new UnityOptionsModel(this, m_unityRadioOptionFilter->options,
53+ m_unityRadioOptionFilter->option_added,
54+ m_unityRadioOptionFilter->option_removed);
55+
56+ m_signals << m_unityRadioOptionFilter->options.changed.connect(sigc::mem_fun(this, &RadioOptionFilter::onOptionsChanged));
57+
58+ Q_EMIT optionsChanged();
59+}
plugins/Unity/radiooptionfilter.hnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef RADIOOPTIONFILTER_H
22+#define RADIOOPTIONFILTER_H
23+
24+// Qt
25+#include <QObject>
26+#include <QMetaType>
27+
28+// libunity-core
29+#include <UnityCore/RadioOptionFilter.h>
30+
31+// Local
32+#include "filter.h"
33+#include "filteroption.h"
34+#include "signalslist.h"
35+
36+class Q_DECL_EXPORT RadioOptionFilter : public Filter
37+{
38+ Q_OBJECT
39+
40+public:
41+ explicit RadioOptionFilter(QObject *parent = nullptr);
42+
43+ /* getters */
44+ GenericOptionsModel* options() const override;
45+
46+protected:
47+ void setUnityFilter(unity::dash::Filter::Ptr filter) override;
48+
49+private:
50+ unity::dash::RadioOptionFilter::Ptr m_unityRadioOptionFilter;
51+ GenericOptionsModel* m_options;
52+ SignalsList m_signals;
53+
54+ void onOptionsChanged(unity::dash::RadioOptionFilter::RadioOptions);
55+};
56+
57+Q_DECLARE_METATYPE(RadioOptionFilter*)
58+
59+#endif // RADIOOPTIONFILTER_H
plugins/Unity/ratingfilteroption.cppnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// self
21+#include "ratingfilteroption.h"
22+
23+RatingFilterOption::RatingFilterOption(const QString &id, float value, QObject *parent) :
24+ AbstractFilterOption(parent),
25+ m_active(false),
26+ m_id(id),
27+ m_value(value)
28+{
29+}
30+
31+QString RatingFilterOption::id() const
32+{
33+ return m_id;
34+}
35+
36+QString RatingFilterOption::name() const
37+{
38+ return QString::Null();
39+}
40+
41+QString RatingFilterOption::iconHint() const
42+{
43+ return QString::Null();
44+}
45+
46+float RatingFilterOption::value() const
47+{
48+ return m_value;
49+}
50+
51+bool RatingFilterOption::active() const
52+{
53+ return m_active;
54+}
55+
56+void RatingFilterOption::setActive(bool active)
57+{
58+ if (active != m_active) {
59+ m_active = active;
60+ Q_EMIT activeChanged(m_active);
61+ }
62+}
plugins/Unity/ratingfilteroption.hnew file+53
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef RATINGFILTEROPTION_H
21+#define RATINGFILTEROPTION_H
22+
23+// local
24+#include "abstractfilteroption.h"
25+
26+class Q_DECL_EXPORT RatingFilterOption : public AbstractFilterOption
27+{
28+ Q_OBJECT
29+
30+ Q_PROPERTY(float value READ value CONSTANT)
31+
32+public:
33+ explicit RatingFilterOption(const QString &id, float ratingValue, QObject *parent = nullptr);
34+
35+ /* getters */
36+ QString id() const override;
37+ QString name() const override;
38+ QString iconHint() const override;
39+ bool active() const override;
40+ float value() const;
41+
42+ /* setters */
43+ void setActive(bool active) override;
44+
45+private:
46+ bool m_active;
47+ QString m_id;
48+ float m_value;
49+};
50+
51+Q_DECLARE_METATYPE(RatingFilterOption*)
52+
53+#endif // RATINGFILTEROPTION_H
plugins/Unity/ratingoptionsmodel.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// self
21+#include "ratingoptionsmodel.h"
22+
23+// local
24+#include "ratingfilteroption.h"
25+
26+RatingOptionsModel::RatingOptionsModel(QObject *parent) :
27+ GenericOptionsModel(parent)
28+{
29+ for (int i=1; i<=5; i++) {
30+ auto opt = new RatingFilterOption(QString::number(i), i*0.2f, this);
31+ addOption(opt, i-1);
32+ }
33+}
plugins/Unity/ratingoptionsmodel.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef RATINGOPTIONSMODEL_H
21+#define RATINGOPTIONSMODEL_H
22+
23+#include "genericoptionsmodel.h"
24+
25+class RatingOptionsModel : public GenericOptionsModel
26+{
27+ Q_OBJECT
28+
29+public:
30+ RatingOptionsModel(QObject *parent);
31+};
32+
33+#endif // RATINGOPTIONSMODEL_H
plugins/Unity/ratingsfilter.cppnew file+72
@@ -0,0 +1,72 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+// Self
22+#include "ratingsfilter.h"
23+
24+// local
25+#include "ratingfilteroption.h"
26+#include "ratingoptionsmodel.h"
27+
28+RatingsFilter::RatingsFilter(QObject *parent) :
29+ Filter(parent),
30+ m_unityRatingsFilter(nullptr),
31+ m_options(nullptr)
32+{
33+}
34+
35+GenericOptionsModel* RatingsFilter::options() const
36+{
37+ return m_options;
38+}
39+
40+float RatingsFilter::rating() const
41+{
42+ if (m_unityRatingsFilter)
43+ {
44+ return m_unityRatingsFilter->rating;
45+ }
46+ return 0.0f;
47+}
48+
49+void RatingsFilter::setUnityFilter(unity::dash::Filter::Ptr filter)
50+{
51+ Filter::setUnityFilter(filter);
52+ m_unityRatingsFilter = std::dynamic_pointer_cast<unity::dash::RatingsFilter>(m_unityFilter);
53+
54+ delete m_options;
55+
56+ m_options = new RatingOptionsModel(this);
57+ connect(m_options, SIGNAL(activeChanged(AbstractFilterOption *)), m_options, SLOT(ensureTheOnlyActive(AbstractFilterOption *)));
58+ connect(m_options, SIGNAL(activeChanged(AbstractFilterOption *)), this, SLOT(onActiveChanged(AbstractFilterOption *)));
59+
60+ Q_EMIT optionsChanged();
61+}
62+
63+void RatingsFilter::onActiveChanged(AbstractFilterOption *option)
64+{
65+ RatingFilterOption *ratingOption = dynamic_cast<RatingFilterOption*>(option);
66+ if (ratingOption != nullptr)
67+ {
68+ const float val = ratingOption->active() ? ratingOption->value() : 0.0f;
69+ m_unityRatingsFilter->rating = val;
70+ Q_EMIT ratingChanged(val);
71+ }
72+}
plugins/Unity/ratingsfilter.hnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Florian Boucault <florian.boucault@canonical.com>
6+ * Pawel Stolowski <pawel.stolowski@canonical.com>
7+ *
8+ * This program is free software; you can redistribute it and/or modify
9+ * it under the terms of the GNU General Public License as published by
10+ * the Free Software Foundation; version 3.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+#ifndef RATINGSFILTER_H
22+#define RATINGSFILTER_H
23+
24+// Qt
25+#include <QObject>
26+#include <QMetaType>
27+
28+// libunity-core
29+#include <UnityCore/RatingsFilter.h>
30+
31+// Local
32+#include "filter.h"
33+
34+class GenericOptionsModel;
35+class AbstractFilterOption;
36+
37+class Q_DECL_EXPORT RatingsFilter : public Filter
38+{
39+ Q_OBJECT
40+
41+ Q_PROPERTY(float rating READ rating NOTIFY ratingChanged)
42+
43+public:
44+ explicit RatingsFilter(QObject *parent = nullptr);
45+
46+ /* getters */
47+ GenericOptionsModel* options() const override;
48+ float rating() const;
49+
50+Q_SIGNALS:
51+ void ratingChanged(float);
52+
53+protected:
54+ void setUnityFilter(unity::dash::Filter::Ptr filter) override;
55+
56+protected Q_SLOTS:
57+ void onActiveChanged(AbstractFilterOption *option);
58+
59+private:
60+ unity::dash::RatingsFilter::Ptr m_unityRatingsFilter;
61+ GenericOptionsModel *m_options;
62+};
63+
64+Q_DECLARE_METATYPE(RatingsFilter*)
65+
66+#endif // RATINGSFILTER_H
plugins/Unity/scope.cpp+27−9
@@ -35,6 +35,7 @@
3535#include <UnityCore/Variant.h>
3636
3737#include <libintl.h>
38+#include <glib.h>
3839
3940Scope::Scope(QObject *parent) : QObject(parent)
4041 , m_formFactor("phone")
@@ -102,6 +103,11 @@ QString Scope::formFactor() const
102103 return m_formFactor;
103104}
104105
106+Filters* Scope::filters() const
107+{
108+ return m_filters.get();
109+}
110+
105111void Scope::setSearchQuery(const QString& search_query)
106112{
107113 /* Checking for m_searchQuery.isNull() which returns true only when the string
@@ -209,18 +215,11 @@ void Scope::onPreviewReady(unity::dash::LocalResult const& /* result */, unity::
209215
210216void Scope::fallbackActivate(const QString& uri)
211217{
212− /* FIXME: stripping all content before the first column because for some
213− reason the scopes give uri with junk content at their beginning.
214− */
215− QString tweakedUri = uri;
216− int firstColumnAt = tweakedUri.indexOf(":");
217− tweakedUri.remove(0, firstColumnAt+1);
218−
219218 /* Tries various methods to trigger a sensible action for the given 'uri'.
220219 If it has no understanding of the given scheme it falls back on asking
221220 Qt to open the uri.
222221 */
223− QUrl url(tweakedUri);
222+ QUrl url(uri);
224223 if (url.scheme() == "file") {
225224 /* Override the files place's default URI handler: we want the file
226225 manager to handle opening folders, not the dash.
@@ -231,7 +230,25 @@ void Scope::fallbackActivate(const QString& uri)
231230 return;
232231 }
233232 if (url.scheme() == "application") {
234− // TODO: implement application handling
233+ // get the full path to the desktop file
234+ QString path(url.path().isEmpty() ? url.authority() : url.path());
235+ if (path.startsWith("/")) {
236+ Q_EMIT activateApplication(path);
237+ } else {
238+ // TODO: use the new desktop file finder/parser when it's ready
239+ gchar* full_path = nullptr;
240+ GKeyFile* key_file = g_key_file_new();
241+ QString apps_path = "applications/" + path;
242+ if (g_key_file_load_from_data_dirs(key_file, apps_path.toLocal8Bit().constData(), &full_path,
243+ G_KEY_FILE_NONE, nullptr)) {
244+ path = full_path;
245+ Q_EMIT activateApplication(path);
246+ } else {
247+ qWarning() << "Unable to activate " << path;
248+ }
249+ g_key_file_free(key_file);
250+ g_free(full_path);
251+ }
235252 return;
236253 }
237254
@@ -246,6 +263,7 @@ void Scope::setUnityScope(const unity::dash::Scope::Ptr& scope)
246263 m_unityScope = scope;
247264
248265 m_categories->setUnityScope(m_unityScope);
266+ m_filters.reset(new Filters(m_unityScope->filters, this));
249267
250268 m_unityScope->form_factor = m_formFactor.toStdString();
251269 /* Property change signals */
plugins/Unity/scope.h+7
@@ -30,6 +30,7 @@
3030#include <UnityCore/Results.h>
3131
3232#include "categories.h"
33+#include "filters.h"
3334
3435class Preview;
3536
@@ -46,6 +47,7 @@ class Scope : public QObject
4647 Q_PROPERTY(QString shortcut READ shortcut NOTIFY shortcutChanged)
4748 Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
4849 Q_PROPERTY(Categories* categories READ categories NOTIFY categoriesChanged)
50+ Q_PROPERTY(Filters* filters READ filters NOTIFY filtersChanged)
4951
5052 Q_PROPERTY(QString searchQuery READ searchQuery WRITE setSearchQuery NOTIFY searchQueryChanged)
5153 Q_PROPERTY(QString noResultsHint READ noResultsHint WRITE setNoResultsHint NOTIFY noResultsHintChanged)
@@ -64,6 +66,7 @@ public:
6466 QString shortcut() const;
6567 bool connected() const;
6668 Categories* categories() const;
69+ Filters* filters() const;
6770 QString searchQuery() const;
6871 QString noResultsHint() const;
6972 QString formFactor() const;
@@ -98,6 +101,7 @@ Q_SIGNALS:
98101 void searchQueryChanged();
99102 void noResultsHintChanged();
100103 void formFactorChanged();
104+ void filtersChanged();
101105
102106 // signals triggered by activate(..) or preview(..) requests.
103107 void previewReady(Preview *preview);
@@ -105,6 +109,8 @@ Q_SIGNALS:
105109 void hideDash();
106110 void gotoUri(const QString &uri);
107111
112+ void activateApplication(const QString &desktop);
113+
108114private Q_SLOTS:
109115 void synchronizeStates();
110116 void onSearchFinished(const std::string &, unity::glib::HintsMap const &);
@@ -121,6 +127,7 @@ private:
121127
122128 unity::dash::Scope::Ptr m_unityScope;
123129 std::unique_ptr<Categories> m_categories;
130+ std::unique_ptr<Filters> m_filters;
124131 QString m_searchQuery;
125132 QString m_noResultsHint;
126133 QString m_formFactor;
plugins/Unity/signalslist.cppnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#include "signalslist.h"
21+
22+void SignalsList::append(const sigc::connection &c)
23+{
24+ m_signals.append(c);
25+}
26+
27+void SignalsList::disconnectAll()
28+{
29+ Q_FOREACH(auto sig, m_signals) {
30+ sig.disconnect();
31+ }
32+ m_signals.clear();
33+}
34+
35+SignalsList& SignalsList::operator<<(const sigc::connection &c)
36+{
37+ append(c);
38+ return *this;
39+}
plugins/Unity/signalslist.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef SIGNALSLIST_H
21+#define SIGNALSLIST_H
22+
23+#include <QList>
24+#include <sigc++/sigc++.h>
25+
26+class Q_DECL_EXPORT SignalsList
27+{
28+public:
29+ void append(const sigc::connection &c);
30+ void disconnectAll();
31+
32+ SignalsList& operator<<(const sigc::connection &c);
33+
34+private:
35+ QList<sigc::connection> m_signals;
36+};
37+
38+#endif // SIGNALSLIST_H
plugins/Unity/unityoptionsmodel.cppnew file+67
@@ -0,0 +1,67 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+// self
21+#include "unityoptionsmodel.h"
22+
23+// local
24+#include "filteroption.h"
25+
26+UnityOptionsModel::UnityOptionsModel(QObject *parent,
27+ const std::vector<unity::dash::FilterOption::Ptr> options,
28+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionAdded,
29+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionRemoved) :
30+ GenericOptionsModel(parent)
31+{
32+ setOptions(options, optionAdded, optionRemoved);
33+}
34+
35+void UnityOptionsModel::setOptions(const std::vector<unity::dash::FilterOption::Ptr> options,
36+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionAdded,
37+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionRemoved)
38+{
39+ for (unsigned int i=0; i<options.size(); i++)
40+ {
41+ addOption(new FilterOption(options[i], this), i);
42+ }
43+ optionAdded.connect(sigc::mem_fun(this, &UnityOptionsModel::onOptionAdded));
44+ optionRemoved.connect(sigc::mem_fun(this, &UnityOptionsModel::onOptionRemoved));
45+}
46+
47+void UnityOptionsModel::onOptionAdded(unity::dash::FilterOption::Ptr option)
48+{
49+ int index = m_options.count();
50+ if (index >= 0)
51+ {
52+ beginInsertRows(QModelIndex(), index, index);
53+ addOption(new FilterOption(option, this), index);
54+ endInsertRows();
55+ }
56+}
57+
58+void UnityOptionsModel::onOptionRemoved(unity::dash::FilterOption::Ptr option)
59+{
60+ auto index = indexOf(QString::fromStdString(option->id));
61+ if (index >= 0)
62+ {
63+ beginRemoveRows(QModelIndex(), index, index);
64+ removeOption(index);
65+ endRemoveRows();
66+ }
67+}
plugins/Unity/unityoptionsmodel.hnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef UNITYOPTIONSMODEL_H
21+#define UNITYOPTIONSMODEL_H
22+
23+#include "genericoptionsmodel.h"
24+
25+// libunity-core
26+#include <UnityCore/Filter.h>
27+
28+#include <sigc++/signal.h>
29+
30+class UnityOptionsModel : public GenericOptionsModel
31+{
32+ Q_OBJECT
33+
34+public:
35+ UnityOptionsModel(QObject *parent,
36+ const std::vector<unity::dash::FilterOption::Ptr> options,
37+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionAdded,
38+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionRemoved);
39+
40+private:
41+ void setOptions(const std::vector<unity::dash::FilterOption::Ptr> options,
42+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionAdded,
43+ sigc::signal<void, unity::dash::FilterOption::Ptr> optionRemoved);
44+
45+ void onOptionAdded(unity::dash::FilterOption::Ptr option);
46+ void onOptionRemoved(unity::dash::FilterOption::Ptr option);
47+};
48+
49+#endif // UNITYOPTIONSMODEL_H
plugins/Utils/plugin.cpp+1−1
@@ -17,7 +17,7 @@
1717 */
1818
1919// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
2121#include <QtQuick/QQuickWindow>
2222
2323// self
plugins/Utils/qlimitproxymodelqml.cpp+90−4
@@ -25,6 +25,8 @@ QLimitProxyModelQML::QLimitProxyModelQML(QObject *parent)
2525 , m_limit(-1)
2626 , m_sourceInserting(false)
2727 , m_sourceRemoving(false)
28+ , m_dataChangedBegin(-1)
29+ , m_dataChangedEnd(-1)
2830{
2931 connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
3032 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
@@ -130,8 +132,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeInserted(const QModelIndex &parent, int
130132 beginInsertRows(mapFromSource(parent), start, end);
131133 m_sourceInserting = true;
132134 } else if (start < m_limit) {
133− beginInsertRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
134− m_sourceInserting = true;
135+ const int nSourceAddedItems = end - start + 1;
136+ const int currentCount = QIdentityProxyModel::rowCount();
137+ if (currentCount + nSourceAddedItems <= m_limit) {
138+ // After Inserting items we will be under the limit
139+ // so just proceed with the insertion normally
140+ beginInsertRows(mapFromSource(parent), start, end);
141+ m_sourceInserting = true;
142+ } else if (currentCount >= m_limit) {
143+ // We are already over the limit so to our users we are not adding items, just
144+ // changing it's data, i.e we had something like
145+ // A B C D E
146+ // with a limit of 5
147+ // after inserting (let's say three 'F' at position 1) we will have
148+ // A F F F B
149+ // so we just need to signal a dataChanged from 1 to 4
150+ m_dataChangedBegin = start;
151+ m_dataChangedEnd = m_limit - 1;
152+ } else { // currentCount < m_limit && currentCount + nSourceAddedItems > m_limit
153+ // We have less items than the limit but after adding them we will be over
154+ // To our users this means we need to insert some items and change the
155+ // data of some others, i.e we had something like
156+ // A B C
157+ // with a limit of 5
158+ // after inserting (let's say three 'F' at position 1) we will have
159+ // A F F F B
160+ // so we need to signal an insetion from position 1 to 2, instead of from
161+ // position 1 to 3 and a after that a data changed from 3 to 4
162+ const int nItemsToInsert = m_limit - currentCount;
163+ beginInsertRows(mapFromSource(parent), start, start + nItemsToInsert - 1);
164+ m_sourceInserting = true;
165+ m_dataChangedBegin = start + nItemsToInsert;
166+ m_dataChangedEnd = m_limit - 1;
167+ if (m_dataChangedBegin > m_dataChangedEnd) {
168+ // Just in case we were empty and insert 6 items with a limit of 5
169+ // We don't want to signal a dataChanged from 5 to 4
170+ m_dataChangedBegin = -1;
171+ m_dataChangedEnd = -1;
172+ }
173+ }
135174 }
136175}
137176
@@ -142,8 +181,45 @@ QLimitProxyModelQML::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int s
142181 beginRemoveRows(mapFromSource(parent), start, end);
143182 m_sourceRemoving = true;
144183 } else if (start < m_limit) {
145− beginRemoveRows(mapFromSource(parent), start, qMin(m_limit - 1, end));
146− m_sourceRemoving = true;
184+ const int nSourceRemovedItems = end - start + 1;
185+ const int currentCount = QIdentityProxyModel::rowCount();
186+ if (currentCount <= m_limit) {
187+ // We are already under the limit so
188+ // so just proceed with the removal normally
189+ beginRemoveRows(mapFromSource(parent), start, end);
190+ m_sourceRemoving = true;
191+ } else if (currentCount - nSourceRemovedItems >= m_limit) {
192+ // Even after removing items we will be at or over the limit
193+ // So to our users we are not removing anything, just changing the data
194+ // i.e. we had a internal model with
195+ // A B C D E F G H
196+ // and a limit of 5, our users just see
197+ // A B C D E
198+ // so if we remove 3 items starting at 1 we have to expose
199+ // A E F G H
200+ // that is, a dataChanged from 1 to 4
201+ m_dataChangedBegin = start;
202+ m_dataChangedEnd = m_limit - 1;
203+ } else { // currentCount > m_limit && currentCount - nSourceRemovedItems < m_limit
204+ // We have more items than the limit but after removing we will be below it
205+ // So to our users we both removing and changing the data
206+ // i.e. we had a internal model with
207+ // A B C D E F G
208+ // and a limit of 5, our users just see
209+ // A B C D E
210+ // so if we remove items from 1 to 3 we have to expose
211+ // A E F G
212+ // that is, a remove from 4 to 4 and a dataChanged from 1 to 3
213+ const int nItemsToRemove = m_limit - (currentCount - nSourceRemovedItems);
214+ beginRemoveRows(mapFromSource(parent), m_limit - nItemsToRemove, m_limit - 1);
215+ m_sourceRemoving = true;
216+ m_dataChangedBegin = start;
217+ m_dataChangedEnd = m_limit - nItemsToRemove - 1;
218+ if (m_dataChangedBegin > m_dataChangedEnd) {
219+ m_dataChangedBegin = -1;
220+ m_dataChangedEnd = -1;
221+ }
222+ }
147223 }
148224}
149225
@@ -154,6 +230,11 @@ QLimitProxyModelQML::sourceRowsInserted(const QModelIndex & /*parent*/, int /*st
154230 endInsertRows();
155231 m_sourceInserting = false;
156232 }
233+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
234+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
235+ m_dataChangedBegin = -1;
236+ m_dataChangedEnd = -1;
237+ }
157238}
158239
159240void
@@ -163,4 +244,9 @@ QLimitProxyModelQML::sourceRowsRemoved(const QModelIndex & /*parent*/, int /*sta
163244 endRemoveRows();
164245 m_sourceRemoving = false;
165246 }
247+ if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) {
248+ dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0));
249+ m_dataChangedBegin = -1;
250+ m_dataChangedEnd = -1;
251+ }
166252}
plugins/Utils/qlimitproxymodelqml.h+2
@@ -55,6 +55,8 @@ private:
5555 int m_limit;
5656 bool m_sourceInserting;
5757 bool m_sourceRemoving;
58+ int m_dataChangedBegin;
59+ int m_dataChangedEnd;
5860};
5961
6062#endif // QLIMITPROXYMODELQML_H
plugins/Utils/qsortfilterproxymodelqml.cpp+1−1
@@ -52,7 +52,6 @@ QSortFilterProxyModelQML::setModel(QAbstractItemModel *itemModel)
5252 connect(itemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
5353 connect(itemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(totalCountChanged()));
5454 Q_EMIT totalCountChanged();
55− Q_EMIT countChanged();
5655 Q_EMIT modelChanged();
5756 }
5857}
@@ -95,6 +94,7 @@ QSortFilterProxyModelQML::setInvertMatch(bool invertMatch)
9594 if (invertMatch != m_invertMatch) {
9695 m_invertMatch = invertMatch;
9796 Q_EMIT invertMatchChanged(invertMatch);
97+ invalidateFilter();
9898 }
9999}
100100
po/ar.po+42−6
@@ -7,15 +7,17 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-26 23:26+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"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
18+"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
1921
2022#: Applications/applications.js:23
2123msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "يوتيوب"
8991msgid "Recent searches"
9092msgstr "عمليات البحث الأخيرة"
9193
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92128#: Dash/DashApps.qml:65
93129msgid "Running apps"
94130msgstr "البرامج المُشغّلة"
@@ -153,15 +189,15 @@ msgstr "الشائع على الإنترنت"
153189msgid "Videos"
154190msgstr "فيديوهات"
155191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157193msgid "Directed by:"
158194msgstr "أخرجه:"
159195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161197msgid "Starring:"
162198msgstr "النجوم:"
163199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165201msgid "Author:"
166202msgstr "المؤلف:"
167203
po/az.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Son axtarışlar"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Videolar"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/bg.po+41−6
@@ -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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 00:06+0000\n"
1212"Last-Translator: Miroslav E. Hadzhiev (Хаджиев) "
1313"<miroslav.hadzhiev@gmail.com>\n"
@@ -15,8 +15,9 @@ msgstr ""
1515"MIME-Version: 1.0\n"
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
18−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
19−"X-Generator: Launchpad (build 16723)\n"
18+"Plural-Forms: nplurals=2; plural=n != 1;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
2021
2122#: Applications/applications.js:23
2223msgid "Phone"
@@ -90,6 +91,40 @@ msgstr "YouTube"
9091msgid "Recent searches"
9192msgstr "Скорошни търсения"
9293
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
93128#: Dash/DashApps.qml:65
94129msgid "Running apps"
95130msgstr ""
@@ -154,15 +189,15 @@ msgstr ""
154189msgid "Videos"
155190msgstr "Видеоклипове"
156191
157−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
158193msgid "Directed by:"
159194msgstr ""
160195
161−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
162197msgid "Starring:"
163198msgstr ""
164199
165−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
166201msgid "Author:"
167202msgstr "Автор:"
168203
po/ca.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 00:24+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Catalan <ca@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Cerques recents"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Aplications actives"
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Vídeos"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/de.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 00:37+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: German <de@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
8990msgid "Recent searches"
9091msgstr "Letzte Suchbegriffe"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Aktive Anwendungen"
@@ -153,15 +188,15 @@ msgstr "Online Beliebt"
153188msgid "Videos"
154189msgstr "Videos"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr "Regie:"
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr "Darsteller:"
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr "Autor:"
167202
po/el.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Πρόσφατες αναζητήσεις"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Βίντεο"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/en_AU.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 01:13+0000\n"
1212"Last-Translator: Jackson Doak <Doak.jackson@gmail.com>\n"
1313"Language-Team: English (Australia) <en_AU@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
8990msgid "Recent searches"
9091msgstr "Recent searches"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Running apps"
@@ -153,15 +188,15 @@ msgstr "Popular Online"
153188msgid "Videos"
154189msgstr "Videos"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr "Directed by:"
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr "Starring:"
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr "Author:"
167202
po/en_GB.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
8990msgid "Recent searches"
9091msgstr "Recent searches"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Running apps"
@@ -153,15 +188,15 @@ msgstr "Popular Online"
153188msgid "Videos"
154189msgstr "Videos"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr "Directed by:"
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr "Starring:"
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr "Author:"
167202
po/eo.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr ""
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr ""
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/es.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-07-08 07:25+0000\n"
1212"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
1313"Language-Team: Spanish <es@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
8990msgid "Recent searches"
9091msgstr "Búsquedas recientes"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Aplicaciones en ejecución"
@@ -153,15 +188,15 @@ msgstr "Populares en línea"
153188msgid "Videos"
154189msgstr "Vídeos"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr "Dirigida por:"
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr "Protagonistas:"
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr "Autor:"
167202
po/fi.po+44−9
@@ -7,15 +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-08-09 10:12+0200\n"
11−"PO-Revision-Date: 2013-08-12 17:45+0000\n"
12−"Last-Translator: Aleksi Kinnunen <Unknown>\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
11+"PO-Revision-Date: 2013-08-23 16:15+0000\n"
12+"Last-Translator: Timo Jyrinki <timo.jyrinki@canonical.com>\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"
17−"X-Launchpad-Export-Date: 2013-08-13 06:08+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-24 05:59+0000\n"
19+"X-Generator: Launchpad (build 16738)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
8990msgid "Recent searches"
9091msgstr "Viimeisimmät haut"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr "(%1)"
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] "%1 arvostelu"
105+msgstr[1] "%1 arvostelua"
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr "Arvioi tämä"
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr "Lisää arvostelu"
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr "Arvostelu"
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr "Lähetä"
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr "Kommentit:"
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Käynnissä olevat sovellukset"
@@ -153,15 +188,15 @@ msgstr "Suosittua verkossa"
153188msgid "Videos"
154189msgstr "Videot"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr "Ohjannut:"
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr "Pääosissa:"
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr "Tekijä:"
167202
@@ -209,4 +244,4 @@ msgstr "Hakuun täsmääviä tuloksia ei löydy."
209244#: Shell.qml:399
210245#, qt-format
211246msgid "Please enter %1"
212−msgstr ""
247+msgstr "Kirjoita %1"
po/fo.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 02:57+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Faroese <fo@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr ""
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Sjónbond"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/fr.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n > 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr ""
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Vidéos"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/gl.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 03:38+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Galician <gl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Buscas recentes"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Aplicativos en execución"
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Vídeos"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/he.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 03:54+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Hebrew <he@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "חיפושים אחרונים"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "סרטונים"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/hr.po+42−6
@@ -7,15 +7,17 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
1921
2022#: Applications/applications.js:23
2123msgid "Phone"
@@ -89,6 +91,40 @@ msgstr ""
8991msgid "Recent searches"
9092msgstr "Nedavne pretrage"
9193
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92128#: Dash/DashApps.qml:65
93129msgid "Running apps"
94130msgstr ""
@@ -153,15 +189,15 @@ msgstr ""
153189msgid "Videos"
154190msgstr "Video"
155191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157193msgid "Directed by:"
158194msgstr ""
159195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161197msgid "Starring:"
162198msgstr ""
163199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165201msgid "Author:"
166202msgstr ""
167203
po/hu.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Legutóbbi keresések"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Futó alkalmazások"
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Videók"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/hy.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Վերջին որոնումներ"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Բացած ծրագրեր"
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Վիդեոներ"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/id.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr ""
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr ""
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/it.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 05:14+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Italian <it@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Ricerche recenti"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Video"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/ja.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr ""
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr ""
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/kn.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "ಇತ್ತೀಚಿನ ಹುಡುಕಾಟಗಳು"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "ವೀಡಿಯೋಗಳು"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/ko.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 05:59+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Korean <ko@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "요즈음 찿은 것들"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "돌아가고 있는 앱"
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr ""
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/ky.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Соңку издөөлөр"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Видеолор"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/lv.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 06:31+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Latvian <lv@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Nesen veiktās meklēšanas"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Video"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/my.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 06:47+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Burmese <my@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "မကြာသေးခင်က ရှာဖွေခြင်း"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "ဗီဒီယိုများ"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/nb.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Siste søk"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Videoer"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/nl.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:05+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Recente zoekopdrachten"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Video's"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/pl.po+42−6
@@ -7,15 +7,17 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 08:52+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Polish <pl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
18+"|| n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
1921
2022#: Applications/applications.js:23
2123msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "YouTube"
8991msgid "Recent searches"
9092msgstr "Ostatnie wyszukiwania"
9193
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92128#: Dash/DashApps.qml:65
93129msgid "Running apps"
94130msgstr "Uruchomione aplikacje"
@@ -153,15 +189,15 @@ msgstr "Popularne w sieci"
153189msgid "Videos"
154190msgstr "Filmy"
155191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157193msgid "Directed by:"
158194msgstr "Reżyseria:"
159195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161197msgid "Starring:"
162198msgstr "Obsada:"
163199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165201msgid "Author:"
166202msgstr "Autor:"
167203
po/pt_BR.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 07:39+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n > 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "Pesquisas recentes"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "Vídeos"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/ro.po+42−6
@@ -7,15 +7,17 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
18+"== 0) && (n != 0))) ? 2: 1));\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
1921
2022#: Applications/applications.js:23
2123msgid "Phone"
@@ -89,6 +91,40 @@ msgstr ""
8991msgid "Recent searches"
9092msgstr "Căutări recente"
9193
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92128#: Dash/DashApps.qml:65
93129msgid "Running apps"
94130msgstr ""
@@ -153,15 +189,15 @@ msgstr ""
153189msgid "Videos"
154190msgstr "Video"
155191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157193msgid "Directed by:"
158194msgstr ""
159195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161197msgid "Starring:"
162198msgstr ""
163199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165201msgid "Author:"
166202msgstr ""
167203
po/ru.po+42−6
@@ -7,15 +7,17 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-28 06:40+0000\n"
1212"Last-Translator: Eugene Marshal <Unknown>\n"
1313"Language-Team: Russian <ru@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
1921
2022#: Applications/applications.js:23
2123msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "YouTube"
8991msgid "Recent searches"
9092msgstr "Последние поисковые запросы"
9193
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92128#: Dash/DashApps.qml:65
93129msgid "Running apps"
94130msgstr "Запущенные приложения"
@@ -153,15 +189,15 @@ msgstr ""
153189msgid "Videos"
154190msgstr "Видео"
155191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157193msgid "Directed by:"
158194msgstr "Режиссёр"
159195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161197msgid "Starring:"
162198msgstr "В главных ролях"
163199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165201msgid "Author:"
166202msgstr "Автор:"
167203
po/si.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr ""
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr ""
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/sl.po+42−6
@@ -7,15 +7,17 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-07-02 12:59+0000\n"
1212"Last-Translator: Damir Jerovšek <Unknown>\n"
1313"Language-Team: Slovenian <sl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
18+"n%100==4 ? 3 : 0);\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
1921
2022#: Applications/applications.js:23
2123msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "YouTube"
8991msgid "Recent searches"
9092msgstr "Nedavna iskanja"
9193
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92128#: Dash/DashApps.qml:65
93129msgid "Running apps"
94130msgstr ""
@@ -153,15 +189,15 @@ msgstr "Priljubljeno na spletu"
153189msgid "Videos"
154190msgstr "Videi"
155191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157193msgid "Directed by:"
158194msgstr ""
159195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161197msgid "Starring:"
162198msgstr ""
163199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165201msgid "Author:"
166202msgstr ""
167203
po/sr.po+42−6
@@ -7,15 +7,17 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
1921
2022#: Applications/applications.js:23
2123msgid "Phone"
@@ -89,6 +91,40 @@ msgstr ""
8991msgid "Recent searches"
9092msgstr "Скорашње претраге"
9193
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92128#: Dash/DashApps.qml:65
93129msgid "Running apps"
94130msgstr ""
@@ -153,15 +189,15 @@ msgstr ""
153189msgid "Videos"
154190msgstr "Видео"
155191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157193msgid "Directed by:"
158194msgstr ""
159195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161197msgid "Starring:"
162198msgstr ""
163199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165201msgid "Author:"
166202msgstr ""
167203
po/sv.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr ""
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr ""
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/te.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr ""
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr ""
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr ""
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
po/tr.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 10:08+0000\n"
1212"Last-Translator: Adar Cem EREN <adarcemeren34@gmail.com>\n"
1313"Language-Team: Turkish <tr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
8990msgid "Recent searches"
9091msgstr "Son Arananlar"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "Çalışan uygulamalar"
@@ -153,15 +188,15 @@ msgstr "Genellikle Çevrimiçi"
153188msgid "Videos"
154189msgstr "Videolar"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr "Yönetmen"
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr "Yazar:"
167202
po/ug.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 11:02+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Uyghur <ug@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
8990msgid "Recent searches"
9091msgstr "يېقىنقى ئىزدەشلەر"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "ئىجرا بولۇۋاتقان ئەپلەر"
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "سىنلار"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr "ئاپتور:"
167202
po/uk.po+42−6
@@ -7,15 +7,17 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2013-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-07-21 05:47+0000\n"
1212"Last-Translator: Ярослав Смук <Smuk.Slavko@yandex.ua>\n"
1313"Language-Team: Ukrainian <uk@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
20+"X-Generator: Launchpad (build 16734)\n"
1921
2022#: Applications/applications.js:23
2123msgid "Phone"
@@ -89,6 +91,40 @@ msgstr "YouTube"
8991msgid "Recent searches"
9092msgstr "Нещодавні пошуки"
9193
94+#. TRANSLATORS: Number of persons who rated this app
95+#: Dash/Apps/AppInfo.qml:72
96+#, qt-format
97+msgid "(%1)"
98+msgstr ""
99+
100+#. TRANSLATORS: Number of persons who wrote reviews for this app
101+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
102+#, qt-format
103+msgid "%1 review"
104+msgid_plural "%1 reviews"
105+msgstr[0] ""
106+msgstr[1] ""
107+
108+#: Dash/Apps/AppPreview.qml:181
109+msgid "Rate this"
110+msgstr ""
111+
112+#: Dash/Apps/AppReviews.qml:72
113+msgid "Add a review"
114+msgstr ""
115+
116+#: Dash/Apps/AppReviews.qml:84
117+msgid "Review"
118+msgstr ""
119+
120+#: Dash/Apps/AppReviews.qml:118
121+msgid "Send"
122+msgstr ""
123+
124+#: Dash/Apps/AppReviews.qml:136
125+msgid "Comments:"
126+msgstr ""
127+
92128#: Dash/DashApps.qml:65
93129msgid "Running apps"
94130msgstr "Запущені програми"
@@ -153,15 +189,15 @@ msgstr "Популярне у мережі"
153189msgid "Videos"
154190msgstr "Відео"
155191
156−#: Dash/Video/VideoPreview.qml:98
192+#: Dash/Video/VideoPreview.qml:130
157193msgid "Directed by:"
158194msgstr "Режисер:"
159195
160−#: Dash/Video/VideoPreview.qml:121
196+#: Dash/Video/VideoPreview.qml:153
161197msgid "Starring:"
162198msgstr "Актори:"
163199
164−#: Dash/Video/VideoPreview.qml:144
200+#: Dash/Video/VideoPreview.qml:176
165201msgid "Author:"
166202msgstr "Автор:"
167203
po/zh_CN.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\n"
1111"PO-Revision-Date: 2013-06-27 10:33+0000\n"
1212"Last-Translator: Anthony Wong <anthony.wong@canonical.com>\n"
1313"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr "YouTube"
8990msgid "Recent searches"
9091msgstr "近期搜索"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "正在运行的应用程序"
@@ -153,15 +188,15 @@ msgstr "经常在线"
153188msgid "Videos"
154189msgstr "视频"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr "导演:"
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr "主演:"
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr "作者:"
167202
po/zh_TW.po+41−6
@@ -7,15 +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-08-09 10:12+0200\n"
10+"POT-Creation-Date: 2013-08-09 12:18+0200\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"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2013-08-10 06:06+0000\n"
18−"X-Generator: Launchpad (build 16723)\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-08-22 06:11+0000\n"
19+"X-Generator: Launchpad (build 16734)\n"
1920
2021#: Applications/applications.js:23
2122msgid "Phone"
@@ -89,6 +90,40 @@ msgstr ""
8990msgid "Recent searches"
9091msgstr "最近搜尋"
9192
93+#. TRANSLATORS: Number of persons who rated this app
94+#: Dash/Apps/AppInfo.qml:72
95+#, qt-format
96+msgid "(%1)"
97+msgstr ""
98+
99+#. TRANSLATORS: Number of persons who wrote reviews for this app
100+#: Dash/Apps/AppInfo.qml:84 tests/qmltests/Dash/Apps/tst_AppPreview.qml:115
101+#, qt-format
102+msgid "%1 review"
103+msgid_plural "%1 reviews"
104+msgstr[0] ""
105+msgstr[1] ""
106+
107+#: Dash/Apps/AppPreview.qml:181
108+msgid "Rate this"
109+msgstr ""
110+
111+#: Dash/Apps/AppReviews.qml:72
112+msgid "Add a review"
113+msgstr ""
114+
115+#: Dash/Apps/AppReviews.qml:84
116+msgid "Review"
117+msgstr ""
118+
119+#: Dash/Apps/AppReviews.qml:118
120+msgid "Send"
121+msgstr ""
122+
123+#: Dash/Apps/AppReviews.qml:136
124+msgid "Comments:"
125+msgstr ""
126+
92127#: Dash/DashApps.qml:65
93128msgid "Running apps"
94129msgstr "執行中的程式"
@@ -153,15 +188,15 @@ msgstr ""
153188msgid "Videos"
154189msgstr "影片"
155190
156−#: Dash/Video/VideoPreview.qml:98
191+#: Dash/Video/VideoPreview.qml:130
157192msgid "Directed by:"
158193msgstr ""
159194
160−#: Dash/Video/VideoPreview.qml:121
195+#: Dash/Video/VideoPreview.qml:153
161196msgid "Starring:"
162197msgstr ""
163198
164−#: Dash/Video/VideoPreview.qml:144
199+#: Dash/Video/VideoPreview.qml:176
165200msgid "Author:"
166201msgstr ""
167202
src/Panel/Indicators/client/indicatorsclient.cpp+12
@@ -26,6 +26,8 @@
2626#include <QQmlEngine>
2727#include <QDebug>
2828
29+namespace {
30+
2931void prependImportPaths(QQmlEngine *engine, const QStringList &paths)
3032{
3133 QStringList importPathList = engine->importPathList();
@@ -51,11 +53,21 @@ void appendImportPaths(QQmlEngine *engine, const QStringList &paths)
5153 engine->setImportPathList(importPathList);
5254}
5355
56+void resolveIconTheme() {
57+ QString ubuntuIconTheme = getenv("UBUNTU_ICON_THEME");
58+ if (ubuntuIconTheme.isEmpty()) {
59+ QIcon::setThemeName("ubuntu-mobile");
60+ }
61+}
62+} // namespace
63+
5464IndicatorsClient::IndicatorsClient(int &argc, char **argv)
5565 : QObject(0),
5666 m_view(0)
5767{
5868 m_application = new QApplication(argc, argv);
69+ resolveIconTheme();
70+
5971 QStringList args = m_application->arguments();
6072
6173 m_view = new QQuickView;
src/Panel/Indicators/servicefiles/CMakeLists.txt−4
@@ -1,9 +1,5 @@
11file(GLOB INDICATOR_SERVICE_FILES
2− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.battery
32 ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.messages
4− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicator.time
5− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.settings.network
6− ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.settings.sound
73)
84
95# copy indicators files into build directory for shadow builds
src/Panel/Indicators/servicefiles/com.canonical.indicator.batterydeleted−10
@@ -1,10 +0,0 @@
1−[Indicator Service]
2−Name=indicator-battery
3−ObjectPath=/com/canonical/indicator/battery
4−Position=40
5−
6−[phone]
7−ObjectPath=/com/canonical/indicator/battery/phone
8−
9−[desktop]
10−ObjectPath=/com/canonical/indicator/battery/phone
src/Panel/Indicators/servicefiles/com.canonical.indicator.messages+1−1
@@ -1,5 +1,5 @@
11[Indicator Service]
2−Name=indicator-messaging
2+Name=indicator-messages
33ObjectPath=/com/canonical/indicator/messages
44Position=50
55
src/Panel/Indicators/servicefiles/com.canonical.indicator.timedeleted−10
@@ -1,10 +0,0 @@
1−[Indicator Service]
2−Name=indicator-time
3−ObjectPath=/com/canonical/indicator/time
4−Position=20
5−
6−[phone]
7−ObjectPath=/com/canonical/indicator/time/phone
8−
9−[desktop]
10−ObjectPath=/com/canonical/indicator/time/desktop
src/Panel/Indicators/servicefiles/com.canonical.settings.networkdeleted−10
@@ -1,10 +0,0 @@
1−[Indicator Service]
2−Name=indicator-network
3−ObjectPath=/com/canonical/settings/network
4−Position=69
5−
6−[phone]
7−ObjectPath=/com/canonical/settings/network/phone
8−
9−[desktop]
10−ObjectPath=/com/canonical/settings/network/desktop
src/Panel/Indicators/servicefiles/com.canonical.settings.sounddeleted−10
@@ -1,10 +0,0 @@
1−[Indicator Service]
2−Name=indicator-sound
3−ObjectPath=/com/canonical/settings/sound
4−Position=30
5−
6−[phone]
7−ObjectPath=/com/canonical/settings/sound/phone
8−
9−[desktop]
10−ObjectPath=/com/canonical/settings/sound/desktop
tests/autopilot/unity8/indicators_client/tests/test_battery.py+14−8
@@ -15,11 +15,21 @@ from unity8.indicators_client.tests import IndicatorsTestCase
1515from time import sleep
1616import random
1717import math
18+import unittest
1819
1920class TestDisplayMenus(IndicatorsTestCase):
2021 def setUp(self):
2122 super(TestDisplayMenus, self).setUp(self.geometry, self.grid_unit)
2223
24+ def tearDown(self):
25+ # self.main_window.get_indicators_client().reset()
26+ super(TestDisplayMenus, self).tearDown()
27+
28+ # START SECTION
29+ # We need to move these to emulators once autopilot supports emulators for
30+ # applications started with AutopilotTestCase.launch_test_application
31+
32+ def open_battery_menu(self):
2333 # This opens the messaging menu so you don't have to do that in
2434 # every test case
2535 self.assertThat(lambda: self.main_window.get_battery_menu(), Eventually(NotEquals(None)));
@@ -31,14 +41,6 @@ class TestDisplayMenus(IndicatorsTestCase):
3141 page_loader = fn_loader();
3242 self.assertThat(page_loader.progress, Eventually(Equals(1.0)))
3343
34− def tearDown(self):
35− # self.main_window.get_indicators_client().reset()
36− super(TestDisplayMenus, self).tearDown()
37−
38− # START SECTION
39− # We need to move these to emulators once autopilot supports emulators for
40− # applications started with AutopilotTestCase.launch_test_application
41−
4244 def switch_auto_brightness(self, auto_brightness):
4345 ab_switch = auto_brightness.select_single("CheckBox");
4446 self.assertThat(ab_switch, NotEquals(None))
@@ -65,8 +67,10 @@ class TestDisplayMenus(IndicatorsTestCase):
6567
6668 old_ab_value = brightness_slider.value
6769
70+ @unittest.skip("Battery indicator has been removed.")
6871 def test_auto_bright_switch(self):
6972 """Test the auto-bright switch"""
73+ self.open_battery_menu()
7074
7175 # wait for the switch menu item
7276 fn_auto_brightness = lambda: self.app.select_single("SwitchMenuItem", objectName="auto-brightness");
@@ -84,8 +88,10 @@ class TestDisplayMenus(IndicatorsTestCase):
8488 # TODO - should check backend when it's introspectable.
8589 self.assertThat(auto_brightness.checked, Eventually(NotEquals(old_ab_value)))
8690
91+ @unittest.skip("Battery indicator has been removed.")
8792 def test_brightness_slider(self):
8893 """Test the auto-bright switch"""
94+ self.open_battery_menu()
8995
9096 fn_brightness_menu = lambda: self.app.select_single("SliderMenuItem", objectName="brightness");
9197 # FIXME: this should go away when we switch to indicator-power
tests/autopilot/unity8/shell/emulators/create_interactive_notification.pynew file+102
@@ -0,0 +1,102 @@
1+#!/usr/bin/env python
2+
3+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4+#
5+# Unity Autopilot Test Suite
6+# Copyright (C) 2012-2013 Canonical
7+#
8+# This program is free software: you can redistribute it and/or modify
9+# it under the terms of the GNU General Public License as published by
10+# the Free Software Foundation, either version 3 of the License, or
11+# (at your option) any later version.
12+#
13+# This program is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+# GNU General Public License for more details.
17+#
18+# You should have received a copy of the GNU General Public License
19+# along with this program. If not, see <http://www.gnu.org/licenses/>.
20+#
21+
22+import argparse
23+from gi.repository import GLib, Notify
24+import signal
25+
26+
27+def action_callback(notification, action_id, data):
28+ print action_id
29+
30+
31+def quit_callback(notification):
32+ loop.quit()
33+
34+
35+if __name__ == '__main__':
36+ parser = argparse.ArgumentParser(
37+ description='Create an interactive notification'
38+ )
39+
40+ parser.add_argument(
41+ '--summary',
42+ help='summary text of the notification',
43+ default='Summary'
44+ )
45+ parser.add_argument(
46+ '--body',
47+ help='body text of the notification',
48+ default='Body'
49+ )
50+ parser.add_argument(
51+ '--icon',
52+ help='path to the icon to display',
53+ default=None
54+ )
55+ parser.add_argument(
56+ '--action',
57+ help='id and label for the callback in the format: id,label',
58+ action='append',
59+ default=[]
60+ )
61+ parser.add_argument(
62+ '--urgency',
63+ help='LOW, NORMAL, CRITICAL',
64+ choices=['LOW', 'NORMAL', 'CRITICAL'],
65+ default='NORMAL'
66+ )
67+ parser.add_argument(
68+ '--hints',
69+ help='list of comma sep options',
70+ action='append',
71+ default=[]
72+ )
73+
74+ args = parser.parse_args()
75+
76+ Notify.init('Interactive Notifications')
77+
78+ notification = Notify.Notification.new(args.summary, args.body, args.icon)
79+
80+ for hint in args.hints:
81+ key, value = hint.split(',', 1)
82+ notification.set_hint_string(key, value)
83+
84+ for action in args.action:
85+ action_id, action_label = action.split(',', 1)
86+ notification.add_action(
87+ action_id,
88+ action_label,
89+ action_callback,
90+ None,
91+ None
92+ )
93+
94+ def signal_handler(signam, frame):
95+ loop.quit()
96+
97+ signal.signal(signal.SIGINT, signal_handler)
98+
99+ loop = GLib.MainLoop.new(None, False)
100+ notification.connect('closed', quit_callback)
101+ notification.show()
102+ loop.run()
tests/autopilot/unity8/shell/emulators/dash.py+3−3
@@ -25,9 +25,9 @@ class Dash(UnityEmulatorBase):
2525 """An emulator that understands the Dash."""
2626
2727 def get_home_applications_grid(self):
28− return self.select_single(
29− "ApplicationsFilterGrid",
30− objectName="dashHomeApplicationsGrid"
28+ return self.get_scope('home').select_single(
29+ "GenericFilterGrid",
30+ objectName="applications.scope"
3131 )
3232
3333 def get_application_icon(self, text):
tests/autopilot/unity8/shell/tests/__init__.py+20−20
@@ -51,26 +51,25 @@ UNITYSHELL_GSETTINGS_PATH = "/org/compiz/profiles/unity/plugins/unityshell/"
5151UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
5252UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
5353
54−def _get_device_emulation_scenarios():
54+def _get_device_emulation_scenarios(devices='All'):
55+ nexus4 = ('Desktop Nexus 4',
56+ dict(app_width=768, app_height=1280, grid_unit_px=18))
57+ nexus10 = ('Desktop Nexus 10',
58+ dict(app_width=2560, app_height=1600, grid_unit_px=20))
59+ native = ('Native Device',
60+ dict(app_width=0, app_height=0, grid_unit_px=0))
61+
5562 if model() == 'Desktop':
56− return [
57− (
58− 'Desktop Nexus 4',
59− dict(app_width=768, app_height=1280, grid_unit_px=18)
60− ),
61− (
62− 'Desktop Nexus 10',
63− dict(app_width=2560, app_height=1600, grid_unit_px=20)
64− ),
65− ]
63+ if devices == 'All':
64+ return [nexus4, nexus10]
65+ elif devices == 'Nexus4':
66+ return [nexus4]
67+ elif devices == 'Nexus10':
68+ return [nexus10]
69+ else:
70+ raise RuntimeException('Unrecognized device-option "%s" passed.' % devices)
6671 else:
67− return [
68− (
69− 'Native Device',
70− dict(app_width=0, app_height=0, grid_unit_px=0)
71− )
72− ]
73−
72+ return [native]
7473
7574class UnityTestCase(AutopilotTestCase):
7675
@@ -148,7 +147,7 @@ class UnityTestCase(AutopilotTestCase):
148147 divisor = divisor * 2
149148 return divisor
150149
151− def launch_unity(self):
150+ def launch_unity(self, **kwargs):
152151 """Launch the unity shell, return a proxy object for it."""
153152 binary_path = get_binary_path()
154153 lib_path = get_lib_path()
@@ -167,7 +166,8 @@ class UnityTestCase(AutopilotTestCase):
167166 binary_path,
168167 *self.unity_geometry_args,
169168 app_type='qt',
170− emulator_base=UnityEmulatorBase
169+ emulator_base=UnityEmulatorBase,
170+ **kwargs
171171 )
172172 self._set_proxy(app_proxy)
173173
tests/autopilot/unity8/shell/tests/test_lock_screen.py+17−2
@@ -145,7 +145,9 @@ class TestLockscreen(UnityTestCase):
145145
146146 pinentryField = self.main_window.get_pinentryField()
147147 self.touch.tap_object(pinentryField)
148− self.keyboard.type(passphrase)
148+ self.assertThat(pinentryField.activeFocus, Eventually(Equals(True)))
149+ for character in passphrase:
150+ self._type_character(character, pinentryField)
149151 logger.debug("Typed passphrase: %s", pinentryField.text)
150152 self.keyboard.type("\n")
151153
@@ -162,6 +164,19 @@ class TestLockscreen(UnityTestCase):
162164
163165 prompt = self.main_window.get_greeter().get_prompt()
164166 self.touch.tap_object(prompt)
165− self.keyboard.type(passphrase)
167+ self.assertThat(prompt.activeFocus, Eventually(Equals(True)))
168+ for character in passphrase:
169+ self._type_character(character, prompt)
166170 logger.debug("Typed passphrase: %s", prompt.text)
167171 self.keyboard.type("\n")
172+
173+ def _type_character(self, character, prompt, retries=5):
174+ current_text = prompt.text
175+ self.keyboard.type(character)
176+ try:
177+ self.assertThat(prompt.text, Eventually(Equals(current_text + character)))
178+ except AssertionError:
179+ if retries > 0:
180+ self._type_character(character, prompt, retries-1)
181+ else:
182+ raise
tests/autopilot/unity8/shell/tests/test_notifications.pynew file+737
@@ -0,0 +1,737 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2012-2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Tests for Notifications"""
21+
22+from __future__ import absolute_import
23+
24+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
25+
26+from testtools.matchers import Equals, NotEquals
27+from autopilot.matchers import Eventually
28+
29+from gi.repository import Notify
30+import time
31+import os
32+import logging
33+import signal
34+import subprocess
35+
36+logger = logging.getLogger(__name__)
37+
38+
39+class NotificationsBase(UnityTestCase):
40+ """Base class for all notification tests that provides helper methods."""
41+
42+ scenarios = _get_device_emulation_scenarios('Nexus4')
43+
44+ def _get_icon_path(self, icon_name):
45+ """Given an icons file name returns the full path (either system or
46+ source tree.
47+
48+ Consider the graphics directory as root so for example (runnign tests
49+ from installed unity8-autopilot package):
50+ >>> self.get_icon_path('clock.png')
51+ /usr/share/unity8/graphics/clock.png
52+
53+ >>> self.get_icon_path('applicationIcons/facebook.png')
54+ /usr/share/unity8/graphics/applicationIcons/facebook.png
55+
56+ """
57+ if os.path.abspath(__file__).startswith('/usr/'):
58+ return '/usr/share/unity8/graphics/' + icon_name
59+ else:
60+ return os.path.abspath(
61+ os.getcwd() + "/../../graphics/" + icon_name
62+ )
63+
64+ def _get_notifications_list(self):
65+ main_view = self.main_window.get_qml_view()
66+ return main_view.select_single(
67+ "QQuickListView",
68+ objectName='notificationList'
69+ )
70+
71+ def _assert_notification(
72+ self,
73+ notification,
74+ summary=None,
75+ body=None,
76+ icon=True,
77+ secondary_icon=False,
78+ opacity=None
79+ ):
80+ """Assert that the expected qualities of a notification are as
81+ expected.
82+
83+ """
84+
85+ if summary is not None:
86+ self.assertThat(notification.summary, Eventually(Equals(summary)))
87+
88+ if body is not None:
89+ self.assertThat(notification.body, Eventually(Equals(body)))
90+
91+ if icon:
92+ self.assertThat(notification.iconSource, Eventually(NotEquals("")))
93+ else:
94+ self.assertThat(notification.iconSource, Eventually(Equals("")))
95+
96+ if secondary_icon:
97+ self.assertThat(
98+ notification.secondaryIconSource,
99+ Eventually(NotEquals(""))
100+ )
101+ else:
102+ self.assertThat(
103+ notification.secondaryIconSource,
104+ Eventually(Equals(""))
105+ )
106+
107+ if opacity is not None:
108+ self.assertThat(notification.opacity, Eventually(Equals(opacity)))
109+
110+
111+class InteractiveNotificationBase(NotificationsBase):
112+ """Collection of test for Interactive tests including snap decisions."""
113+
114+ def setUp(self):
115+ super(InteractiveNotificationBase, self).setUp()
116+ # Need to keep track when we launch the notification script.
117+ self._notify_proc = None
118+
119+ def test_interactive(self):
120+ """Interactive notification must react upon click on itself."""
121+ self.launch_unity()
122+ greeter = self.main_window.get_greeter()
123+ greeter.swipe()
124+
125+ notify_list = self._get_notifications_list()
126+
127+ summary = "Interactive notification"
128+ body = "This notification can be clicked on to trigger an action."
129+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
130+ actions = [("action_id", "dummy")]
131+ hints = [
132+ ("x-canonical-switch-to-application", "true"),
133+ (
134+ "x-canonical-secondary-icon",
135+ self._get_icon_path('applicationIcons/phone-app.png')
136+ )
137+ ]
138+
139+ self._create_interactive_notification(
140+ summary,
141+ body,
142+ icon_path,
143+ "NORMAL",
144+ actions,
145+ hints,
146+ )
147+
148+ get_notification = lambda: notify_list.select_single('Notification')
149+ self.assertThat(get_notification, Eventually(NotEquals(None)))
150+ notification = get_notification()
151+
152+ self.touch.tap_object(
153+ notification.select_single(objectName="interactiveArea")
154+ )
155+
156+ self.assert_notification_action_id_was_called('action_id')
157+
158+ def test_sd_incoming_call(self):
159+ """Rejecting a call should make notification expand and offer more options."""
160+ self.launch_unity()
161+ greeter = self.main_window.get_greeter()
162+ greeter.swipe()
163+
164+ notify_list = self._get_notifications_list()
165+
166+ summary = "Incoming call"
167+ body = "Frank Zappa\n+44 (0)7736 027340"
168+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
169+ hints = [
170+ (
171+ "x-canonical-secondary-icon",
172+ self._get_icon_path('applicationIcons/phone-app.png')
173+ ),
174+ ("x-canonical-snap-decisions", "true"),
175+ ]
176+
177+ actions = [
178+ ('action_accept', 'Accept'),
179+ ('action_decline_1', 'Decline'),
180+ ('action_decline_2', '"Can\'t talk now, what\'s up?"'),
181+ ('action_decline_3', '"I call you back."'),
182+ ('action_decline_4', 'Send custom message...'),
183+ ]
184+
185+ self._create_interactive_notification(
186+ summary,
187+ body,
188+ icon_path,
189+ "NORMAL",
190+ actions,
191+ hints
192+ )
193+
194+ get_notification = lambda: notify_list.select_single('Notification')
195+ self.assertThat(get_notification, Eventually(NotEquals(None)))
196+ notification = get_notification()
197+ self._assert_notification(notification, None, None, True, True, 1.0)
198+ initial_height = notification.height
199+ self.touch.tap_object(notification.select_single(objectName="button1"))
200+ self.assertThat(
201+ notification.height,
202+ Eventually(Equals(initial_height +
203+ 3 * notification.select_single(objectName="buttonColumn").spacing +
204+ 3 * notification.select_single(objectName="button4").height)))
205+ self.touch.tap_object(notification.select_single(objectName="button4"))
206+ self.assert_notification_action_id_was_called("action_decline_4")
207+
208+ def _create_interactive_notification(
209+ self,
210+ summary="",
211+ body="",
212+ icon=None,
213+ urgency="NORMAL",
214+ actions=[],
215+ hints=[]
216+ ):
217+ """Create a interactive notification command.
218+
219+ :param summary: Summary text for the notification
220+ :param body: Body text to display in the notification
221+ :param icon: Path string to the icon to use
222+ :param urgency: Urgency string for the noticiation, either: 'LOW',
223+ 'NORMAL', 'CRITICAL'
224+ :param actions: List of tuples containing the 'id' and 'label' for all
225+ the actions to add
226+ :param hint_strings: List of tuples containing the 'name' and value for
227+ setting the hint strings for the notification
228+
229+ """
230+
231+ logger.info(
232+ "Creating snap-decision notification with summary(%s), body(%s) "
233+ "and urgency(%r)",
234+ summary,
235+ body,
236+ urgency
237+ )
238+
239+ script_args = [
240+ '--summary', summary,
241+ '--body', body,
242+ '--urgency', urgency
243+ ]
244+
245+ if icon is not None:
246+ script_args.extend(['--icon', icon])
247+
248+ for hint in hints:
249+ key, value = hint
250+ script_args.extend(['--hint', "%s,%s" % (key, value)])
251+
252+ for action in actions:
253+ action_id, action_label = action
254+ action_string = "%s,%s" % (action_id, action_label)
255+ script_args.extend(['--action', action_string])
256+
257+ python_bin = subprocess.check_output(['which', 'python']).strip()
258+ command = [python_bin, self._get_notify_script()] + script_args
259+ logger.info("Launching snap-decision notification as: %s", command)
260+ self._notify_proc = subprocess.Popen(
261+ command,
262+ stdin=subprocess.PIPE,
263+ stdout=subprocess.PIPE,
264+ stderr=subprocess.PIPE,
265+ close_fds=True,
266+ )
267+
268+ self.addCleanup(self._tidy_up_script_process)
269+
270+ poll_result = self._notify_proc.poll()
271+ if poll_result is not None and self._notify_proc.returncode != 0:
272+ error_output = self._notify_proc.communicate()[1]
273+ raise RuntimeError("Call to script failed with: %s" % error_output)
274+
275+ def _get_notify_script(self):
276+ """Returns the path to the interactive notification creation script."""
277+ file_path = "../../emulators/create_interactive_notification.py"
278+
279+ the_path = os.path.abspath(
280+ os.path.join(__file__, file_path))
281+
282+ return the_path
283+
284+ def _tidy_up_script_process(self):
285+ if self._notify_proc is not None and self._notify_proc.poll() is None:
286+ logger.error("Notification process wasn't killed, killing now.")
287+ os.killpg(self._notify_proc.pid, signal.SIGTERM)
288+
289+ def assert_notification_action_id_was_called(self, action_id, timeout=10):
290+ """Assert that the interactive notification callback of id *action_id*
291+ was called.
292+
293+ :raises AssertionError: If no interactive notification has actually
294+ been created.
295+ :raises AssertionError: When *action_id* does not match the actual
296+ returned.
297+ :raises AssertionError: If no callback was called at all.
298+ """
299+
300+ if self._notify_proc is None:
301+ raise AssertionError("No interactive notification was created.")
302+
303+ for i in xrange(timeout):
304+ self._notify_proc.poll()
305+ if self._notify_proc.returncode is not None:
306+ output = self._notify_proc.communicate()
307+ actual_action_id = output[0].strip("\n")
308+ if actual_action_id != action_id:
309+ raise AssertionError(
310+ "action id '%s' does not match actual returned '%s'"
311+ % (action_id, actual_action_id)
312+ )
313+ else:
314+ return
315+ time.sleep(1)
316+
317+ os.killpg(self._notify_proc.pid, signal.SIGTERM)
318+ self._notify_proc = None
319+ raise AssertionError(
320+ "No callback was called, killing interactivenotification script"
321+ )
322+
323+
324+class EphemeralNotificationsTests(NotificationsBase):
325+ """Collection of tests for Emphemeral notifications (non-interactive.)"""
326+
327+ def setUp(self):
328+ super(EphemeralNotificationsTests, self).setUp()
329+ # Because we are using the Notify library we need to init and un-init
330+ # otherwise we get crashes.
331+ Notify.init("Autopilot Ephemeral Notification Tests")
332+ self.addCleanup(Notify.uninit)
333+
334+ def test_icon_summary_body(self):
335+ """Notification must display the expected summary and body text."""
336+ self.launch_unity()
337+ greeter = self.main_window.get_greeter()
338+ greeter.swipe()
339+
340+ notify_list = self._get_notifications_list()
341+
342+ summary = "Icon-Summary-Body"
343+ body = "Hey pal, what's up with the party next weekend? Will you " \
344+ "join me and Anna?"
345+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
346+ hints = [
347+ (
348+ "x-canonical-secondary-icon",
349+ self._get_icon_path('applicationIcons/phone-app.png')
350+ )
351+ ]
352+
353+ notification = self._create_ephemeral_notification(
354+ summary,
355+ body,
356+ icon_path,
357+ hints,
358+ "NORMAL",
359+ )
360+
361+ notification.show()
362+
363+ get_notification = lambda: notify_list.select_single('Notification')
364+ self.assertThat(get_notification, Eventually(NotEquals(None)))
365+ notification = get_notification()
366+ self._assert_notification(notification, summary, body, True, True, 1.0)
367+
368+ def test_icon_summary(self):
369+ """Notification must display the expected summary and secondary icon."""
370+ self.launch_unity()
371+ greeter = self.main_window.get_greeter()
372+ greeter.swipe()
373+
374+ notify_list = self._get_notifications_list()
375+
376+ summary = "Upload of image completed"
377+ hints = [
378+ (
379+ "x-canonical-secondary-icon",
380+ self._get_icon_path('applicationIcons/facebook.png')
381+ )
382+ ]
383+
384+ notification = self._create_ephemeral_notification(
385+ summary,
386+ None,
387+ None,
388+ hints,
389+ "NORMAL",
390+ )
391+
392+ notification.show()
393+
394+ get_notification = lambda: notify_list.select_single('Notification')
395+ self.assertThat(get_notification, Eventually(NotEquals(None)))
396+ notification = get_notification()
397+ self._assert_notification(
398+ notification,
399+ summary,
400+ None,
401+ False,
402+ True,
403+ 1.0
404+ )
405+
406+ def test_urgency_order(self):
407+ """Notifications must be displayed in order according to their urgency."""
408+ self.launch_unity()
409+ greeter = self.main_window.get_greeter()
410+ greeter.swipe()
411+
412+ notify_list = self._get_notifications_list()
413+
414+ summary_low = 'Low Urgency'
415+ body_low = "No, I'd rather see paint dry, pal *yawn*"
416+ icon_path_low = self._get_icon_path('avatars/amanda.png')
417+
418+ summary_normal = 'Normal Urgency'
419+ body_normal = "Hey pal, what's up with the party next weekend? Will " \
420+ "you join me and Anna?"
421+ icon_path_normal = self._get_icon_path('avatars/funky.png')
422+
423+ summary_critical = 'Critical Urgency'
424+ body_critical = 'Dude, this is so urgent you have no idea :)'
425+ icon_path_critical = self._get_icon_path('avatars/anna_olsson.png')
426+
427+ notification_normal = self._create_ephemeral_notification(
428+ summary_normal,
429+ body_normal,
430+ icon_path_normal,
431+ urgency="NORMAL"
432+ )
433+ notification_normal.show()
434+
435+ notification_low = self._create_ephemeral_notification(
436+ summary_low,
437+ body_low,
438+ icon_path_low,
439+ urgency="LOW"
440+ )
441+ notification_low.show()
442+
443+ notification_critical = self._create_ephemeral_notification(
444+ summary_critical,
445+ body_critical,
446+ icon_path_critical,
447+ urgency="CRITICAL"
448+ )
449+ notification_critical.show()
450+
451+ get_notification = lambda: notify_list.select_single(
452+ 'Notification',
453+ summary=summary_critical
454+ )
455+ self.assertThat(get_notification, Eventually(NotEquals(None)))
456+
457+ notification = get_notification()
458+ self._assert_notification(
459+ notification,
460+ summary_critical,
461+ body_critical,
462+ True,
463+ False,
464+ 1.0
465+ )
466+
467+ get_normal_notification = lambda: notify_list.select_single(
468+ 'Notification',
469+ summary=summary_normal
470+ )
471+ self.assertThat(get_normal_notification, Eventually(NotEquals(None)))
472+ notification = get_normal_notification()
473+ self._assert_notification(
474+ notification,
475+ summary_normal,
476+ body_normal,
477+ True,
478+ False,
479+ 1.0
480+ )
481+
482+ get_low_notification = lambda: notify_list.select_single(
483+ 'Notification',
484+ summary=summary_low
485+ )
486+ self.assertThat(get_low_notification, Eventually(NotEquals(None)))
487+ notification = get_low_notification()
488+ self._assert_notification(
489+ notification,
490+ summary_low,
491+ body_low,
492+ True,
493+ False,
494+ 1.0
495+ )
496+
497+ def test_summary_and_body(self):
498+ """Notification must display the expected summary- and body-text."""
499+ self.launch_unity()
500+ greeter = self.main_window.get_greeter()
501+ greeter.swipe()
502+
503+ notify_list = self._get_notifications_list()
504+
505+ summary = 'Summary-Body'
506+ body = 'This is a superfluous notification'
507+
508+ notification = self._create_ephemeral_notification(summary, body)
509+ notification.show()
510+
511+ get_notification = lambda: notify_list.select_single('Notification')
512+ self.assertThat(get_notification, Eventually(NotEquals(None)))
513+ notification = get_notification()
514+ self._assert_notification(
515+ notification,
516+ summary,
517+ body,
518+ False,
519+ False,
520+ 1.0
521+ )
522+
523+ def test_summary_only(self):
524+ """Notification must display only the expected summary-text."""
525+ self.launch_unity()
526+ greeter = self.main_window.get_greeter()
527+ greeter.swipe()
528+
529+ notify_list = self._get_notifications_list()
530+
531+ summary = 'Summary-Only'
532+
533+ notification = self._create_ephemeral_notification(summary)
534+ notification.show()
535+
536+ get_notification = lambda: notify_list.select_single('Notification')
537+ self.assertThat(get_notification, Eventually(NotEquals(None)))
538+ notification = get_notification()
539+
540+ self._assert_notification(notification, summary, '', False, False, 1.0)
541+
542+ def test_update_notification_same_layout(self):
543+ """Notification must allow updating its contents while being displayed."""
544+ self.launch_unity()
545+ greeter = self.main_window.get_greeter()
546+ greeter.swipe()
547+
548+ notify_list = self._get_notifications_list()
549+
550+ summary = 'Initial notification'
551+ body = 'This is the original content of this notification-bubble.'
552+ icon_path = self._get_icon_path('avatars/funky.png')
553+
554+ notification = self._create_ephemeral_notification(
555+ summary,
556+ body,
557+ icon_path
558+ )
559+ notification.show()
560+
561+ get_notification = lambda: notify_list.select_single('Notification', summary=summary)
562+ self.assertThat(get_notification, Eventually(NotEquals(None)))
563+ self._assert_notification(
564+ get_notification(),
565+ summary,
566+ body,
567+ True,
568+ False,
569+ 1.0
570+ )
571+
572+ summary = 'Updated notification'
573+ body = 'Here the same bubble with new title- and body-text, even ' \
574+ 'the icon can be changed on the update.'
575+ icon_path = self._get_icon_path('avatars/amanda.png')
576+ notification.update(summary, body, icon_path)
577+ notification.show()
578+ self.assertThat(get_notification, Eventually(NotEquals(None)))
579+ self._assert_notification(get_notification(), summary, body)
580+
581+ def test_update_notification_layout_change(self):
582+ """Notification must allow updating its contents and layout while being displayed."""
583+ self.launch_unity()
584+ greeter = self.main_window.get_greeter()
585+ greeter.swipe()
586+
587+ notify_list = self._get_notifications_list()
588+
589+ summary = 'Initial layout'
590+ body = 'This bubble uses the icon-title-body layout with a ' \
591+ 'secondary icon.'
592+ icon_path = self._get_icon_path('avatars/anna_olsson.png')
593+ hint_icon = self._get_icon_path('applicationIcons/phone-app.png')
594+
595+ notification = self._create_ephemeral_notification(
596+ summary,
597+ body,
598+ icon_path
599+ )
600+ notification.set_hint_string(
601+ 'x-canonical-secondary-icon',
602+ hint_icon
603+ )
604+ notification.show()
605+
606+ get_notification = lambda: notify_list.select_single('Notification')
607+ self.assertThat(get_notification, Eventually(NotEquals(None)))
608+ self._assert_notification(
609+ get_notification(),
610+ summary,
611+ body,
612+ True,
613+ True,
614+ 1.0
615+ )
616+
617+ notification.clear_hints()
618+ summary = 'Updated layout'
619+ body = 'After the update we now have a bubble using the title-body ' \
620+ 'layout.'
621+ notification.update(summary, body, None)
622+ notification.show()
623+
624+ self.assertThat(get_notification, Eventually(NotEquals(None)))
625+ self._assert_notification(get_notification(), summary, body, False)
626+
627+ def test_append_hint(self):
628+ """Notification has to accumulate body-text using append-hint."""
629+ self.launch_unity()
630+ greeter = self.main_window.get_greeter()
631+ greeter.swipe()
632+
633+ notify_list = self._get_notifications_list()
634+
635+ summary = 'Cole Raby'
636+ body = 'Hey Bro Coly!'
637+ icon_path = self._get_icon_path('avatars/amanda.png')
638+ body_sum = body
639+ notification = self._create_ephemeral_notification(
640+ summary,
641+ body,
642+ icon_path,
643+ hints=[('x-canonical-append', 'true')]
644+ )
645+
646+ notification.show()
647+
648+ get_notification = lambda: notify_list.select_single('Notification')
649+ self.assertThat(get_notification, Eventually(NotEquals(None)))
650+ notification = get_notification()
651+ self._assert_notification(
652+ notification,
653+ summary,
654+ body_sum,
655+ True,
656+ False,
657+ 1.0
658+ )
659+
660+ bodies = [
661+ 'What\'s up dude?',
662+ 'Did you watch the air-race in Oshkosh last week?',
663+ 'Phil owned the place like no one before him!',
664+ 'Did really everything in the race work according to regulations?',
665+ 'Somehow I think to remember Burt Williams did cut corners and was not punished for this.',
666+ 'Hopefully the referees will watch the videos of the race.',
667+ 'Burt could get fined with US$ 50000 for that rule-violation :)'
668+ ]
669+
670+ for new_body in bodies:
671+ body = new_body
672+ body_sum += '\n' + body
673+ notification = self._create_ephemeral_notification(
674+ summary,
675+ body,
676+ icon_path,
677+ hints=[('x-canonical-append', 'true')]
678+ )
679+ notification.show()
680+ get_notification = lambda: notify_list.select_single(
681+ 'Notification'
682+ )
683+ self.assertThat(get_notification, Eventually(NotEquals(None)))
684+ notification = get_notification()
685+ self._assert_notification(
686+ notification,
687+ summary,
688+ body_sum,
689+ True,
690+ False,
691+ 1.0
692+ )
693+
694+ def _create_ephemeral_notification(
695+ self,
696+ summary="",
697+ body="",
698+ icon=None,
699+ hints=[],
700+ urgency="NORMAL"
701+ ):
702+ """Create an ephemeral (non-interactive) notification
703+
704+ :param summary: Summary text for the notification
705+ :param body: Body text to display in the notification
706+ :param icon: Path string to the icon to use
707+ :param hint_strings: List of tuples containing the 'name' and value
708+ for setting the hint strings for the notification
709+ :param urgency: Urgency string for the noticiation, either: 'LOW',
710+ 'NORMAL', 'CRITICAL'
711+
712+ """
713+ logger.info(
714+ "Creating ephemeral: summary(%s), body(%s), urgency(%r) "
715+ "and Icon(%s)",
716+ summary,
717+ body,
718+ urgency,
719+ icon
720+ )
721+
722+ n = Notify.Notification.new(summary, body, icon)
723+
724+ for hint in hints:
725+ key, value = hint
726+ n.set_hint_string(key, value)
727+ logger.info("Adding hint to notification: (%s, %s)", key, value)
728+ n.set_urgency(self._get_urgency(urgency))
729+
730+ return n
731+
732+ def _get_urgency(self, urgency):
733+ """Translates urgency string to enum."""
734+ _urgency_enums = {'LOW': Notify.Urgency.LOW,
735+ 'NORMAL': Notify.Urgency.NORMAL,
736+ 'CRITICAL': Notify.Urgency.CRITICAL}
737+ return _urgency_enums.get(urgency.upper())
tests/data/unity/backgrounds/blue.pngnew file

Binary file not shown.

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

Binary file not shown.

tests/mocks/CMakeLists.txt+3−1
@@ -1,6 +1,8 @@
1+add_subdirectory(GSettings.1.0)
2+add_subdirectory(HudClient)
13add_subdirectory(libusermetrics)
24add_subdirectory(LightDM)
5+add_subdirectory(Powerd)
36add_subdirectory(QMenuModel)
47add_subdirectory(Ubuntu)
58add_subdirectory(Unity)
6−add_subdirectory(HudClient)
tests/mocks/GSettings.1.0/CMakeLists.txtnew file+23
@@ -0,0 +1,23 @@
1+include_directories(
2+ ${CMAKE_CURRENT_BINARY_DIR}
3+ ${Qt5Core_INCLUDE_DIRS}
4+ ${Qt5Quick_INCLUDE_DIRS}
5+)
6+
7+set(GSettingsQML_SOURCES
8+ fake_gsettings.cpp
9+ plugin.cpp
10+)
11+
12+add_library(FakeGSettingsQml MODULE ${GSettingsQML_SOURCES})
13+target_link_libraries(FakeGSettingsQml
14+ ${Qt5Core_LIBRARIES}
15+ ${Qt5Quick_LIBRARIES}
16+)
17+
18+qt5_use_modules(FakeGSettingsQml Qml)
19+
20+# copy qmldir file into build directory for shadow builds
21+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
22+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
23+ )
tests/mocks/GSettings.1.0/fake_gsettings.cppnew file+102
@@ -0,0 +1,102 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "fake_gsettings.h"
18+
19+#include <QList>
20+
21+GSettingsControllerQml* GSettingsControllerQml::s_controllerInstance = 0;
22+
23+GSettingsControllerQml::GSettingsControllerQml() {
24+}
25+
26+GSettingsControllerQml::~GSettingsControllerQml() {
27+ s_controllerInstance = 0;
28+}
29+
30+GSettingsControllerQml* GSettingsControllerQml::instance() {
31+ if (!s_controllerInstance) {
32+ s_controllerInstance = new GSettingsControllerQml();
33+ }
34+ return s_controllerInstance;
35+}
36+
37+void GSettingsControllerQml::registerSettingsObject(GSettingsQml *obj) {
38+ m_registeredGSettings.append(obj);
39+}
40+
41+void GSettingsControllerQml::unRegisterSettingsObject(GSettingsQml *obj) {
42+ m_registeredGSettings.removeOne(obj);
43+}
44+
45+void GSettingsControllerQml::setPictureUri(const QString &str) {
46+ Q_FOREACH (GSettingsQml *obj, m_registeredGSettings) {
47+ obj->setPictureUri(str);
48+ }
49+}
50+
51+GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {
52+}
53+
54+QByteArray GSettingsSchemaQml::id() const {
55+ return m_id;
56+}
57+
58+void GSettingsSchemaQml::setId(const QByteArray &id) {
59+ if (m_id.isEmpty()) {
60+ qWarning("GSettings.schema.id may only be set on construction");
61+ return;
62+ }
63+
64+ m_id = id;
65+}
66+
67+QByteArray GSettingsSchemaQml::path() const {
68+ return m_path;
69+}
70+
71+void GSettingsSchemaQml::setPath(const QByteArray &path) {
72+ if (m_path.isEmpty()) {
73+ qWarning("GSettings.schema.path may only be set on construction");
74+ return;
75+ }
76+
77+ m_path = path;
78+}
79+
80+GSettingsQml::GSettingsQml(QObject *parent): QObject(parent) {
81+ m_schema = new GSettingsSchemaQml(this);
82+ GSettingsControllerQml::instance()->registerSettingsObject(this);
83+}
84+
85+GSettingsQml::~GSettingsQml() {
86+ GSettingsControllerQml::instance()->unRegisterSettingsObject(this);
87+}
88+
89+GSettingsSchemaQml * GSettingsQml::schema() const {
90+ return m_schema;
91+}
92+
93+QString GSettingsQml::pictureUri() const {
94+ return m_pictureUri;
95+}
96+
97+void GSettingsQml::setPictureUri(const QString &str) {
98+ if (str != m_pictureUri) {
99+ m_pictureUri = str;
100+ Q_EMIT pictureUriChanged(m_pictureUri);
101+ }
102+}
tests/mocks/GSettings.1.0/fake_gsettings.hnew file+90
@@ -0,0 +1,90 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FAKE_GSETTINGS_H
18+#define FAKE_GSETTINGS_H
19+
20+#include <QList>
21+#include <QObject>
22+
23+class GSettingsSchemaQml: public QObject
24+{
25+ Q_OBJECT
26+
27+ Q_PROPERTY(QByteArray id READ id WRITE setId)
28+
29+public:
30+ GSettingsSchemaQml(QObject *parent = NULL);
31+
32+ QByteArray id() const;
33+ void setId(const QByteArray &id);
34+
35+ QByteArray path() const;
36+ void setPath(const QByteArray &path);
37+
38+private:
39+ QByteArray m_id;
40+ QByteArray m_path;
41+};
42+
43+class GSettingsQml: public QObject
44+{
45+ Q_OBJECT
46+
47+ Q_PROPERTY(GSettingsSchemaQml* schema READ schema NOTIFY schemaChanged)
48+ Q_PROPERTY(QString pictureUri READ pictureUri WRITE setPictureUri NOTIFY pictureUriChanged)
49+
50+public:
51+ GSettingsQml(QObject *parent = NULL);
52+ ~GSettingsQml();
53+
54+ GSettingsSchemaQml * schema() const;
55+ QString pictureUri() const;
56+
57+ void setPictureUri(const QString &str);
58+
59+Q_SIGNALS:
60+ void schemaChanged();
61+ void pictureUriChanged(const QString&);
62+
63+private:
64+ GSettingsSchemaQml* m_schema;
65+
66+ QString m_pictureUri;
67+
68+ friend class GSettingsSchemaQml;
69+};
70+
71+class GSettingsControllerQml: public QObject
72+{
73+ Q_OBJECT
74+
75+public:
76+ static GSettingsControllerQml* instance();
77+ ~GSettingsControllerQml();
78+
79+ void registerSettingsObject(GSettingsQml *obj);
80+ void unRegisterSettingsObject(GSettingsQml *obj);
81+ Q_INVOKABLE void setPictureUri(const QString &str);
82+
83+private:
84+ GSettingsControllerQml();
85+
86+ static GSettingsControllerQml* s_controllerInstance;
87+ QList<GSettingsQml *> m_registeredGSettings;
88+};
89+
90+#endif // FAKE_GSETTINGS_H
tests/mocks/GSettings.1.0/plugin.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "plugin.h"
18+#include "fake_gsettings.h"
19+
20+#include <QtQml/qqml.h>
21+
22+static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
23+{
24+ return GSettingsControllerQml::instance();
25+}
26+
27+void FakeGSettingsQmlPlugin::registerTypes(const char *uri)
28+{
29+ qmlRegisterSingletonType<GSettingsControllerQml>(uri, 1, 0, "GSettingsController", controllerProvider);
30+ qmlRegisterType<GSettingsQml>(uri, 1, 0, "GSettings");
31+ qmlRegisterUncreatableType<GSettingsSchemaQml>(uri, 1, 0, "GSettingsSchema",
32+ "GSettingsSchema can only be used inside of a GSettings component");
33+}
tests/mocks/GSettings.1.0/plugin.hnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef PLUGIN_H
18+#define PLUGIN_H
19+
20+#include <QtQml/QQmlExtensionPlugin>
21+
22+class FakeGSettingsQmlPlugin : public QQmlExtensionPlugin
23+{
24+ Q_OBJECT
25+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26+
27+public:
28+ void registerTypes(const char *uri);
29+};
30+
31+#endif // PLUGIN_H
tests/mocks/GSettings.1.0/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module GSettings
2+plugin FakeGSettingsQml
tests/mocks/Powerd/CMakeLists.txtnew file+21
@@ -0,0 +1,21 @@
1+add_definitions(-DSM_BUSNAME=sessionBus)
2+
3+add_library(MockPowerd-qml MODULE
4+ plugin.cpp
5+ Powerd.cpp
6+ )
7+
8+qt5_use_modules(MockPowerd-qml Qml)
9+
10+# copy qmldir file into build directory for shadow builds
11+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
12+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
13+ )
14+
15+install(TARGETS MockPowerd-qml
16+ DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Powerd
17+ )
18+
19+install(FILES qmldir
20+ DESTINATION ${SHELL_PRIVATE_LIBDIR}/qml/mocks/Powerd
21+ )
tests/mocks/Powerd/Powerd.cppnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "Powerd.h"
20+
21+Powerd::Powerd(QObject* parent)
22+ : QObject(parent)
23+{
24+}
tests/mocks/Powerd/Powerd.hnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef UNITY_MOCK_POWERD_H
20+#define UNITY_MOCK_POWERD_H
21+
22+#include <QtCore/QObject>
23+
24+class Powerd: public QObject
25+{
26+ Q_OBJECT
27+ Q_ENUMS(Status)
28+ Q_FLAGS(DisplayFlag DisplayFlags)
29+
30+public:
31+ enum DisplayFlag {
32+ UseProximity = 1, // Use proximity sensor to override screen state
33+ DisableAutoBrightness = 2, // Force autobrightness to be disabled
34+ Bright = 4, // Request the screen to stay bright
35+ };
36+ Q_DECLARE_FLAGS(DisplayFlags, DisplayFlag)
37+
38+ enum Status {
39+ Off,
40+ On,
41+ };
42+
43+ explicit Powerd(QObject *parent = 0);
44+
45+Q_SIGNALS:
46+ void displayPowerStateChange(int status, unsigned int flags);
47+};
48+
49+#endif
tests/mocks/Powerd/plugin.cppnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#include "plugin.h"
20+#include "Powerd.h"
21+
22+#include <QtQml/qqml.h>
23+
24+static QObject *powerd_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25+{
26+ Q_UNUSED(engine)
27+ Q_UNUSED(scriptEngine)
28+ return new Powerd();
29+}
30+
31+void PowerdPlugin::registerTypes(const char *uri)
32+{
33+ Q_ASSERT(uri == QLatin1String("Powerd"));
34+ qmlRegisterSingletonType<Powerd>(uri, 0, 1, "Powerd", powerd_provider);
35+}
tests/mocks/Powerd/plugin.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef MOCK_POWER_PLUGIN_H
20+#define MOCK_POWER_PLUGIN_H
21+
22+#include <QtQml/QQmlEngine>
23+#include <QtQml/QQmlExtensionPlugin>
24+
25+class PowerdPlugin : public QQmlExtensionPlugin
26+{
27+ Q_OBJECT
28+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29+
30+public:
31+ void registerTypes(const char *uri);
32+};
33+
34+#endif
tests/mocks/Powerd/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module Powerd
2+plugin MockPowerd-qml
tests/mocks/Ubuntu/DownloadDaemonListener/plugin.cpp−1
@@ -17,7 +17,6 @@
1717#include "plugin.h"
1818#include "MockDownloadTracker.h"
1919
20−#include <QtQml>
2120#include <QtQml/QQmlContext>
2221#include <qqml.h>
2322
tests/mocks/Unity/Indicators/IndicatorsModel.qml+1−1
@@ -64,7 +64,7 @@ ListModel {
6464 isValid: "";
6565 }
6666 ListElement {
67− identifier: "indicator-fake4";
67+ identifier: "indicator-fake5";
6868 priority: 4
6969 title: "Menu5";
7070 description: "";
tests/mocks/Unity/Indicators/fakeplugin.cpp+1−1
@@ -17,7 +17,7 @@
1717 */
1818
1919// Qt
20−#include <QtQml>
20+#include <QtQml/qqml.h>
2121
2222// self
2323#include "fakeplugin.h"
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+2−2
@@ -31,8 +31,8 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
3131 m_pinned(false),
3232 m_running(false),
3333 m_recent(false),
34− m_progress(8),
35− m_count(8),
34+ m_progress(-1),
35+ m_count(0),
3636 m_quickList(new MockQuickListModel(this))
3737{
3838
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+48−18
@@ -22,34 +22,40 @@
2222
2323MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(parent)
2424{
25− MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app");
25+ MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
26+ item->setProgress(0);
2627 m_list.append(item);
27− item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera");
28+ item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29+ item->setProgress(10);
2830 m_list.append(item);
29− item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery");
31+ item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
32+ item->setProgress(50);
3033 m_list.append(item);
31− item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook");
34+ item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
35+ item->setProgress(150);
3236 m_list.append(item);
33− item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser");
37+ item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
38+ item->setCount(1);
3439 m_list.append(item);
35− item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter");
40+ item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
41+ item->setCount(12);
3642 m_list.append(item);
37− item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail");
43+ item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
44+ item->setCount(123);
3845 m_list.append(item);
39− item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather");
46+ item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
47+ item->setCount(1234567890);
4048 m_list.append(item);
41− item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad");
49+ item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50+ item->setProgress(50);
51+ item->setCount(5);
4252 m_list.append(item);
43− item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar");
53+ item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
4454 m_list.append(item);
4555}
4656
4757MockLauncherModel::~MockLauncherModel()
4858{
49− while (!m_list.empty())
50− {
51− m_list.takeFirst()->deleteLater();
52− }
5359}
5460
5561int MockLauncherModel::rowCount(const QModelIndex& parent) const
@@ -95,9 +101,29 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
95101
96102void MockLauncherModel::move(int oldIndex, int newIndex)
97103{
98− beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
104+ // Make sure its not moved outside the lists
105+ if (newIndex < 0) {
106+ newIndex = 0;
107+ }
108+ if (newIndex >= m_list.count()) {
109+ newIndex = m_list.count()-1;
110+ }
111+
112+ // Nothing to do?
113+ if (oldIndex == newIndex) {
114+ return;
115+ }
116+
117+ // QList's and QAbstractItemModel's move implementation differ when moving an item up the list :/
118+ // While QList needs the index in the resulting list, beginMoveRows expects it to be in the current list
119+ // adjust the model's index by +1 in case we're moving upwards
120+ int newModelIndex = newIndex > oldIndex ? newIndex+1 : newIndex;
121+
122+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newModelIndex);
99123 m_list.move(oldIndex, newIndex);
100124 endMoveRows();
125+
126+ pin(m_list.at(newIndex)->appId());
101127}
102128
103129void MockLauncherModel::pin(const QString &appId, int index)
@@ -135,9 +161,7 @@ void MockLauncherModel::requestRemove(const QString &appId)
135161
136162void MockLauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
137163{
138− Q_UNUSED(appId)
139− Q_UNUSED(actionIndex)
140− // Nothing to mock yet...
164+ Q_EMIT quickListTriggered(appId, actionIndex);
141165}
142166
143167int MockLauncherModel::findApp(const QString &appId)
@@ -150,3 +174,9 @@ int MockLauncherModel::findApp(const QString &appId)
150174 }
151175 return -1;
152176}
177+
178+void MockLauncherModel::setUser(const QString &username)
179+{
180+ Q_UNUSED(username)
181+ // TODO: implement this...
182+}
tests/mocks/Unity/Launcher/MockLauncherModel.h+4
@@ -43,6 +43,10 @@ public:
4343 Q_INVOKABLE void pin(const QString &appId, int index = -1);
4444 Q_INVOKABLE void requestRemove(const QString &appId);
4545 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
46+ Q_INVOKABLE void setUser(const QString &username);
47+
48+Q_SIGNALS:
49+ void quickListTriggered(const QString &appId, int index);
4650
4751private:
4852 int findApp(const QString &appId);
tests/mocks/Unity/Launcher/MockQuickListModel.cpp+2
@@ -35,6 +35,8 @@ QVariant MockQuickListModel::data(const QModelIndex &index, int role) const
3535 return QLatin1String("test menu entry ") + QString::number(index.row());
3636 case RoleIcon:
3737 return QLatin1String("copy.png");
38+ case RoleClickable:
39+ return index.row() == 1 ? false : true;
3840 }
3941 return QVariant();
4042}
tests/mocks/Unity/Launcher/plugin.cpp+3
@@ -20,6 +20,7 @@
2020#include "plugin.h"
2121#include "MockLauncherModel.h"
2222#include "MockLauncherItem.h"
23+#include "MockQuickListModel.h"
2324
2425#include <unity/shell/launcher/LauncherModelInterface.h>
2526#include <unity/shell/launcher/LauncherItemInterface.h>
@@ -39,7 +40,9 @@ void TestLauncherPlugin::registerTypes(const char* uri)
3940 // @uri Unity.Launcher
4041 qmlRegisterUncreatableType<LauncherModelInterface>(uri, 0, 1, "LauncherModelInterface", "Abstract Interface. Cannot be instantiated.");
4142 qmlRegisterUncreatableType<LauncherItemInterface>(uri, 0, 1, "LauncherItemInterface", "Abstract Interface. Cannot be instantiated.");
43+ qmlRegisterUncreatableType<QuickListModelInterface>(uri, 0, 1, "QuickListModelInterface", "Abstract Interface. Cannot be instantiated.");
4244
4345 qmlRegisterSingletonType<MockLauncherModel>(uri, 0, 1, "LauncherModel", modelProvider);
4446 qmlRegisterUncreatableType<MockLauncherItem>(uri, 0, 1, "LauncherItem", "Can't create LauncherItems in QML. Get them from the LauncherModel");
47+ qmlRegisterUncreatableType<MockQuickListModel>(uri, 0, 1, "QuickListModel", "Can't create QuickLists in QML. Get them from the LauncherItems");
4548}
tests/mocks/Unity/fake_unity_plugin.cpp+2−1
@@ -26,7 +26,8 @@
2626
2727// External
2828#include <glib-object.h>
29−#include <QtQml>
29+
30+#include <QtQml/qqml.h>
3031
3132void FakeUnityPlugin::registerTypes(const char *uri)
3233{
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertest.cpp+44−1
@@ -19,6 +19,7 @@
1919#include <QAbstractItemModel>
2020#include <QQmlEngine>
2121#include <QQuickView>
22+#include <QSignalSpy>
2223#include <QtTestGui>
2324#pragma GCC diagnostic push
2425#pragma GCC diagnostic ignored "-pedantic"
@@ -1521,7 +1522,7 @@ private Q_SLOTS:
15211522 QCOMPARE(lvwph->m_firstVisibleIndex, 1);
15221523 verifyItem(0, -458., 1000., false);
15231524 verifyItem(1, 542., 350., true);
1524− QCOMPARE(lvwph->m_minYExtent, 362.5);
1525+ QCOMPARE(lvwph->m_minYExtent, 525.);
15251526 QCOMPARE(lvwph->m_clipItem->y(), 658.);
15261527 QCOMPARE(lvwph->m_clipItem->clip(), false);
15271528 QCOMPARE(lvwph->m_headerItem->y(), 0.);
@@ -1612,6 +1613,48 @@ private Q_SLOTS:
16121613 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
16131614 }
16141615
1616+ void testMaximizeVisibleAreaWithItemResize()
1617+ {
1618+ model->setProperty(0, "size", 1000);
1619+
1620+ bool res = lvwph->maximizeVisibleArea(1);
1621+ QVERIFY(res);
1622+ QTRY_VERIFY(!lvwph->m_contentYAnimation->isRunning());
1623+
1624+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 3);
1625+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1626+ verifyItem(0, -658., 1000., false);
1627+ verifyItem(1, 342., 200., false);
1628+ verifyItem(2, 542, 350., true);
1629+ QCOMPARE(lvwph->m_minYExtent, 0.);
1630+ QCOMPARE(lvwph->m_clipItem->y(), 708.);
1631+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1632+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1633+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1634+ QCOMPARE(lvwph->contentY(), 708.);
1635+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1636+ }
1637+
1638+ void addingRemoveItemsShouldNotChangeContentY()
1639+ {
1640+ QSignalSpy spy(lvwph, SIGNAL(contentYChanged()));
1641+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 150));
1642+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 1), Q_ARG(QVariant, 6));
1643+
1644+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 1);
1645+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1646+ verifyItem(0, 50., 150., false);
1647+ QCOMPARE(lvwph->m_minYExtent, 0.);
1648+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
1649+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1650+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1651+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1652+ QCOMPARE(lvwph->contentY(), 0.);
1653+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1654+
1655+ QCOMPARE(spy.count(), 0);
1656+ }
1657+
16151658private:
16161659 QQuickView *view;
16171660 ListViewWithPageHeader *lvwph;
tests/plugins/ListViewWithPageHeader/listviewwithpageheadertestsection.cpp+44−1
@@ -19,6 +19,7 @@
1919#include <QAbstractItemModel>
2020#include <QQmlEngine>
2121#include <QQuickView>
22+#include <QSignalSpy>
2223#include <QtTestGui>
2324#pragma GCC diagnostic push
2425#pragma GCC diagnostic ignored "-pedantic"
@@ -1875,7 +1876,7 @@ private Q_SLOTS:
18751876 QCOMPARE(lvwph->m_firstVisibleIndex, 1);
18761877 verifyItem(0, -498., 1040., false, "Regular", true);
18771878 verifyItem(1, 542., 390., true, "Mild", true);
1878− QCOMPARE(lvwph->m_minYExtent, 1250/3.);
1879+ QCOMPARE(lvwph->m_minYExtent, 525.);
18791880 QCOMPARE(lvwph->m_clipItem->y(), 738.);
18801881 QCOMPARE(lvwph->m_clipItem->clip(), false);
18811882 QCOMPARE(lvwph->m_headerItem->y(), 0.);
@@ -2033,6 +2034,48 @@ private Q_SLOTS:
20332034 QCOMPARE(lvwph->m_topSectionItem->y(), 0.);
20342035 }
20352036
2037+ void testMaximizeVisibleAreaWithItemResize()
2038+ {
2039+ model->setProperty(0, "size", 1000);
2040+
2041+ bool res = lvwph->maximizeVisibleArea(1);
2042+ QVERIFY(res);
2043+ QTRY_VERIFY(!lvwph->m_contentYAnimation->isRunning());
2044+
2045+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 3);
2046+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
2047+ verifyItem(0, -738., 1040., false, "Agressive", true);
2048+ verifyItem(1, 302., 240., false, "Regular", false);
2049+ verifyItem(2, 542, 390., true, "Mild", true);
2050+ QCOMPARE(lvwph->m_minYExtent, 0.);
2051+ QCOMPARE(lvwph->m_clipItem->y(), 788.);
2052+ QCOMPARE(lvwph->m_clipItem->clip(), false);
2053+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
2054+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
2055+ QCOMPARE(lvwph->contentY(), 788.);
2056+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
2057+ }
2058+
2059+ void addingRemoveItemsShouldNotChangeContentY()
2060+ {
2061+ QSignalSpy spy(lvwph, SIGNAL(contentYChanged()));
2062+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 150), Q_ARG(QVariant, "Agressive"));
2063+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 1), Q_ARG(QVariant, 6));
2064+
2065+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 1);
2066+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
2067+ verifyItem(0, 50., 190., false, "Agressive", false);
2068+ QCOMPARE(lvwph->m_minYExtent, 0.);
2069+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
2070+ QCOMPARE(lvwph->m_clipItem->clip(), false);
2071+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
2072+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
2073+ QCOMPARE(lvwph->contentY(), 0.);
2074+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
2075+
2076+ QCOMPARE(spy.count(), 0);
2077+ }
2078+
20362079private:
20372080 QQuickView *view;
20382081 ListViewWithPageHeader *lvwph;
tests/plugins/Unity/CMakeLists.txt+9−1
@@ -2,12 +2,16 @@ add_subdirectory(Indicators)
22
33pkg_check_modules(UNITYCORE REQUIRED unity-core-6.0)
44pkg_check_modules(LIBUNITYPROTO REQUIRED unity-protocol-private)
5+pkg_check_modules(LIBUNITY REQUIRED unity)
6+pkg_check_modules(LIBSIGCPP REQUIRED sigc++-2.0)
57
68include_directories(
79 ${Unity-qml_SOURCE_DIR}
810 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Unity
911 ${CMAKE_CURRENT_BINARY_DIR}
1012 ${LIBUNITYPROTO_INCLUDE_DIRS}
13+ ${LIBUNITY_INCLUDE_DIRS}
14+ ${LIBSIGCPP_INCLUDE_DIRS}
1115 ${UNITYCORE_INCLUDE_DIRS}
1216 )
1317
@@ -20,7 +24,7 @@ macro(run_tests)
2024 -o -,txt)
2125 add_test(NAME test${CLASSNAME}${_test} COMMAND ${testCommand})
2226 add_custom_target(${_test} ${testCommand})
23− add_executable(${_test}Exec ${_test}.cpp previewbindingstest.cpp)
27+ add_executable(${_test}Exec ${_test}.cpp)
2428 qt5_use_modules(${_test}Exec Test Core Qml)
2529 set_tests_properties(test${CLASSNAME}${_test}
2630 PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Unity:${LIBUNITYPROTO_LIBRARY_DIRS}")
@@ -28,7 +32,9 @@ macro(run_tests)
2832 target_link_libraries(${_test}Exec
2933 Unity-qml
3034 ${UNITYCORE_LDFLAGS}
35+ ${LIBUNITY_LDFLAGS}
3136 ${LIBUNITYPROTO_LDFLAGS}
37+ ${LIBSIGCPP_LDFLAGS}
3238 )
3339
3440 set(_test_list "${_test_list};${_test}")
@@ -36,5 +42,7 @@ macro(run_tests)
3642endmacro(run_tests)
3743
3844run_tests(
45+ signalslisttest
3946 previewbindingstest
47+ filtersbindingstest
4048 )
tests/plugins/Unity/Indicators/indicatorsmanagertest.cpp−1
@@ -77,7 +77,6 @@ private Q_SLOTS:
7777 // Check that the initial properties have been set.
7878 QVariantMap props = indicator->indicatorProperties().toMap();
7979 QCOMPARE(props.count(), 4);
80− QCOMPARE(props["busType"].toInt(), 1);
8180 QCOMPARE(props["busName"].toString(), QString("com.canonical.indicator.fake1"));
8281 QCOMPARE(props["actionsObjectPath"].toString(), QString("/com/canonical/indicator/fake1"));
8382
tests/plugins/Unity/filtersbindingstest.cppnew file+484
@@ -0,0 +1,484 @@
1+/*
2+* Copyright (C) 2013 Canonical, Ltd.
3+*
4+* Authors:
5+* Pawel Stolowski <pawel.stolowski@canonical.com>
6+*
7+* This program is free software; you can redistribute it and/or modify
8+* it under the terms of the GNU General Public License as published by
9+* the Free Software Foundation; version 3.
10+*
11+* This program is distributed in the hope that it will be useful,
12+* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+* GNU General Public License for more details.
15+*
16+* You should have received a copy of the GNU General Public License
17+* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+*/
19+
20+#include "filtersbindingstest.h"
21+
22+// libunity-core
23+#include <UnityCore/Filter.h>
24+#include <UnityCore/MultiRangeFilter.h>
25+
26+// Qt
27+#include <QTest>
28+#include <QSignalSpy>
29+
30+// libunity
31+#include <unity.h>
32+
33+// local
34+#include "filter.h"
35+#include "multirangefilter.h"
36+#include "checkoptionfilter.h"
37+#include "radiooptionfilter.h"
38+#include "ratingsfilter.h"
39+#include "ratingfilteroption.h"
40+#include "genericoptionsmodel.h"
41+
42+void FiltersBindingsTest::initTestCase()
43+{
44+}
45+
46+GVariant* FiltersBindingsTest::createOptions(int numOfOptions)
47+{
48+ auto vtype = g_variant_type_new("a(sssb)");
49+ auto vb = g_variant_builder_new(vtype);
50+ for (int i = 0; i<numOfOptions; i++) {
51+ const std::string optId = "opt" + std::to_string(i);
52+ const std::string optName = "Option " + std::to_string(i);
53+ g_variant_builder_add(vb, "(sssb)", optId.c_str(), optName.c_str(), "", false);
54+ }
55+ return g_variant_builder_end(vb);
56+}
57+
58+DeeModel* FiltersBindingsTest::createFilterModel()
59+{
60+ const char* filter_schema[8] = {"s", "s", "s", "s", "a{sv}", "b", "b", "b"};
61+ auto model = dee_sequence_model_new();
62+ dee_model_set_schema_full (DEE_MODEL(model), filter_schema, 8);
63+ return model;
64+}
65+
66+void FiltersBindingsTest::createMultiRangeFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
67+{
68+ createFilter(model, "filter-multirange", id, name, optionCount);
69+}
70+
71+void FiltersBindingsTest::createRadioOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
72+{
73+ createFilter(model, "filter-radiooption", id, name, optionCount);
74+}
75+
76+void FiltersBindingsTest::createCheckOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
77+{
78+ createFilter(model, "filter-checkoption", id, name, optionCount);
79+}
80+
81+void FiltersBindingsTest::createRatingsFilter(DeeModel *model, const std::string &id, const std::string &name)
82+{
83+ GVariant* row[8];
84+
85+ row[0] = g_variant_new_string(id.c_str());
86+ row[1] = g_variant_new_string(name.c_str());
87+ row[2] = g_variant_new_string(""); // icon hint
88+ row[3] = g_variant_new_string("filter-ratings");
89+
90+ GVariant *children[2];
91+ GVariant *key1 = g_variant_new_string("show-all-button");
92+ GVariant *key2 = g_variant_new_string("rating");
93+ children[0] = g_variant_new_dict_entry(key1, g_variant_new_variant(g_variant_new_boolean(false)));
94+ children[1] = g_variant_new_dict_entry(key2, g_variant_new_variant(g_variant_new_double(0.0f)));
95+ auto fhints = g_variant_new_array(G_VARIANT_TYPE("{sv}"), children, 2);
96+
97+ row[4] = fhints;
98+ row[5] = g_variant_new_boolean(true); // visible
99+ row[6] = g_variant_new_boolean(false); // collapsed
100+ row[7] = g_variant_new_boolean(false); // filtering
101+
102+ dee_model_append_row(model, row);
103+}
104+
105+void FiltersBindingsTest::createFilter(DeeModel *model, const std::string &renderer, const std::string &id, const std::string &name, int optionCount)
106+{
107+ GVariant* row[8];
108+
109+ row[0] = g_variant_new_string(id.c_str());
110+ row[1] = g_variant_new_string(name.c_str());
111+ row[2] = g_variant_new_string(""); // icon hint
112+ row[3] = g_variant_new_string(renderer.c_str()); // renderer
113+
114+ GVariant *children[2];
115+ GVariant *key1 = g_variant_new_string("show-all-button");
116+ GVariant *key2 = g_variant_new_string("options");
117+ children[0] = g_variant_new_dict_entry(key1, g_variant_new_variant(g_variant_new_boolean(false)));
118+ children[1] = g_variant_new_dict_entry(key2, g_variant_new_variant(createOptions(optionCount)));
119+ auto fhints = g_variant_new_array(G_VARIANT_TYPE("{sv}"), children, 2);
120+
121+ row[4] = fhints;
122+ row[5] = g_variant_new_boolean(true); // visible
123+ row[6] = g_variant_new_boolean(false); // collapsed
124+ row[7] = g_variant_new_boolean(false); // filtering
125+
126+ dee_model_append_row(model, row);
127+}
128+
129+void FiltersBindingsTest::testMultiRangeFilter()
130+{
131+ auto model = createFilterModel();
132+
133+ createMultiRangeFilter(model, "f1", "Filter1", 1);
134+ createMultiRangeFilter(model, "f2", "Filter2", 2);
135+ createMultiRangeFilter(model, "f3", "Filter3", 3);
136+
137+ // create filter out of 1st row
138+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
139+ {
140+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
141+ QVERIFY(core_filter != nullptr);
142+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
143+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
144+ QVERIFY(multi_filter != nullptr);
145+ auto options = multi_filter->options();
146+ QCOMPARE(options->rowCount(), 1);
147+
148+ auto idx = options->index(0);
149+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
150+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
151+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
152+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
153+
154+ QCOMPARE(id_var.toString(), QString("opt0"));
155+ QCOMPARE(name_var.toString(), QString("Option 0"));
156+ QCOMPARE(icon_var.toString(), QString(""));
157+ QCOMPARE(active_var.toBool(), false);
158+
159+ delete multi_filter;
160+ }
161+
162+ // create filter out of 2nd row
163+ iter = dee_model_next(DEE_MODEL(model), iter);
164+ {
165+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
166+ QVERIFY(core_filter != nullptr);
167+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
168+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
169+ QVERIFY(multi_filter != nullptr);
170+ auto options = multi_filter->options();
171+ QCOMPARE(options->rowCount(), 2);
172+
173+ auto idx = options->index(0);
174+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
175+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
176+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
177+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
178+
179+ QCOMPARE(id_var.toString(), QString("opt0"));
180+ QCOMPARE(name_var.toString(), QString("Option 0"));
181+ QCOMPARE(icon_var.toString(), QString(""));
182+ QCOMPARE(active_var.toBool(), false);
183+
184+ AbstractFilterOption* opt1 = options->getRawOption(0);
185+ QCOMPARE(opt1->id(), QString("opt0"));
186+
187+ AbstractFilterOption* opt2 = options->getRawOption(1);
188+ QCOMPARE(opt2->id(), QString("opt1"));
189+
190+ delete multi_filter;
191+ }
192+
193+ // create filter out of 3nd row
194+ iter = dee_model_next(DEE_MODEL(model), iter);
195+ {
196+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
197+ QVERIFY(core_filter != nullptr);
198+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
199+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
200+ QVERIFY(multi_filter != nullptr);
201+ auto options = multi_filter->options();
202+ QCOMPARE(options->rowCount(), 3);
203+
204+ AbstractFilterOption* opt0 = options->getRawOption(0);
205+ QCOMPARE(opt0->id(), QString("opt0"));
206+
207+ AbstractFilterOption* opt1 = options->getRawOption(1);
208+ QCOMPARE(opt1->id(), QString("opt1"));
209+
210+ AbstractFilterOption* opt2 = options->getRawOption(2);
211+ QCOMPARE(opt2->id(), QString("opt2"));
212+
213+ // verify combined filter options are bound to correct underlying unity options
214+ auto core_multi_filter = std::dynamic_pointer_cast<unity::dash::MultiRangeFilter>(core_filter);
215+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[0]->id()), QString("opt0"));
216+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[1]->id()), QString("opt1"));
217+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[2]->id()), QString("opt2"));
218+
219+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
220+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
221+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
222+
223+ // test active property changes
224+ QCOMPARE(opt0->active(), false);
225+
226+ opt0->setActive(true); // activate 1st option
227+ QCOMPARE(opt0spy.count(), 1);
228+ QCOMPARE(opt1spy.count(), 0);
229+ QCOMPARE(opt2spy.count(), 0);
230+ QCOMPARE(opt0->active(), true);
231+ QCOMPARE(opt1->active(), false);
232+ QCOMPARE(opt2->active(), false);
233+
234+ opt1->setActive(true);
235+ QCOMPARE(opt0spy.count(), 2);
236+ QCOMPARE(opt1spy.count(), 1);
237+ QCOMPARE(opt2spy.count(), 0);
238+ QCOMPARE(opt0->active(), false); //1st combined option gets deactivated
239+ QCOMPARE(opt1->active(), true);
240+ QCOMPARE(opt2->active(), false);
241+
242+ opt1->setActive(false); // and de-activate it
243+ QCOMPARE(opt0spy.count(), 2);
244+ QCOMPARE(opt1spy.count(), 2);
245+ QCOMPARE(opt2spy.count(), 0);
246+ QCOMPARE(opt0->active(), false);
247+ QCOMPARE(opt1->active(), false);
248+ QCOMPARE(opt2->active(), false);
249+
250+ opt2->setActive(true); // activate another combined option
251+ QCOMPARE(opt0spy.count(), 2);
252+ QCOMPARE(opt1spy.count(), 2);
253+ QCOMPARE(opt2spy.count(), 1);
254+ QCOMPARE(opt0->active(), false);
255+ QCOMPARE(opt1->active(), false);
256+ QCOMPARE(opt2->active(), true);
257+
258+ delete multi_filter;
259+ }
260+}
261+
262+void FiltersBindingsTest::testCheckOptionFilter()
263+{
264+ auto model = createFilterModel();
265+
266+ createCheckOptionFilter(model, "f1", "Filter1", 3);
267+
268+ // create filter out of 1st row
269+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
270+ {
271+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
272+ QVERIFY(core_filter != nullptr);
273+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
274+ auto check_filter = dynamic_cast<CheckOptionFilter*>(bind_filter);
275+ QVERIFY(check_filter != nullptr);
276+ auto options = check_filter->options();
277+ QCOMPARE(options->rowCount(), 3);
278+
279+ QCOMPARE(check_filter->id(), QString("f1"));
280+ QCOMPARE(check_filter->name(), QString("Filter1"));
281+ QCOMPARE(check_filter->iconHint(), QString(""));
282+ QCOMPARE(check_filter->rendererName(), QString("filter-checkoption"));
283+ QCOMPARE(check_filter->visible(), true);
284+ QCOMPARE(check_filter->collapsed(), false);
285+ QCOMPARE(check_filter->filtering(), false);
286+
287+ auto idx = options->index(0);
288+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
289+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
290+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
291+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
292+
293+ QCOMPARE(id_var.toString(), QString("opt0"));
294+ QCOMPARE(name_var.toString(), QString("Option 0"));
295+ QCOMPARE(icon_var.toString(), QString(""));
296+ QCOMPARE(active_var.toBool(), false);
297+
298+ idx = options->index(1);
299+ id_var = options->data(idx, GenericOptionsModel::RoleId);
300+ name_var = options->data(idx, GenericOptionsModel::RoleName);
301+ icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
302+ active_var = options->data(idx, GenericOptionsModel::RoleActive);
303+
304+ QCOMPARE(id_var.toString(), QString("opt1"));
305+ QCOMPARE(name_var.toString(), QString("Option 1"));
306+ QCOMPARE(icon_var.toString(), QString(""));
307+ QCOMPARE(active_var.toBool(), false);
308+
309+ AbstractFilterOption* opt0 = options->getRawOption(0);
310+ AbstractFilterOption* opt1 = options->getRawOption(1);
311+ AbstractFilterOption* opt2 = options->getRawOption(2);
312+
313+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
314+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
315+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
316+ QSignalSpy dataChangedSpy(options, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
317+
318+ // test active property changes
319+ QCOMPARE(opt0->active(), false);
320+ QCOMPARE(opt1->active(), false);
321+ QCOMPARE(opt2->active(), false);
322+
323+ opt0->setActive(true);
324+ QCOMPARE(opt0spy.count(), 1);
325+ QCOMPARE(opt1spy.count(), 0);
326+ QCOMPARE(opt2spy.count(), 0);
327+ QCOMPARE(opt0->active(), true);
328+ QCOMPARE(opt1->active(), false);
329+ QCOMPARE(opt2->active(), false);
330+
331+ opt1->setActive(true);
332+ QCOMPARE(opt0spy.count(), 2);
333+ QCOMPARE(opt1spy.count(), 1);
334+ QCOMPARE(opt2spy.count(), 0);
335+ QCOMPARE(opt0->active(), false);
336+ QCOMPARE(opt1->active(), true);
337+ QCOMPARE(opt2->active(), false);
338+
339+ QCOMPARE(dataChangedSpy.count(), 3);
340+
341+ delete check_filter;
342+ }
343+}
344+
345+void FiltersBindingsTest::testRadioOptionFilter()
346+{
347+ auto model = createFilterModel();
348+
349+ createRadioOptionFilter(model, "f1", "Filter1", 3);
350+
351+ // create filter out of 1st row
352+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
353+ {
354+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
355+ QVERIFY(core_filter != nullptr);
356+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
357+ auto radio_filter = dynamic_cast<RadioOptionFilter*>(bind_filter);
358+ QVERIFY(radio_filter != nullptr);
359+ auto options = radio_filter->options();
360+ QCOMPARE(options->rowCount(), 3);
361+
362+ auto idx = options->index(0);
363+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
364+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
365+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
366+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
367+
368+ QCOMPARE(id_var.toString(), QString("opt0"));
369+ QCOMPARE(name_var.toString(), QString("Option 0"));
370+ QCOMPARE(icon_var.toString(), QString(""));
371+ QCOMPARE(active_var.toBool(), false);
372+
373+ idx = options->index(1);
374+ id_var = options->data(idx, GenericOptionsModel::RoleId);
375+ name_var = options->data(idx, GenericOptionsModel::RoleName);
376+ icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
377+ active_var = options->data(idx, GenericOptionsModel::RoleActive);
378+
379+ QCOMPARE(id_var.toString(), QString("opt1"));
380+ QCOMPARE(name_var.toString(), QString("Option 1"));
381+ QCOMPARE(icon_var.toString(), QString(""));
382+ QCOMPARE(active_var.toBool(), false);
383+
384+ AbstractFilterOption* opt0 = options->getRawOption(0);
385+ AbstractFilterOption* opt1 = options->getRawOption(1);
386+ AbstractFilterOption* opt2 = options->getRawOption(2);
387+
388+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
389+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
390+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
391+ QSignalSpy dataChangedSpy(options, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
392+
393+ // test active property changes
394+ QCOMPARE(opt0->active(), false);
395+ QCOMPARE(opt1->active(), false);
396+ QCOMPARE(opt2->active(), false);
397+
398+ opt0->setActive(true);
399+ QCOMPARE(opt0spy.count(), 1);
400+ QCOMPARE(opt1spy.count(), 0);
401+ QCOMPARE(opt2spy.count(), 0);
402+ QCOMPARE(opt0->active(), true);
403+ QCOMPARE(opt1->active(), false);
404+ QCOMPARE(opt2->active(), false);
405+
406+ opt1->setActive(true);
407+ QCOMPARE(opt0spy.count(), 2);
408+ QCOMPARE(opt1spy.count(), 1);
409+ QCOMPARE(opt2spy.count(), 0);
410+ QCOMPARE(opt0->active(), false);
411+ QCOMPARE(opt1->active(), true);
412+ QCOMPARE(opt2->active(), false);
413+
414+ QCOMPARE(dataChangedSpy.count(), 3);
415+
416+ delete radio_filter;
417+ }
418+}
419+
420+void FiltersBindingsTest::testRatingsFilter()
421+{
422+ auto model = createFilterModel();
423+
424+ createRatingsFilter(model, "f1", "Filter1");
425+
426+ // create filter out of 1st row
427+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
428+ {
429+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
430+ QVERIFY(core_filter != nullptr);
431+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
432+ auto rating_filter = dynamic_cast<RatingsFilter*>(bind_filter);
433+ QVERIFY(rating_filter != nullptr);
434+ auto options = rating_filter->options();
435+ QCOMPARE(options->rowCount(), 5);
436+
437+ RatingFilterOption* opt[5];
438+ for (int i = 0; i<5; i++) {
439+ opt[i] = dynamic_cast<RatingFilterOption *>(options->getRawOption(i));
440+ }
441+
442+ QSignalSpy opt0spy(opt[0], SIGNAL(activeChanged(bool)));
443+ QSignalSpy opt4spy(opt[4], SIGNAL(activeChanged(bool)));
444+ QSignalSpy dataChangedSpy(options, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
445+
446+ QVERIFY(qAbs(opt[0]->value() - 0.2f) <= 0.0001f);
447+ QVERIFY(qAbs(opt[1]->value() - 0.4f) <= 0.0001f);
448+ QVERIFY(qAbs(opt[2]->value() - 0.6f) <= 0.0001f);
449+ QVERIFY(qAbs(opt[3]->value() - 0.8f) <= 0.0001f);
450+ QVERIFY(qAbs(opt[4]->value() - 1.0f) <= 0.0001f);
451+
452+ QVERIFY(rating_filter->rating() <= 0.0001f); // rating equals zero
453+
454+ // all options initially inactive
455+ for (int i=0; i<5; i++) {
456+ QCOMPARE(opt[i]->active(), false);
457+ }
458+
459+ // verify activation for all options
460+ for (int i=0; i<5; i++) {
461+ opt[i]->setActive(true);
462+ QCOMPARE(opt[i]->active(), true);
463+ QVERIFY(rating_filter->rating() - ((i+1)*0.2f) <= 0.0001f);
464+
465+ // verify that all other options are inactive
466+ for (int j=0; j<5; j++) {
467+ if (i != j) {
468+ QCOMPARE(opt[j]->active(), false);
469+ }
470+ }
471+ }
472+
473+ QCOMPARE(opt0spy.count(), 2);
474+ QCOMPARE(opt4spy.count(), 1);
475+
476+ opt[4]->setActive(false);
477+ QVERIFY(rating_filter->rating() <= 0.0001f); // rating equals zero
478+ QCOMPARE(opt4spy.count(), 2);
479+
480+ QCOMPARE(dataChangedSpy.count(), 10);
481+ }
482+}
483+
484+QTEST_MAIN(FiltersBindingsTest)
tests/plugins/Unity/filtersbindingstest.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Pawel Stolowski <pawel.stolowski@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef FILTERSBINDINGSTEST_H
21+#define FILTERSBINDINGSTEST_H
22+
23+#include <QObject>
24+#include <dee.h>
25+
26+class FiltersBindingsTest : public QObject
27+{
28+ Q_OBJECT
29+
30+ private Q_SLOTS:
31+ void initTestCase();
32+ void testMultiRangeFilter();
33+ void testCheckOptionFilter();
34+ void testRadioOptionFilter();
35+ void testRatingsFilter();
36+
37+ private:
38+ DeeModel* createFilterModel();
39+ GVariant* createOptions(int numOfOptions);
40+ void createMultiRangeFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount);
41+ void createCheckOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount);
42+ void createRadioOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount);
43+ void createRatingsFilter(DeeModel *model, const std::string &id, const std::string &name);
44+ void createFilter(DeeModel *model, const std::string &renderer, const std::string &id, const std::string &name, int optionCount);
45+};
46+
47+#endif // FILTERSBINDINGSTEST_H
tests/plugins/Unity/signalslisttest.cppnew file+57
@@ -0,0 +1,57 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+// local
18+#include "signalslisttest.h"
19+#include "signalslist.h"
20+
21+// Qt
22+#include <QTest>
23+
24+class TestSource
25+{
26+ public:
27+ TestSource(): m_count(0) {}
28+ void update()
29+ {
30+ countChanged(++m_count);
31+ }
32+
33+ sigc::signal<void, int> countChanged;
34+ int m_count;
35+};
36+
37+void SignalsListTest::testSignalsList()
38+{
39+ TestSource src;
40+ SignalsList signals;
41+ int counter = -1;
42+
43+ signals << src.countChanged.connect([&counter](int val) {
44+ counter = val;
45+ });
46+
47+ src.update();
48+ QCOMPARE(counter, 1);
49+ src.update();
50+ QCOMPARE(counter, 2);
51+
52+ signals.disconnectAll();
53+ src.update();
54+ QCOMPARE(counter, 2);
55+}
56+
57+QTEST_MAIN(SignalsListTest)
tests/plugins/Unity/signalslisttest.hnew file+30
@@ -0,0 +1,30 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef SIGNALSLISTTEST_H
18+#define SIGNALSLISTTEST_H
19+
20+#include <QObject>
21+
22+class SignalsListTest : public QObject
23+{
24+ Q_OBJECT
25+
26+ private Q_SLOTS:
27+ void testSignalsList();
28+};
29+
30+#endif // SIGNALSLISTTEST_H
tests/plugins/Utils/qlimitproxymodeltest.cpp+133−8
@@ -58,10 +58,17 @@ public:
5858 m_roles = roles;
5959 }
6060
61+ void clear()
62+ {
63+ beginRemoveRows(QModelIndex(), 0, m_list.count() - 1);
64+ m_list.clear();
65+ endRemoveRows();
66+ }
67+
6168 bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) {
6269 beginInsertRows(parent, row, row+count-1);
6370 for (int i=0; i<count; i++) {
64− m_list.insert(i+row, "test"+i);
71+ m_list.insert(i+row, QString::number(i));
6572 }
6673 endInsertRows();
6774 return true;
@@ -88,6 +95,60 @@ private:
8895 QHash<int, QByteArray> m_roles;
8996};
9097
98+class MirrorModel : public QObject
99+{
100+ Q_OBJECT
101+
102+public:
103+ MirrorModel(QAbstractItemModel *model) : m_mirror(model)
104+ {
105+ connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)));
106+ connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int)));
107+ connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
108+ }
109+
110+ void check()
111+ {
112+ QCOMPARE(m_list.count(), m_mirror->rowCount());
113+ for (int i = 0; i < m_list.count(); ++i) {
114+ QCOMPARE(m_list[i], m_mirror->data(m_mirror->index(i, 0)).toString());
115+ }
116+ }
117+
118+private Q_SLOTS:
119+ void rowsInserted(const QModelIndex &parent, int start, int end)
120+ {
121+ QVERIFY(!parent.isValid());
122+ for (int i = start; i <= end; ++i) {
123+ m_list.insert(i, m_mirror->data(m_mirror->index(i, 0)).toString());
124+ }
125+ }
126+
127+ void rowsRemoved(const QModelIndex &parent, int start, int end)
128+ {
129+ QVERIFY(!parent.isValid());
130+ for (int i = end; i >= start; --i) {
131+ m_list.removeAt(i);
132+ }
133+ }
134+
135+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
136+ {
137+ QVERIFY(topLeft.isValid());
138+ QVERIFY(bottomRight.isValid());
139+ QVERIFY(!topLeft.parent().isValid());
140+ QVERIFY(!bottomRight.parent().isValid());
141+ for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
142+ m_list[i] = m_mirror->data(m_mirror->index(i, 0)).toString();
143+ }
144+ }
145+
146+
147+private:
148+ QStringList m_list;
149+ QAbstractItemModel *m_mirror;
150+};
151+
91152class QLimitProxyModelTest : public QObject
92153{
93154 Q_OBJECT
@@ -401,15 +462,10 @@ private Q_SLOTS:
401462 QCOMPARE(spyOnCountChanged.count(), 0);
402463
403464 model.removeRows(2, 2);
404− QCOMPARE(spyOnRowsRemoved.count(), 1);
465+ QCOMPARE(spyOnRowsRemoved.count(), 0);
405466 QCOMPARE(spyOnRowsInserted.count(), 0);
406− QCOMPARE(spyOnCountChanged.count(), 1);
407− arguments = spyOnRowsRemoved.takeFirst();
408− QCOMPARE(arguments.at(1).toInt(), 2);
409− QCOMPARE(arguments.at(2).toInt(), 3);
467+ QCOMPARE(spyOnCountChanged.count(), 0);
410468 QCOMPARE(proxy.rowCount(), 7);
411− spyOnRowsInserted.clear();
412− spyOnCountChanged.clear();
413469
414470 model.removeRows(0, 7);
415471 QCOMPARE(spyOnRowsRemoved.count(), 1);
@@ -479,6 +535,75 @@ private Q_SLOTS:
479535 QCOMPARE(proxy.rowCount(), 5);
480536 QCOMPARE(spyOnCountChanged.count(), 1);
481537 }
538+
539+ void testMirrorModel() {
540+ QLimitProxyModelQML proxy;
541+ MockListModel model;
542+ MirrorModel mirror(&proxy);
543+
544+ proxy.setModel(&model);
545+ proxy.setLimit(5);
546+
547+ // Checks what happens when the model already has
548+ // more items than the limit and we add stuff to
549+ // the front
550+ model.insertRows(0, 7);
551+ mirror.check();
552+ model.insertRows(1, 3);
553+ mirror.check();
554+ model.clear();
555+ mirror.check();
556+
557+ // Checks what happens when the model does
558+ // not has more items than limit but adding
559+ // stuff to its front makes it go past the limit
560+ model.insertRows(0, 3);
561+ mirror.check();
562+ model.insertRows(1, 3);
563+ mirror.check();
564+ model.clear();
565+ mirror.check();
566+
567+ // Checks what happens when the model does
568+ // not has more items than limit and adding
569+ // stuff to its front makes it not go past the limit
570+ model.insertRows(0, 1);
571+ mirror.check();
572+ model.insertRows(1, 3);
573+ mirror.check();
574+ model.clear();
575+ mirror.check();
576+
577+ // Checks what happens when the model already has
578+ // more items than the limit and we remove stuff from
579+ // the front but it still has more than the limit
580+ model.insertRows(0, 10);
581+ mirror.check();
582+ model.removeRows(1, 3);
583+ mirror.check();
584+ model.clear();
585+ mirror.check();
586+
587+ // Checks what happens when the model already has
588+ // more items than the limit and we remove stuff from
589+ // the front but it has less than the limit
590+ model.insertRows(0, 6);
591+ mirror.check();
592+ model.removeRows(1, 3);
593+ mirror.check();
594+ model.clear();
595+ mirror.check();
596+
597+ // Checks what happens when the model has
598+ // less items than the limit and we remove stuff from
599+ // the front
600+ model.insertRows(0, 4);
601+ mirror.check();
602+ model.removeRows(1, 3);
603+ mirror.check();
604+ model.clear();
605+ mirror.check();
606+ }
482607};
483608
484609QTEST_GUILESS_MAIN(QLimitProxyModelTest)
tests/qmltests/CMakeLists.txt+1−1
@@ -26,7 +26,7 @@ set(qmltest_DEFAULT_TARGETS qmluitests)
2626set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
2727set(qmltest_DEFAULT_PROPERTIES "")
2828
29−add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS}
29+add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
3030 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
3131add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
3232add_qml_test(Components DraggingArea)
tests/qmltests/Components/tst_FilterGrid.qml−35
@@ -42,15 +42,6 @@ Rectangle {
4242 values: [1,2,3,4]
4343 selectedIndex: 1
4444 }
45− Row {
46− spacing: units.gu(1)
47− Label { anchors.verticalCenter: parent.verticalCenter
48− text: "Expandable" }
49− CheckBox {
50− id: expandableCheckBox
51− checked: true
52− }
53− }
5445 Row {
5546 spacing: units.gu(1)
5647 Label { anchors.verticalCenter: parent.verticalCenter
@@ -94,7 +85,6 @@ Rectangle {
9485 collapsedRowCount:
9586 collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex]
9687 filter: filterCheckBox.checked
97− expandable: expandableCheckBox.checked
9888 minimumHorizontalSpacing: units.gu(1)
9989 delegateWidth: units.gu(6)
10090 delegateHeight: units.gu(6)
@@ -142,31 +132,6 @@ Rectangle {
142132 collapsedRowCountSelector.selectedIndex = 1
143133 }
144134
145− // Checks that the filter toggle button, the one that says "View All (xy)",
146− // shows up only when it's possible for the grid to be expanded.
147− function test_filterToggleButton() {
148− var filterToggleButton = findChild(filterGrid, "filterToggleButton");
149−
150− for (var i = 0; i < 4; ++i) {
151− collapsedRowCountSelector.selectedIndex = i
152− // row count == index + 1
153− // The total number of rows is 4.
154−
155− expandableCheckBox.checked = false
156− compare(filterToggleButton.visible, false)
157− expandableCheckBox.checked = true
158−
159− if ((i+1) < 4)
160− compare(filterToggleButton.visible, true)
161− else
162− compare(filterToggleButton.visible, false)
163− }
164−
165− // back to initial state
166− collapsedRowCountSelector.selectedIndex = 1
167− expandableCheckBox.checked = true
168− }
169−
170135 function countVisibleDelegates() {
171136 return __countVisibleDelegates(filterGrid.visibleChildren, 0)
172137 }
tests/qmltests/Dash/Apps/tst_AppPreview.qml+17−3
@@ -35,7 +35,7 @@ Item {
3535
3636 property string commentary: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus a mi vitae augue rhoncus lobortis ut rutrum metus. Curabitur tortor leo, tristique sed mollis quis, condimentum venenatis nibh.";
3737 QtObject { id: screenshots; property var value: ["fake_image1.png", "fake_image2.png", "fake_image3.png"] }
38− QtObject { id: rating; property int value: 8 }
38+ QtObject { id: rating; property real value: 0.8 }
3939 QtObject { id: rated; property int value: 120 }
4040 QtObject { id: reviews; property int value: 8 }
4141 QtObject { id: progress; property string value: "source" }
@@ -51,12 +51,12 @@ Item {
5151 property string title: "Unity App"
5252 property string appIcon: "fake_image.png"
5353 property string description: "This is an Application description"
54+ property real rating: rating.value
55+ property int numRatings: reviews.value
5456 property var execute: root.fake_call
5557 property var infoMap: {
5658 "more-screenshots": screenshots,
57− "rating": rating,
5859 "rated": rated,
59− "reviews": reviews,
6060 "comments": comments
6161 }
6262 property var actions: [
@@ -88,6 +88,7 @@ Item {
8888 var reviewField = findChild(appPreview, "reviewField");
8989 reviewField.focus = false;
9090 reviewField.text = "";
91+ data.rating = rating.value;
9192 }
9293
9394 function test_actions() {
@@ -148,5 +149,18 @@ Item {
148149 compare(comment, comments.value[i][3], "Comment don't match");
149150 }
150151 }
152+
153+ function test_negative_rating() {
154+ data.rating = -1.0;
155+ var rated = findChild(appPreview, "reviewsLabel");
156+ verify(rated.visible == false);
157+ var commentsArea = findChild(appPreview, "commentsArea");
158+ verify(commentsArea.visible == false);
159+ var appReviews = findChild(appPreview, "appReviews");
160+ verify(appReviews.visible == false);
161+ var buttons = findChild(appPreview, "gridButtons");
162+ verify(buttons.visible == true);
163+ }
164+
151165 }
152166}
tests/qmltests/Greeter/tst_Clock.qml+1−1
@@ -70,7 +70,7 @@ Rectangle {
7070 compare(clock.__timerRunning, true, "Timer should be running")
7171 wait(0) // spin event loop to trigger the timer
7272 verify(dateLabel.text !== dateString)
73− if (timeLabel.text == "11:13") wait(60000) // next test will fail at 11:13, wait 1 minute
73+ if (timeLabel.text.indexOf("11:13") != -1) wait(60000) // next test will fail at 11:13, wait 1 minute
7474 verify(timeLabel.text !== timeString)
7575 }
7676
tests/qmltests/Launcher/tst_Launcher.qml+183−6
@@ -19,6 +19,7 @@ import QtTest 1.0
1919import Unity.Test 0.1 as UT
2020import ".."
2121import "../../../Launcher"
22+import Unity.Launcher 0.1
2223
2324/* Nothing is shown at first. If you drag from left edge you will bring up the
2425 launcher. */
@@ -48,6 +49,11 @@ Item {
4849 property int maxPanelX: 0
4950 }
5051
52+ SignalSpy {
53+ id: signalSpy
54+ target: LauncherModel
55+ }
56+
5157 Connections {
5258 target: testCase.findChild(launcher, "launcherPanel")
5359
@@ -176,6 +182,22 @@ Item {
176182 revealer.waitUntilLauncherDisappears();
177183 launcher.available = true;
178184 }
185+
186+ function test_countEmblems() {
187+ var launcherListView = findChild(launcher, "launcherListView");
188+ for (var i = 0; i < launcherListView.count; ++i) {
189+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
190+ compare(findChild(delegate, "countEmblem").visible, LauncherModel.get(i).count > 0)
191+ }
192+ }
193+
194+ function test_progressOverlays() {
195+ var launcherListView = findChild(launcher, "launcherListView");
196+ for (var i = 0; i < launcherListView.count; ++i) {
197+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
198+ compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
199+ }
200+ }
179201 }
180202
181203 UT.UnityTestCase {
@@ -227,11 +249,10 @@ Item {
227249 name: "LauncherInit"
228250 when: windowShown
229251
230− /*
231− * FIXME: There is a bug in ListView which makes it snap to an item
232− * instead of the edge at startup. Enable this test once our patch for
233− * ListView has landed upstream.
234− * https://bugreports.qt-project.org/browse/QTBUG-32251
252+ function initTestCase() {
253+ var listView = findChild(launcher, "launcherListView");
254+ tryCompare(listView, "flicking", false)
255+ }
235256
236257 function test_initFirstUnfolded() {
237258
@@ -245,6 +266,162 @@ Item {
245266 // Now do check that snapping is in fact enabled
246267 compare(listView.snapMode, ListView.SnapToItem, "Snapping is not enabled");
247268 }
248− */
269+ }
270+
271+ UT.UnityTestCase {
272+ id: dndTestCase
273+ name: "Drag and Drop"
274+ when: windowShown && initTestCase.completed
275+
276+ function test_dragndrop_data() {
277+ return [
278+ {tag: "startDrag", fullDrag: false },
279+ {tag: "fullDrag horizontal", fullDrag: true, orientation: ListView.Horizontal },
280+ {tag: "fullDrag vertical", fullDrag: true, orientation: ListView.Vertical },
281+ ];
282+ }
283+
284+ function test_dragndrop(data) {
285+ revealer.dragLauncherIntoView();
286+ var draggedItem = findChild(launcher, "launcherDelegate4")
287+ var item0 = findChild(launcher, "launcherDelegate0")
288+ var fakeDragItem = findChild(launcher, "fakeDragItem")
289+ var initialItemHeight = draggedItem.height
290+ var item4 = LauncherModel.get(4).appId
291+ var item3 = LauncherModel.get(3).appId
292+
293+ var listView = findChild(launcher, "launcherListView");
294+ listView.flick(0, units.gu(200));
295+ tryCompare(listView, "flicking", false);
296+
297+ // Initial state
298+ compare(draggedItem.itemOpacity, 1, "Item's opacity is not 1 at beginning")
299+ compare(fakeDragItem.visible, false, "FakeDragItem isn't invisible at the beginning")
300+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
301+
302+ // Doing longpress
303+ var currentMouseX = draggedItem.width / 2
304+ var currentMouseY = draggedItem.height / 2
305+ mousePress(draggedItem, currentMouseX, currentMouseY)
306+ // DraggedItem needs to hide and fakeDragItem become visible
307+ tryCompare(draggedItem, "itemOpacity", 0)
308+ tryCompare(fakeDragItem, "visible", true)
309+
310+ // Dragging a bit (> 1.5 gu)
311+ currentMouseX -= units.gu(2)
312+ mouseMove(draggedItem, currentMouseX, currentMouseY)
313+ // Other items need to expand and become 0.6 opaque
314+ tryCompare(item0, "angle", 0)
315+ tryCompare(item0, "itemOpacity", 0.6)
316+
317+ if (data.fullDrag) {
318+ // Dragging a bit more
319+ if (data.orientation == ListView.Horizontal) {
320+ currentMouseX -= units.gu(15)
321+ mouseMove(draggedItem, currentMouseX, currentMouseY, 100)
322+
323+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
324+ tryCompare(draggedItem, "height", units.gu(1))
325+
326+ // Dragging downwards. Item needs to move in the model
327+ currentMouseY -= initialItemHeight * 1.5
328+ mouseMove(draggedItem, currentMouseX, currentMouseY)
329+ } else if (data.orientation == ListView.Vertical) {
330+ currentMouseY -= initialItemHeight * 1.5
331+ mouseMove(draggedItem, currentMouseX, currentMouseY, 100)
332+
333+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 1)
334+ tryCompare(draggedItem, "height", units.gu(1))
335+ }
336+
337+ waitForRendering(draggedItem)
338+ compare(LauncherModel.get(4).appId, item3)
339+ compare(LauncherModel.get(3).appId, item4)
340+ }
341+
342+ // Releasing and checking if initial values are restored
343+ mouseRelease(draggedItem)
344+ tryCompare(findChild(draggedItem, "dropIndicator"), "opacity", 0)
345+ tryCompare(draggedItem, "itemOpacity", 1)
346+ tryCompare(fakeDragItem, "visible", false)
347+
348+ // Click somewhere in the empty space to make it hide in case it isn't
349+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
350+ revealer.waitUntilLauncherDisappears();
351+ }
352+
353+ function test_quicklist_dismiss() {
354+ revealer.dragLauncherIntoView();
355+ var draggedItem = findChild(launcher, "launcherDelegate5")
356+ var item0 = findChild(launcher, "launcherDelegate0")
357+ var fakeDragItem = findChild(launcher, "fakeDragItem")
358+ var dndArea = findChild(launcher, "dndArea")
359+
360+ // Initial state
361+ compare(dndArea.quickListPopover, null)
362+
363+ // Doing longpress
364+ mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
365+ tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
366+ verify(dndArea.quickListPopover != null)
367+
368+ // Dragging a bit (> 1.5 gu)
369+ mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
370+
371+ // QuickList needs to be closed when a drag operation starts
372+ tryCompare(dndArea, "quickListPopover", null)
373+
374+ mouseRelease(draggedItem);
375+ }
376+
377+ function test_quicklist_click_data() {
378+ return [
379+ {tag: "non-clickable", index: 1, clickable: false },
380+ {tag: "clickable", index: 2, clickable: true },
381+ ];
382+ }
383+
384+ function test_quicklist_click(data) {
385+ revealer.dragLauncherIntoView();
386+ var clickedItem = findChild(launcher, "launcherDelegate5")
387+ var dndArea = findChild(launcher, "dndArea")
388+
389+ // Initial state
390+ compare(dndArea.quickListPopover, null)
391+
392+ // Doing longpress
393+ mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
394+ tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
395+ verify(dndArea.quickListPopover != null)
396+
397+ mouseRelease(clickedItem);
398+
399+ var quickListEntry = findChild(dndArea.quickListPopover, "quickListEntry" + data.index)
400+
401+ signalSpy.clear();
402+ signalSpy.signalName = "quickListTriggered"
403+
404+ mouseClick(quickListEntry, quickListEntry.width / 2, quickListEntry.height / 2)
405+
406+ if (data.clickable) {
407+ // QuickList needs to be closed when some clickable item is clicked
408+ tryCompare(dndArea, "quickListPopover", null)
409+
410+ compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
411+ compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
412+ compare(signalSpy.signalArguments[0][1], 2)
413+
414+ } else {
415+
416+ // QuickList must not be closed when a non-clickable item is clicked
417+ verify(dndArea.quickListPopover != null)
418+
419+ compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
420+
421+ // Click somewhere in the empty space to dismiss the quicklist
422+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
423+ tryCompare(dndArea, "quickListPopover", null)
424+ }
425+ }
249426 }
250427}
tests/qmltests/Panel/tst_MenuContent.qml+32−47
@@ -85,29 +85,29 @@ Item {
8585
8686 function activate_content(index)
8787 {
88− menuContent.currentMenuIndex = index
88+ menuContent.setCurrentMenuIndex(index)
8989 }
9090
9191 function get_test_menu_objecName(index) {
9292 return "menu_page"+(index+1);
9393 }
9494
95− property string test_menu_objectName : ""
96− function current_menu_equals_test_menu() {
97− var current_loader = menu_content_test.findChild(menuContent, "menus").currentItem
98− if (current_loader == undefined) {
99− console.log("current_loader 'menus' undefined");
95+ property string testTabObjectName : ""
96+
97+ function selected_tab_equals_test_tab() {
98+ var currentTab = menu_content_test.findChild(menuContent, "tabs").selectedTab
99+ if (currentTab == undefined) {
100+ console.log("selected tab undefined");
100101 return false;
101102 }
102103
103− var menu = menu_content_test.findChild(menuContent, test_menu_objectName);
104− if (menu == undefined) {
105− console.log("test_menu " + test_menu_objectName + " undefined");
104+ var testTab = menu_content_test.findChild(menuContent, testTabObjectName);
105+ if (testTab == undefined) {
106+ console.log("test_tab " + testTabObjectName + " undefined");
106107 return false;
107108 }
108109
109− // The parent of the menu will be the loader
110− return menu.parent == current_loader;
110+ return testTab == currentTab;
111111 }
112112
113113 UT.UnityTestCase {
@@ -123,81 +123,66 @@ Item {
123123
124124 // Check that the correct menus are displayed for the requested item.
125125 function test_show_menu() {
126− var menu_count = indicatorsModel.count;
127− verify(menu_count > 0, "Menu count should be greater than zero");
126+ var menuCount = indicatorsModel.count;
127+ verify(menuCount > 0, "Menu count should be greater than zero");
128128
129− var menus = menu_content_test.findChild(menuContent, "menus")
129+ var tabs = menu_content_test.findChild(menuContent, "tabs")
130130
131131 // Loop over twice to test jump between last and first.
132− for (var i = 0; i < menu_count*2; i++) {
132+ for (var i = 0; i < menuCount*2; i++) {
133133
134− var menu_index = i%menu_count;
134+ var menuIndex = i%menuCount;
135135
136− activate_content(menu_index);
137− test_menu_objectName = get_test_menu_objecName(menu_index);
138− compare(menus.currentIndex, menu_index, "Current menu index does not match selected menu index");
139− tryCompareFunction(current_menu_equals_test_menu, true);
136+ activate_content(menuIndex);
137+ testTabObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
138+ compare(tabs.selectedTabIndex, menuIndex, "Current tab index does not match selected tab index");
139+ tryCompareFunction(selected_tab_equals_test_tab, true);
140140 }
141141 }
142142
143143 // Calling activateContent should call start on all menus
144144 function test_activate_content() {
145− var menu_count = indicatorsModel.count;
146− verify(menu_count > 0, "Menu count should be greater than zero");
145+ var menuCount = indicatorsModel.count;
146+ verify(menuCount > 0, "Menu count should be greater than zero");
147147
148148 // Ensure all the menus are stopped first
149149 menuContent.__contentActive = false;
150− for (var i = 0; i < menu_count; i++) {
150+ for (var i = 0; i < menuCount; i++) {
151151 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
152152 }
153153
154154 // activate content the content to call stop.
155155 menuContent.activateContent();
156− for (var i = 0; i < menu_count; i++) {
156+ for (var i = 0; i < menuCount; i++) {
157157 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
158158 }
159159 }
160160
161161 // Calling activateContent should call stop on all menus.
162162 function test_release_content() {
163− var menu_count = indicatorsModel.count;
164− verify(menu_count > 0, "Menu count should be greater than zero");
163+ var menuCount = indicatorsModel.count;
164+ verify(menuCount > 0, "Menu count should be greater than zero");
165165
166166 // Ensure all the menus are started first
167167 menuContent.__contentActive = true;
168− for (var i = 0; i < menu_count; i++) {
168+ for (var i = 0; i < menuCount; i++) {
169169 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", true);
170170 }
171171 // release the content to call stop.
172172 menuContent.releaseContent();
173− for (var i = 0; i < menu_count; i++) {
173+ for (var i = 0; i < menuCount; i++) {
174174 tryCompare(indicator_status[get_test_menu_objecName(i)], "started", false);
175175 }
176176 }
177177
178− // Header title should be the same as the item
179− function test_menu_header() {
180− var menu_count = indicatorsModel.count;
181− verify(menu_count > 0, "Menu count should be greater than zero");
182−
183− var header = findChild(menuContent, "header")
184−
185− for (var i = 0; i < menu_count; i++) {
186− activate_content(i);
187−
188− var menu_title = indicatorsModel.get(i).title;
189− compare(header.title, menu_title, "Header doesnt match menu title for menu " + i);
190− }
191− }
192−
193178 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
194179 function test_multi_activate() {
195− var menu_count = indicatorsModel.count;
196− verify(menu_count > 0, "Menu count should be greater than zero");
180+ var menuCount = indicatorsModel.count;
181+ verify(menuCount > 0, "Menu count should be greater than zero");
197182
198183 for (var i = 0; i < 100; i++) {
199− activate_content(i % menu_count);
200− compare(menuContent.currentMenuIndex, i%menu_count);
184+ activate_content(i % menuCount);
185+ compare(menuContent.currentMenuIndex, i%menuCount);
201186 }
202187 wait(100);
203188 }
tests/qmltests/tst_Shell.qml+77−18
@@ -19,8 +19,10 @@
1919
2020import QtQuick 2.0
2121import QtTest 1.0
22+import GSettings 1.0
2223import Ubuntu.Application 0.1
2324import Unity.Test 0.1 as UT
25+import Powerd 0.1
2426
2527import "../.."
2628
@@ -53,26 +55,30 @@ Item {
5355 when: windowShown
5456
5557 function initTestCase() {
56− // swipe away the greeter/lockscreen
57− var touchX = shell.width - (shell.edgeSize / 2);
58− var touchY = shell.height / 2;
59− touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
60−
61− var dash = findChild(shell, "dash");
62− // wait until the animation has finished
63− tryCompare(dash, "contentScale", 1.0);
64− tryCompare(dash, "opacity", 1.0);
58+ swipeAwayGreeter();
6559 }
6660
6761 function cleanup() {
62+ // If a test invoked the greeter, make sure we swipe it away again
63+ var greeter = findChild(shell, "greeter");
64+ if (greeter.shown) {
65+ swipeAwayGreeter();
66+ }
67+
6868 // kill all (fake) running apps
69− ApplicationManager.mainStageApplications.clear();
70− ApplicationManager.sideStageApplications.clear();
69+ killApps(ApplicationManager.mainStageApplications);
70+ killApps(ApplicationManager.sideStageApplications);
7171
7272 var dashHome = findChild(shell, "DashHome");
7373 swipeUntilScopeViewIsReached(dashHome);
7474 }
7575
76+ function killApps(apps) {
77+ while (apps.count > 0) {
78+ ApplicationManager.stopProcess(apps.get(0));
79+ }
80+ }
81+
7682 /*
7783 Test the effect of a right-edge drag on the dash in 3 situations:
7884 1 - when no application has been launched yet
@@ -100,15 +106,52 @@ Item {
100106 // Minimize that application once again
101107 swipeFromLeftEdge();
102108
103− // kill it
104− ApplicationManager.mainStageApplications.clear();
105−
109+ // Right edge behavior should now be the same as before that app,
110+ // was launched. Manually cleanup beforehand to get to initial
111+ // state.
112+ cleanup();
106113 waitForUIToSettle();
107−
108− // Right edge behavior should now be the same as before that app was launched
109114 checkRightEdgeDragWithNoRunningApps();
110115 }
111116
117+ function test_suspend() {
118+ var greeter = findChild(shell, "greeter");
119+
120+ // Launch an app from the launcher
121+ dragLauncherIntoView();
122+ tapOnAppIconInLauncher();
123+ waitUntilApplicationWindowIsFullyVisible();
124+
125+ var mainApp = ApplicationManager.mainStageFocusedApplication;
126+ tryCompareFunction(function() { return mainApp != null; }, true);
127+
128+ // Try to suspend while proximity is engaged...
129+ Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
130+ tryCompare(greeter, "showProgress", 0);
131+
132+ // Now really suspend
133+ Powerd.displayPowerStateChange(Powerd.Off, 0);
134+ tryCompare(greeter, "showProgress", 1);
135+ tryCompare(ApplicationManager, "mainStageFocusedApplication", null);
136+
137+ // And wake up
138+ Powerd.displayPowerStateChange(Powerd.On, 0);
139+ tryCompare(ApplicationManager, "mainStageFocusedApplication", mainApp);
140+ tryCompare(greeter, "showProgress", 1);
141+ }
142+
143+ function swipeAwayGreeter() {
144+ var greeter = findChild(shell, "greeter");
145+ tryCompare(greeter, "showProgress", 1);
146+
147+ var touchX = shell.width - (shell.edgeSize / 2);
148+ var touchY = shell.height / 2;
149+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
150+
151+ // wait until the animation has finished
152+ tryCompare(greeter, "showProgress", 0);
153+ }
154+
112155 /*
113156 Perform a right-edge drag when the Dash is being show and there are
114157 no running/minimized apps to be restored.
@@ -123,8 +166,8 @@ Item {
123166
124167 var dash = findChild(shell, "dash");
125168 // check that dash has normal scale and opacity
126− compare(dash.contentScale, 1.0);
127− compare(dash.opacity, 1.0);
169+ tryCompare(dash.contentScale, 1.0);
170+ tryCompare(dash.opacity, 1.0);
128171
129172 touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
130173 true /* beginTouch */, false /* endTouch */);
@@ -277,5 +320,21 @@ Item {
277320 }
278321 } while (!isStill);
279322 }
323+
324+ function test_wallpaper_data() {
325+ return [
326+ {tag: "red", url: "tests/data/unity/backgrounds/red.png", expectedUrl: "tests/data/unity/backgrounds/red.png"},
327+ {tag: "blue", url: "tests/data/unity/backgrounds/blue.png", expectedUrl: "tests/data/unity/backgrounds/blue.png"},
328+ {tag: "invalid", url: "invalid", expectedUrl: shell.defaultBackground},
329+ {tag: "empty", url: "", expectedUrl: shell.defaultBackground}
330+ ]
331+ }
332+
333+ function test_wallpaper(data) {
334+ var backgroundImage = findChild(shell, "backgroundImage")
335+ GSettingsController.setPictureUri(data.url)
336+ tryCompareFunction(function() { return backgroundImage.source.toString().indexOf(data.expectedUrl) !== -1; }, true)
337+ tryCompare(backgroundImage, "status", Image.Ready)
338+ }
280339 }
281340}
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+1−1
@@ -19,7 +19,7 @@
1919
2020#include <QtCore/QObject>
2121#include <QtTest/QTest>
22−#include <QtQml/QtQml>
22+#include <QtQml/qqml.h>
2323
2424class TouchEventSequenceWrapper : public QObject
2525{
tests/utils/modules/Unity/Test/UnityTestCase.qml+21−2
@@ -69,9 +69,28 @@ TestCase {
6969 }
7070
7171
72− // Find an object with the given name recursively, starting
73− // at the given object "obj"
72+ // Find an object with the given name in the children tree of "obj"
7473 function findChild(obj,objectName) {
74+ var childs = new Array(0);
75+ childs.push(obj)
76+ while (childs.length > 0) {
77+ if (childs[0].objectName == objectName) {
78+ return childs[0]
79+ }
80+ for (var i in childs[0].children) {
81+ childs.push(childs[0].children[i])
82+ }
83+ childs.splice(0, 1);
84+ }
85+ return undefined;
86+ }
87+
88+ // Find an object with the given name in the children tree of "obj"
89+ // Including invisible children like animations, timers etc.
90+ // Note: you should use findChild if you're not sure you need this
91+ // as this tree is much bigger and might contain stuff that goes
92+ // away randomly.
93+ function findInvisibleChild(obj,objectName) {
7594 var childs = new Array(0);
7695 childs.push(obj)
7796 while (childs.length > 0) {