on xenial help sign in
Merge trunk and resolve conflict

[ Michael Terry ] * Skip the tutorial entirely when in desktop mode. This fixes the emulator not being able to pass the tutorial. * Skip the tutorial entirely when in desktop mode. This fixes the emulator not being able to pass the tutorial. * Drop gcc 4.9 requirement in debian/rules * Make the test more stable * Optimize scope.preview() calls (LP: #1478022) [ Alberto Mardegan ] * Set currentIndex after the model change is completed (LP: #1433442) [ Albert Astals Cid ] * Make the test pass again after rework of appmanager * fix testPhoneStage [ CI Train Bot ] * New rebuild forced. [ Daniel d'Andrada ] * Fix and clean up InputInfo plugin [ Rodney Dawes ] * Do not call verify in the payments button code. (LP: #1468038) [ Albert Astals Cid ] * Add a test that checks that items in an expandable are the correct size after expanding the expandable * Fix the expandable test * Replace some waitForRendering with wait(0) * Workaround for bug 1473471 (LP: #1473471) [ Gary.Wzl ] * Fix layout issue when adding audio track widget in expandable widget [ handsome_feng ] * Added indication for running apps. (LP: #1471577) * Adjust the color to highlighting indicator. (LP: #1457098) * Convert the MouseArea to MultiPointTouchArea to track multiple touch points. (LP: #1445472) [ handsome_feng<445865575@qq.com> ] * Convert the MouseArea to MultiPointTouchArea to track multiple touch points. (LP: #1445472) [ Kevin Gunn ] * No change, rebuild for gcc5 [ CI Train Bot ] * Resync trunk. added: po/cy.po * Resync trunk. added: po/cy.po [ Daniel d'Andrada ] * Stages now control the "requestedState" property of applications * Stages now control the "requestedState" property of applications arrow label (LP: #1431564) [ Daniel d'Andrada ] * TouchRegistry: remove null candidates from list of candidates (LP: #1473492) [ Lukáš Tinkl ] * Fix power dialogs on desktop * Provide DBUS compatibility with various session services (suspend/hibernate, lock/unlock, screensaver, etc) * React on PrtScr keyboard shortcut for taking screenshots on desktop (LP: #1474149) * launcher parity: close apps from quicklist (LP: #1457201) [ Michael Zanetti ] * Implement first edition for a desktop Alt+Tab spread * drop the gcc-4.9 dependency (LP: #1452348) [ Mirco Müller ] * Added corresponding tests and visual tweaks to a launcher-item's progress-overlay. * Added corresponding tests and visual tweaks to a launcher-item's progress-overlay. * Implemented alert/wiggle feature for launcher-icons. * Implemented alert/wiggle feature for launcher-icons. * makes left swip reset the search string. (LP: #1413791) * Don't expand indicators when tap to return to call. (LP: #1453217) * makes left swip reset the search string. (LP: #1413791)

commit8989975880d62214d42fc9d881b5632cdea9f8e7 authorGary.Wzl <Gary.Wang@canonical.com> dateWed 26 Aug 2015, 15:47 (11 years ago) parent74fad7a09ff98f
203 files changed+4339−964
CMakeLists.txt+1−1 debian/changelog+101−25 debian/control+7−9 debian/rules−5 libs/UbuntuGestures/TouchRegistry.cpp+60−14 libs/UbuntuGestures/TouchRegistry.h+4−1 plugins/AccountsService/AccountsService.cpp+1−2 plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−2 plugins/Greeter/Unity/Launcher/launcheritem.cpp+14 plugins/Greeter/Unity/Launcher/launcheritem.h+4 plugins/Greeter/Unity/Launcher/launchermodelas.cpp+28−12 plugins/Greeter/Unity/Launcher/launchermodelas.h+1 plugins/ScreenGrabber/screengrabber.cpp+31−20 plugins/ScreenGrabber/screengrabber.h+8−6 plugins/Ubuntu/Payments/Payments.cpp−2 plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux.cpp+2−11 plugins/Unity/Launcher/CMakeLists.txt+1−2 plugins/Unity/Launcher/asadapter.cpp+4−2 plugins/Unity/Launcher/asadapter.h+1−4 plugins/Unity/Launcher/dbusinterface.cpp+42−12 plugins/Unity/Launcher/dbusinterface.h+2 plugins/Unity/Launcher/gsettings.cpp+1−1 plugins/Unity/Launcher/launcheritem.cpp+33 plugins/Unity/Launcher/launcheritem.h+4 plugins/Unity/Launcher/launchermodel.cpp+77−26 plugins/Unity/Launcher/launchermodel.h+3−1 plugins/Unity/Launcher/quicklistentry.cpp+6−2 plugins/Unity/Launcher/quicklistentry.h+3−1 plugins/Unity/Launcher/quicklistmodel.cpp+12−1 plugins/Unity/Launcher/quicklistmodel.h+3−1 plugins/Unity/Session/CMakeLists.txt+1 plugins/Unity/Session/dbusunitysessionservice.cpp+372−24 plugins/Unity/Session/dbusunitysessionservice.h+200−16 plugins/Unity/Session/plugin.cpp+2 plugins/Utils/CMakeLists.txt+1 po/am.po+2−2 po/ar.po+2−2 po/ast.po+2−2 po/az.po+2−2 po/be.po+2−2 po/bg.po+2−2 po/br.po+5−5 po/bs.po+2−2 po/ca.po+3−3 po/ca@valencia.po+2−2 po/ce.po+2−2 po/ckb.po+2−2 po/cs.po+2−2 po/cy.po+2−2 po/da.po+2−2 po/de.po+2−2 po/el.po+3−3 po/en_AU.po+2−2 po/en_GB.po+3−3 po/eo.po+2−2 po/es.po+3−3 po/eu.po+2−2 po/fa.po+2−2 po/fi.po+2−2 po/fo.po+2−2 po/fr.po+2−2 po/ga.po+2−2 po/gd.po+2−2 po/gl.po+2−2 po/gv.po+2−2 po/he.po+2−2 po/hi.po+2−2 po/hr.po+2−2 po/hu.po+7−7 po/hy.po+2−2 po/ia.po+22−22 po/id.po+2−2 po/is.po+2−2 po/it.po+3−3 po/ja.po+5−5 po/km.po+40−40 po/kn.po+2−2 po/ko.po+2−2 po/ku.po+2−2 po/ky.po+2−2 po/lo.po+2−2 po/lt.po+2−2 po/lv.po+2−2 po/ml.po+2−2 po/mr.po+12−12 po/ms.po+81−73 po/my.po+2−2 po/nb.po+2−2 po/nl.po+2−2 po/nn.po+2−2 po/oc.po+2−2 po/or.po+2−2 po/pa.po+2−2 po/pl.po+2−2 po/pt.po+2−2 po/pt_BR.po+12−12 po/ro.po+2−2 po/ru.po+2−2 po/shn.po+2−2 po/si.po+75−13 po/sk.po+2−2 po/sl.po+2−2 po/sq.po+2−2 po/sr.po+2−2 po/sv.po+2−2 po/sw.po+2−2 po/ta.po+2−2 po/te.po+2−2 po/th.po+2−2 po/tr.po+40−40 po/ug.po+2−2 po/uk.po+3−3 po/ur.po+2−2 po/uz.po+2−2 po/vi.po+2−2 po/xh.po+2−2 po/zh_CN.po+2−2 po/zh_HK.po+2−2 po/zh_TW.po+2−2 qml/Components/PhysicalKeysMapper.qml+24 qml/Components/ScreenGrabber.qml+1−5 qml/Components/Unity8Settings.qml+50 qml/Components/UnityInputInfo/UnityInputInfo.qml+2 qml/Dash/Dash.qml+1−1 qml/Dash/DashContent.qml+1 qml/Dash/GenericScopeView.qml+66−31 qml/Dash/PreviewListView.qml+17−6 qml/Dash/Previews/PreviewActions.qml+1−1 qml/Dash/Previews/PreviewAudioPlayback.qml+1−1 qml/Dash/Previews/PreviewHeader.qml+1−1 qml/Dash/Previews/PreviewRatingDisplay.qml+1−1 qml/Dash/ScopesList.qml+1−1 qml/Launcher/Launcher.qml+2−1 qml/Launcher/LauncherDelegate.qml+113−15 qml/Launcher/LauncherPanel.qml+95−14 qml/Launcher/graphics/progressbar-fill@30.png qml/Launcher/graphics/progressbar-fill@30.sci−5 qml/Launcher/graphics/progressbar-trough@30.png qml/Launcher/graphics/progressbar-trough@30.sci−5 qml/OrientedShell.qml+6−3 qml/Panel/IndicatorItem.qml+3−3 qml/Panel/IndicatorItemRow.qml+1−1 qml/Panel/IndicatorsBar.qml+1−1 qml/Panel/IndicatorsMenu.qml+3−1 qml/Panel/Panel.qml+1 qml/Shell.qml+23−21 qml/Stages/ApplicationWindow.qml+3 qml/Stages/DecoratedWindow.qml+72−8 qml/Stages/DesktopStage.qml+511−57 qml/Stages/PhoneStage.qml+10 qml/Stages/SpreadMaths.qml+164 qml/Stages/TabletStage.qml+14 qml/Stages/graphics/window-close.svg+64 qml/Tutorial/Tutorial.qml−7 qml/Tutorial/TutorialContent.qml+2−9 tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+47 tests/mocks/Unity/Application/ApplicationInfo.cpp+40−6 tests/mocks/Unity/Application/ApplicationInfo.h+7 tests/mocks/Unity/Application/ApplicationManager.cpp+2−49 tests/mocks/Unity/Application/ApplicationManager.h−7 tests/mocks/Unity/CMakeLists.txt+1−1 tests/mocks/Unity/Launcher/CMakeLists.txt+1−1 tests/mocks/Unity/Launcher/MockLauncherItem.cpp+22 tests/mocks/Unity/Launcher/MockLauncherItem.h+4 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+54−1 tests/mocks/Unity/Launcher/MockLauncherModel.h+4 tests/mocks/Unity/fake_scope.cpp+5−3 tests/mocks/Unity/fake_scope.h+2−2 tests/mocks/Unity/fake_scopesoverview.cpp+1−1 tests/mocks/Unity/fake_scopesoverview.h+1−1 tests/mocks/Utils/CMakeLists.txt+3−1 tests/mocks/Utils/plugin.cpp+9−1 tests/plugins/CMakeLists.txt+1 tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−2 tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+3 tests/plugins/ScreenGrabber/CMakeLists.txt+14 tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp+65 tests/plugins/ScreenGrabber/grabber.qml+31 tests/plugins/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+83−11 tests/plugins/Unity/Session/sessionbackendtest.cpp+54−8 tests/qmltests/Components/tst_PhysicalKeysMapper.qml+5 tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+146−6 tests/qmltests/Dash/tst_PageHeader.qml+16−12 tests/qmltests/Launcher/tst_Launcher.qml+246−12 tests/qmltests/Panel/tst_IndicatorItem.qml+12−12 tests/qmltests/Panel/tst_Panel.qml+18 tests/qmltests/Stages/ApplicationCheckBox.qml+21 tests/qmltests/Stages/tst_DesktopStage.qml+2−2 tests/qmltests/Stages/tst_PhoneStage.qml+76−3 tests/qmltests/Stages/tst_TabletStage.qml+161−1 tests/qmltests/Tutorial/tst_Tutorial.qml+7−5 tests/qmltests/tst_OrientedShell.qml+3−3 tests/qmltests/tst_Shell.qml+390−23 tests/qmltests/tst_ShellWithPin.qml+5−4 tests/scripts/README+9 tests/scripts/alert-launcher-icon.sh+3 tests/scripts/get-progress.sh+3 tests/scripts/list-launcher-icons.sh+3 tests/scripts/set-count-visible.sh+3 tests/scripts/set-count.sh+3 tests/scripts/set-progress.sh+3 tests/utils/modules/Unity/Test/UnityTestCase.qml+9
CMakeLists.txt+1−1
@@ -56,7 +56,7 @@ find_package(Qt5DBus 5.2 REQUIRED)
5656find_package(Qt5Concurrent 5.2 REQUIRED)
5757find_package(Qt5Sql 5.2 REQUIRED)
5858
59−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
59+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=7)
6060
6161# Standard install paths
6262include(GNUInstallDirs)
debian/changelog+101−25
@@ -1,44 +1,120 @@
1−unity8 (8.10+15.10.20150721-0ubuntu1) wily; urgency=medium
1+unity8 (8.11+15.04.20150821-0ubuntu1) vivid; urgency=medium
2+
3+ [ Michael Terry ]
4+ * Skip the tutorial entirely when in desktop mode. This fixes the
5+ emulator not being able to pass the tutorial.
6+ * Skip the tutorial entirely when in desktop mode. This fixes the
7+ emulator not being able to pass the tutorial.
8+
9+ -- CI Train Bot <ci-train-bot@canonical.com> Fri, 21 Aug 2015 14:34:50 +0000
10+
11+unity8 (8.11+15.04.20150817-0ubuntu1) vivid; urgency=medium
212
313 [ Albert Astals Cid ]
4− * Add context for "Please re-enter" (LP: #1377195)
5− * Fix i18n in scope tool (LP: #1317541)
6− * Fix warning when doing make try
7− * New phone background provided by design (LP: #1364537)
8− * PreviewExpandable should be not expanded on startup
9− * Update state of indicators bar only when the user is actively
10− dragging himself (LP: #1457044)
11− * icon-actions preview widget
12− * make testPageHeader more stable
14+ * Drop gcc 4.9 requirement in debian/rules
15+ * Make the test more stable
16+ * Optimize scope.preview() calls (LP: #1478022)
17+
18+ [ Alberto Mardegan ]
19+ * Set currentIndex after the model change is completed (LP: #1433442)
20+
21+ -- CI Train Bot <ci-train-bot@canonical.com> Mon, 17 Aug 2015 11:57:31 +0000
22+
23+unity8 (8.11+15.04.20150814-0ubuntu1) vivid; urgency=medium
24+
25+ [ Albert Astals Cid ]
26+ * Make the test pass again after rework of appmanager
27+ * fix testPhoneStage
1328
1429 [ CI Train Bot ]
15− * Resync trunk.
30+ * New rebuild forced.
1631
17− [ Josh Arenson ]
18− * Add handling for the location page to the autopilot settings wizard
19− test
32+ [ Daniel d'Andrada ]
33+ * Fix and clean up InputInfo plugin
2034
21− [ Michael Zanetti ]
22− * allow opening the manage dash area by clicking with a mouse on the
23− arrow label
35+ [ Rodney Dawes ]
36+ * Do not call verify in the payments button code. (LP: #1468038)
37+
38+ -- CI Train Bot <ci-train-bot@canonical.com> Fri, 14 Aug 2015 09:53:11 +0000
39+
40+unity8 (8.11+15.04.20150812-0ubuntu1) vivid; urgency=medium
41+
42+ [ Albert Astals Cid ]
43+ * Add a test that checks that items in an expandable are the correct
44+ size after expanding the expandable
45+ * Fix the expandable test
46+ * Replace some waitForRendering with wait(0)
47+ * Workaround for bug 1473471 (LP: #1473471)
48+
49+ [ Gary.Wzl ]
50+ * Fix layout issue when adding audio track widget in expandable widget
2451
2552 [ handsome_feng ]
26− * Added touch state on navigation. (LP: #1383213)
53+ * Added indication for running apps. (LP: #1471577)
54+ * Adjust the color to highlighting indicator. (LP: #1457098)
55+ * Convert the MouseArea to MultiPointTouchArea to track multiple touch
56+ points. (LP: #1445472)
2757
2858 [ handsome_feng<445865575@qq.com> ]
29− * Added touch state on navigation. (LP: #1383213)
59+ * Convert the MouseArea to MultiPointTouchArea to track multiple touch
60+ points. (LP: #1445472)
3061
31− -- CI Train Bot <ci-train-bot@canonical.com> Tue, 21 Jul 2015 09:10:05 +0000
62+ -- CI Train Bot <ci-train-bot@canonical.com> Wed, 12 Aug 2015 08:41:24 +0000
3263
33−unity8 (8.10+15.10.20150713-0ubuntu1) wily; urgency=medium
64+unity8 (8.10+15.10.20150804-0ubuntu1) wily; urgency=medium
3465
35− [ Albert Astals Cid ]
36− * Revert Session Screenshotter (LP: #1473476)
66+ [ Kevin Gunn ]
67+ * No change, rebuild for gcc5
68+
69+ [ CI Train Bot ]
70+ * Resync trunk. added: po/cy.po
71+ * Resync trunk. added: po/cy.po
72+
73+ [ Daniel d'Andrada ]
74+ * Stages now control the "requestedState" property of applications
75+ * Stages now control the "requestedState" property of applications
76+
77+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 04 Aug 2015 20:56:05 +0000
78+
79+unity8 (8.10+15.10.20150729-0ubuntu1) wily; urgency=medium
80+
81+ [ CI Train Bot ]
82+ * Resync trunk.
83+ * allow opening the manage dash area by clicking with a mouse on the
84+ arrow label (LP: #1431564)
3785
3886 [ Daniel d'Andrada ]
39− * Dismiss the apps spread if greeter gets shown (LP: #1471609)
87+ * TouchRegistry: remove null candidates from list of candidates (LP:
88+ #1473492)
89+
90+ [ Lukáš Tinkl ]
91+ * Fix power dialogs on desktop
92+ * Provide DBUS compatibility with various session services
93+ (suspend/hibernate, lock/unlock, screensaver, etc)
94+ * React on PrtScr keyboard shortcut for taking screenshots on desktop
95+ (LP: #1474149)
96+ * launcher parity: close apps from quicklist (LP: #1457201)
97+
98+ [ Michael Zanetti ]
99+ * Implement first edition for a desktop Alt+Tab spread
100+ * drop the gcc-4.9 dependency (LP: #1452348)
101+
102+ [ Mirco Müller ]
103+ * Added corresponding tests and visual tweaks to a launcher-item's
104+ progress-overlay.
105+ * Added corresponding tests and visual tweaks to a launcher-item's
106+ progress-overlay.
107+ * Implemented alert/wiggle feature for launcher-icons.
108+ * Implemented alert/wiggle feature for launcher-icons.
109+
110+ [ handsome_feng ]
111+ * makes left swip reset the search string. (LP: #1413791)
112+
113+ [ handsome_feng<445865575@qq.com> ]
114+ * Don't expand indicators when tap to return to call. (LP: #1453217)
115+ * makes left swip reset the search string. (LP: #1413791)
40116
41− -- CI Train Bot <ci-train-bot@canonical.com> Mon, 13 Jul 2015 14:17:20 +0000
117+ -- CI Train Bot <ci-train-bot@canonical.com> Wed, 29 Jul 2015 12:39:01 +0000
42118
43119unity8 (8.10+15.10.20150708.2-0ubuntu1) wily; urgency=medium
44120
debian/control+7−9
@@ -6,12 +6,10 @@ Build-Depends: cmake,
66 dbus-test-runner,
77 debhelper (>= 9),
88 doxyqml,
9−# We rely on C++11 features, and to prevent from ABI breaks
10−# in libstdc++ causing us issues, we explicitly select a G++
11−# version. To allow cross-compiling to work, we also must
12−# append :native to g++-4.9 so we don't try to run armhf g++
9+# To allow cross-compiling to work, we also must append :native
10+# to g++ so we don't try to run armhf g++
1311# on an x86 CPU for eaxmple, when cross-compiling.
14− g++-4.9:native,
12+ g++:native,
1513 libandroid-properties-dev,
1614 graphviz,
1715 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
@@ -30,7 +28,7 @@ Build-Depends: cmake,
3028 libqt5xmlpatterns5-dev,
3129 libsystemsettings-dev,
3230 libudev-dev,
33− libunity-api-dev (>= 7.97),
31+ libunity-api-dev (>= 7.99),
3432 libusermetricsoutput1-dev,
3533 libxcb1-dev,
3634 pkg-config,
@@ -101,7 +99,7 @@ Depends: gsettings-desktop-schemas,
10199 qtdeclarative5-ubuntu-telephony0.1,
102100 qtdeclarative5-ubuntu-web-plugin,
103101 ubuntu-system-settings,
104− unity-launcher-impl-4,
102+ unity-launcher-impl-7,
105103 unity8-common (= ${source:Version}),
106104 unity8-private (= ${binary:Version}),
107105 unity8-private | unity-launcher-impl,
@@ -130,7 +128,7 @@ Depends: qml-module-qtquick-layouts,
130128 unity-application-impl-6,
131129 unity-notifications-impl-3,
132130 unity-plugin-scopes | unity-scopes-impl,
133− unity-scopes-impl-6,
131+ unity-scopes-impl-7,
134132 unity8-fake-env | unity-application-impl,
135133 ${misc:Depends},
136134Breaks: unity8 (<< 7.86),
@@ -191,7 +189,7 @@ Depends: accountsservice-ubuntu-schemas (>= 0.0.3),
191189 ${misc:Depends},
192190 ${shlibs:Depends},
193191Provides: unity-launcher-impl,
194− unity-launcher-impl-4,
192+ unity-launcher-impl-7,
195193Description: Unity 8 private libs
196194 The Unity 8 shell is the primary user interface for Ubuntu devices.
197195 .
debian/rules−5
@@ -8,11 +8,6 @@ include /usr/share/dpkg/default.mk
88export DPKG_GENSYMBOLS_CHECK_LEVEL=4
99export NO_PNG_PKG_MANGLE=1
1010
11−# Explicitly selecting a G{CC,++}-version here to avoid accidental
12−# ABI breaks introduced by toolchain updates.
13−export CC=$(DEB_HOST_GNU_TYPE)-gcc-4.9
14−export CXX=$(DEB_HOST_GNU_TYPE)-g++-4.9
15−
1611%:
1712 dh $@ --parallel --fail-missing --with python3
1813
libs/UbuntuGestures/TouchRegistry.cpp+60−14
@@ -271,6 +271,8 @@ void TouchRegistry::addCandidateOwnerForTouch(int id, QQuickItem *candidate)
271271 this, &TouchRegistry::rejectCandidateOwnerForTouch);
272272
273273 touchInfo->candidates.append(candidateInfo);
274+
275+ connect(candidate, &QObject::destroyed, this, [=](){ pruneNullCandidatesForTouch(id); });
274276}
275277
276278void TouchRegistry::addTouchWatcher(int touchId, QQuickItem *watcher)
@@ -296,25 +298,53 @@ void TouchRegistry::removeCandidateOwnerForTouch(int id, QQuickItem *candidate)
296298 Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id);
297299 if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); }
298300
299− int indexRemoved = -1;
300301
301302 // TODO: check if the candidate is in fact the owner of the touch
302303
303− for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
304− CandidateInfo &candidateInfo = touchInfo->candidates[i];
305− if (candidateInfo.item == candidate) {
306− Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
307− if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
308− qCritical("TouchRegistry: touch owner is being removed.");
309− }
310− delete candidateInfo.inactivityTimer;
311− candidateInfo.inactivityTimer = nullptr;
312− touchInfo->candidates.removeAt(i);
313− indexRemoved = i;
304+ bool removed = false;
305+ for (int i = 0; i < touchInfo->candidates.count() && !removed; ++i) {
306+ if (touchInfo->candidates[i].item == candidate) {
307+ removeCandidateOwnerForTouchByIndex(touchInfo, i);
308+ removed = true;
314309 }
315310 }
311+}
312+
313+void TouchRegistry::pruneNullCandidatesForTouch(int touchId)
314+{
315+ #if TOUCHREGISTRY_DEBUG
316+ UG_DEBUG << "pruneNullCandidatesForTouch touchId" << touchId;
317+ #endif
318+
319+ Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(touchId);
320+ if (!touchInfo) {
321+ // doesn't matter as touch is already gone.
322+ return;
323+ }
316324
317− if (indexRemoved == 0) {
325+ int i = 0;
326+ while (i < touchInfo->candidates.count()) {
327+ if (touchInfo->candidates[i].item.isNull()) {
328+ removeCandidateOwnerForTouchByIndex(touchInfo, i);
329+ } else {
330+ ++i;
331+ }
332+ }
333+}
334+
335+void TouchRegistry::removeCandidateOwnerForTouchByIndex(Pool<TouchRegistry::TouchInfo>::Iterator &touchInfo,
336+ int candidateIndex)
337+{
338+ // TODO: check if the candidate is in fact the owner of the touch
339+
340+ Q_ASSERT(candidateIndex < touchInfo->candidates.count());
341+
342+ if (candidateIndex == 0 && touchInfo->candidates[candidateIndex].state != CandidateInfo::Undecided) {
343+ qCritical("TouchRegistry: touch owner is being removed.");
344+ }
345+ removeCandidateHelper(touchInfo, candidateIndex);
346+
347+ if (candidateIndex == 0) {
318348 // the top candidate has been removed. if the new top candidate
319349 // wants the touch let him know he's now the owner.
320350 if (touchInfo->isOwned()) {
@@ -359,6 +389,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
359389 touchInfo->candidates.append(candidateInfo);
360390 // it's the last one
361391 candidateIndex = touchInfo->candidates.count() - 1;
392+ connect(candidate, &QObject::destroyed, this, [=](){ pruneNullCandidatesForTouch(id); });
362393 }
363394
364395 // If it's the top candidate it means it's now the owner. Let
@@ -430,7 +461,7 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
430461 QCoreApplication::sendEvent(candidate, &lostOwnershipEvent);
431462 }
432463
433− touchInfo->candidates.removeAt(rejectedCandidateIndex);
464+ removeCandidateHelper(touchInfo, rejectedCandidateIndex);
434465
435466 if (rejectedCandidateIndex == 0) {
436467 // the top candidate has been removed. if the new top candidate
@@ -441,6 +472,21 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
441472 }
442473}
443474
475+void TouchRegistry::removeCandidateHelper(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex)
476+{
477+ {
478+ CandidateInfo &candidateInfo = touchInfo->candidates[candidateIndex];
479+
480+ delete candidateInfo.inactivityTimer;
481+ candidateInfo.inactivityTimer = nullptr;
482+
483+ if (candidateInfo.item) {
484+ disconnect(candidateInfo.item.data(), nullptr, this, nullptr);
485+ }
486+ }
487+ touchInfo->candidates.removeAt(candidateIndex);
488+}
489+
444490////////////////////////////////////// TouchRegistry::TouchInfo ////////////////////////////////////
445491
446492TouchRegistry::TouchInfo::TouchInfo(int id)
libs/UbuntuGestures/TouchRegistry.h+4−1
@@ -149,7 +149,6 @@ private:
149149 // yet because of undecided candidates higher up.
150150 InterimOwner = 2
151151 } state;
152− // TODO: Prune candidates that become null and resolve ownership accordingly.
153152 QPointer<QQuickItem> item;
154153 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
155154 };
@@ -172,6 +171,10 @@ private:
172171 QList<QPointer<QQuickItem>> watchers;
173172 };
174173
174+ void pruneNullCandidatesForTouch(int touchId);
175+ void removeCandidateOwnerForTouchByIndex(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex);
176+ void removeCandidateHelper(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex);
177+
175178 Pool<TouchInfo>::Iterator findTouchInfo(int id);
176179
177180 void deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QTouchEvent *event);
plugins/AccountsService/AccountsService.cpp+1−2
@@ -141,9 +141,9 @@ void AccountsService::updateDemoEdges(bool async)
141141 this, [this](QDBusPendingCallWatcher* watcher) {
142142
143143 QDBusPendingReply<QDBusVariant> reply = *watcher;
144+ watcher->deleteLater();
144145 if (reply.isError()) {
145146 qWarning() << "Failed to get 'demo-edges' property - " << reply.error().message();
146− watcher->deleteLater();
147147 return;
148148 }
149149
@@ -152,7 +152,6 @@ void AccountsService::updateDemoEdges(bool async)
152152 m_demoEdges = demoEdges;
153153 Q_EMIT demoEdgesChanged();
154154 }
155− watcher->deleteLater();
156155 });
157156 if (!async) {
158157 watcher->waitForFinished();
plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−2
@@ -1,5 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
32pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
43
54add_definitions(-DSM_BUSNAME=systemBus)
plugins/Greeter/Unity/Launcher/launcheritem.cpp+14
@@ -31,6 +31,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
3131 m_count(0),
3232 m_countVisible(false),
3333 m_focused(false),
34+ m_alerting(false),
3435 m_quickList(new QuickListModel(this))
3536{
3637 QuickListEntry nameAction;
@@ -165,6 +166,19 @@ void LauncherItem::setFocused(bool focused)
165166 }
166167}
167168
169+bool LauncherItem::alerting() const
170+{
171+ return m_alerting;
172+}
173+
174+void LauncherItem::setAlerting(bool alerting)
175+{
176+ if (m_alerting != alerting) {
177+ m_alerting = alerting;
178+ Q_EMIT alertingChanged(alerting);
179+ }
180+}
181+
168182unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
169183{
170184 return m_quickList;
plugins/Greeter/Unity/Launcher/launcheritem.h+4
@@ -41,6 +41,7 @@ public:
4141 int count() const override;
4242 bool countVisible() const override;
4343 bool focused() const override;
44+ bool alerting() const override;
4445
4546 unity::shell::launcher::QuickListModelInterface *quickList() const override;
4647
@@ -54,6 +55,8 @@ private:
5455 void setCount(int count);
5556 void setCountVisible(bool countVisible);
5657 void setFocused(bool focused);
58+ void setAlerting(bool alerting);
59+
5760
5861private:
5962 QString m_appId;
@@ -66,6 +69,7 @@ private:
6669 int m_count;
6770 bool m_countVisible;
6871 bool m_focused;
72+ bool m_alerting;
6973 QuickListModel *m_quickList;
7074
7175 friend class LauncherModel;
plugins/Greeter/Unity/Launcher/launchermodelas.cpp+28−12
@@ -67,11 +67,20 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
6767 return item->progress();
6868 case RoleFocused:
6969 return item->focused();
70+ case RoleRunning:
71+ return item->running();
7072 }
7173
7274 return QVariant();
7375}
7476
77+void LauncherModel::setAlerting(const QString &appId, bool alerting)
78+{
79+ Q_UNUSED(appId)
80+ Q_UNUSED(alerting)
81+ qWarning() << "This is a read only implementation. Cannot set alert-state of items.";
82+}
83+
7584unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
7685{
7786 if (index < 0 || index >= m_list.count()) {
@@ -200,16 +209,20 @@ void LauncherModel::refresh()
200209 Q_FOREACH (LauncherItem* item, m_list) {
201210 bool found = false;
202211 Q_FOREACH(const QVariant &asItem, items) {
203− if (asItem.toMap().value("id").toString() == item->appId()) {
212+ QVariantMap cachedMap = asItem.toMap();
213+ if (cachedMap.value("id").toString() == item->appId()) {
204214 // Only keep and update it if we either show unpinned or it is pinned
205− if (!m_onlyPinned || asItem.toMap().value("pinned").toBool()) {
215+ if (!m_onlyPinned || cachedMap.value("pinned").toBool()) {
206216 found = true;
207− item->setName(asItem.toMap().value("name").toString());
208− item->setIcon(asItem.toMap().value("icon").toString());
209− item->setCount(asItem.toMap().value("count").toInt());
210− item->setCountVisible(asItem.toMap().value("countVisible").toBool());
217+ item->setName(cachedMap.value("name").toString());
218+ item->setIcon(cachedMap.value("icon").toString());
219+ item->setCount(cachedMap.value("count").toInt());
220+ item->setCountVisible(cachedMap.value("countVisible").toBool());
221+ item->setProgress(cachedMap.value("progress").toInt());
222+ item->setRunning(cachedMap.value("running").toBool());
223+
211224 int idx = m_list.indexOf(item);
212− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
225+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleCount, RoleCountVisible, RoleRunning, RoleProgress});
213226 }
214227 break;
215228 }
@@ -247,14 +260,17 @@ void LauncherModel::refresh()
247260 }
248261
249262 if (itemIndex == -1) {
263+ QVariantMap cachedMap = entry.toMap();
250264 // Need to add it. Just add it into the addedIndex to keep same ordering as the list in AS.
251− LauncherItem *item = new LauncherItem(entry.toMap().value("id").toString(),
252− entry.toMap().value("name").toString(),
253− entry.toMap().value("icon").toString(),
265+ LauncherItem *item = new LauncherItem(cachedMap.value("id").toString(),
266+ cachedMap.value("name").toString(),
267+ cachedMap.value("icon").toString(),
254268 this);
255269 item->setPinned(true);
256− item->setCount(entry.toMap().value("count").toInt());
257− item->setCountVisible(entry.toMap().value("countVisible").toBool());
270+ item->setCount(cachedMap.value("count").toInt());
271+ item->setCountVisible(cachedMap.value("countVisible").toBool());
272+ item->setProgress(cachedMap.value("progress").toInt());
273+ item->setRunning(cachedMap.value("running").toBool());
258274 beginInsertRows(QModelIndex(), newPosition, newPosition);
259275 m_list.insert(newPosition, item);
260276 endInsertRows();
plugins/Greeter/Unity/Launcher/launchermodelas.h+1
@@ -41,6 +41,7 @@ public:
4141
4242 QVariant data(const QModelIndex &index, int role) const override;
4343
44+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
4445 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
4546 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
4647 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
plugins/ScreenGrabber/screengrabber.cpp+31−20
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,8 +12,6 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
1715 */
1816
1917#include "screengrabber.h"
@@ -28,27 +26,35 @@
2826
2927#include <QDebug>
3028
31−void saveScreenshot(QImage screenshot, QString filename, QString format, int quality)
29+bool saveScreenshot(const QImage &screenshot, const QString &filename, const QString &format, int quality)
3230{
33− if (!screenshot.save(filename, format.toLatin1().data(), quality))
34− qWarning() << "ScreenShotter: failed to save snapshot!";
31+ if (!screenshot.save(filename, format.toLatin1().data(), quality)) {
32+ qWarning() << "ScreenGrabber: failed to save snapshot!";
33+ return false;
34+ }
35+
36+ return true;
3537}
3638
3739ScreenGrabber::ScreenGrabber(QObject *parent)
38− : QObject(parent),
39− screenshotQuality(0)
40+ : QObject(parent)
4041{
41− QDir screenshotsDir(QStandardPaths::displayName(QStandardPaths::PicturesLocation));
42− screenshotsDir.mkdir("Screenshots");
42+ QDir screenshotsDir;
43+ if (qEnvironmentVariableIsSet("UNITY_TESTING")) {
44+ qDebug() << "Using test environment";
45+ QStandardPaths::setTestModeEnabled(true);
46+ screenshotsDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
47+ } else {
48+ qDebug() << "Using real environment";
49+ screenshotsDir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
50+ }
51+ screenshotsDir.mkpath("Screenshots");
4352 screenshotsDir.cd("Screenshots");
44− if (screenshotsDir.exists())
45− {
53+ if (screenshotsDir.exists()) {
4654 fileNamePrefix = screenshotsDir.absolutePath();
4755 fileNamePrefix.append("/screenshot");
48− }
49− else
50− {
51− qWarning() << "ScreenShotter: failed to create directory at: " << screenshotsDir.absolutePath();
56+ } else {
57+ qWarning() << "ScreenGrabber: failed to create directory at: " << screenshotsDir.absolutePath();
5258 }
5359}
5460
@@ -74,11 +80,16 @@ void ScreenGrabber::captureAndSave()
7480 return;
7581 }
7682
77− QImage screenshot = main_window->grabWindow();
78− QtConcurrent::run(saveScreenshot, screenshot, makeFileName(), getFormat(), screenshotQuality);
83+ const QImage screenshot = main_window->grabWindow();
84+ const QString filename = makeFileName();
85+ qDebug() << "Saving screenshot to" << filename;
86+ auto saveOp = QtConcurrent::run(saveScreenshot, screenshot, filename, getFormat(), screenshotQuality);
87+ if (saveOp.result()) {
88+ Q_EMIT screenshotSaved(filename);
89+ }
7990}
8091
81−QString ScreenGrabber::makeFileName()
92+QString ScreenGrabber::makeFileName() const
8293{
8394 QString fileName(fileNamePrefix);
8495 fileName.append(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz"));
@@ -87,7 +98,7 @@ QString ScreenGrabber::makeFileName()
8798 return fileName;
8899}
89100
90−QString ScreenGrabber::getFormat()
101+QString ScreenGrabber::getFormat() const
91102{
92103 //TODO: This should be configurable (perhaps through gsettings?)
93104 return "png";
plugins/ScreenGrabber/screengrabber.h+8−6
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,8 +12,6 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
1715 */
1816
1917#ifndef SNAPSHOTTER_H
@@ -28,15 +26,19 @@ class ScreenGrabber: public QObject
2826
2927public:
3028 explicit ScreenGrabber(QObject *parent = 0);
29+ ~ScreenGrabber() = default;
3130
3231public Q_SLOTS:
3332 void captureAndSave();
3433
34+Q_SIGNALS:
35+ void screenshotSaved(const QString &filename);
36+
3537private:
36− QString makeFileName();
37− QString getFormat();
38+ QString makeFileName() const;
39+ QString getFormat() const;
3840 QString fileNamePrefix;
39− int screenshotQuality;
41+ int screenshotQuality = -1; // default quality for the format
4042};
4143
4244#endif
plugins/Ubuntu/Payments/Payments.cpp−2
@@ -123,8 +123,6 @@ void Payments::setStoreItemId(const QString &store_item_id)
123123 if (m_store_item_id.isEmpty()) {
124124 return;
125125 }
126−
127− pay_package_item_start_verification(m_package, m_store_item_id.toLocal8Bit().data());
128126}
129127
130128void Payments::setPurchasing(bool is_purchasing)
plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux.cpp+2−11
@@ -101,8 +101,6 @@ void QInputDeviceInfoPrivate::init()
101101 if (!iDevice)
102102 continue;
103103
104− qDebug() << "*** ADDING DEVICE" << eventPath;
105−
106104 iDevice->setTypes(getInputTypes(dev));
107105
108106 if (iDevice->switches().count() > 0 && iDevice->buttons().count() == 0)
@@ -125,12 +123,6 @@ void QInputDeviceInfoPrivate::init()
125123
126124QInputDeviceInfo::InputTypes QInputDeviceInfoPrivate::getInputTypes( struct udev_device *dev)
127125{
128− qDebug() << "******* have input type Keyboard" << udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
129− qDebug() << "******* have input type Mouse" << udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
130− qDebug() << "******* have input type Touchpad" << udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD");
131− qDebug() << "******* have input type Touchscreen" << udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
132− qDebug() << "******* have input type Tablet" << udev_device_get_property_value(dev, "ID_INPUT_TABLET");
133− qDebug() << "******* have input type joystick" << udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
134126 QInputDeviceInfo::InputTypes types = QInputDeviceInfo::Unknown;
135127 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"), "1") == 0 )
136128 types |= QInputDeviceInfo::Keyboard;
@@ -151,6 +143,7 @@ QInputDeviceInfo::InputTypes QInputDeviceInfoPrivate::getInputTypes( struct udev
151143QInputDevice *QInputDeviceInfoPrivate::addDevice(const QString &path)
152144{
153145 QInputDevice *inputDevice = new QInputDevice(this);
146+ inputDevice->setDevicePath(path);
154147
155148 struct libevdev *dev = NULL;
156149 int fd;
@@ -158,17 +151,15 @@ QInputDevice *QInputDeviceInfoPrivate::addDevice(const QString &path)
158151 fd = open(path.toLatin1(), O_RDONLY|O_NONBLOCK);
159152
160153 if (fd == -1) {
161− qDebug() << "Failed to open";
162154 return inputDevice;
163155 }
164156 rc = libevdev_new_from_fd(fd, &dev);
165157 if (rc < 0) {
166− qDebug() << "Failed to init libevdev ("<< strerror(-rc) << ")";
158+ qWarning() << "Failed to init libevdev ("<< strerror(-rc) << ")";
167159 return inputDevice;
168160 }
169161
170162 inputDevice->setName(QString::fromLatin1(libevdev_get_name(dev)));
171− inputDevice->setDevicePath(path);
172163 for (int i = 0; i < EV_MAX; i++) {
173164 if (i == EV_KEY || i == EV_SW || i == EV_REL
174165 || i == EV_REL || i == EV_ABS) {
plugins/Unity/Launcher/CMakeLists.txt+1−2
@@ -1,5 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
32pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
43
54add_definitions(-DSM_BUSNAME=systemBus)
plugins/Unity/Launcher/asadapter.cpp+4−2
@@ -34,12 +34,12 @@ ASAdapter::~ASAdapter()
3434 m_accounts->deleteLater();
3535}
3636
37−void ASAdapter::syncItems(QList<LauncherItem *> m_list)
37+void ASAdapter::syncItems(const QList<LauncherItem*> &list)
3838{
3939 if (m_accounts && !m_user.isEmpty()) {
4040 QList<QVariantMap> items;
4141
42− Q_FOREACH(LauncherItem *item, m_list) {
42+ Q_FOREACH(LauncherItem *item, list) {
4343 items << itemToVariant(item);
4444 }
4545
@@ -56,5 +56,7 @@ QVariantMap ASAdapter::itemToVariant(LauncherItem *item) const
5656 details.insert("count", item->count());
5757 details.insert("countVisible", item->countVisible());
5858 details.insert("pinned", item->pinned());
59+ details.insert("running", item->running());
60+ details.insert("progress", item->progress());
5961 return details;
6062}
plugins/Unity/Launcher/asadapter.h+1−4
@@ -21,7 +21,6 @@
2121
2222class LauncherItem;
2323class AccountsServiceDBusAdaptor;
24−class QDBusInterface;
2524
2625class ASAdapter
2726{
@@ -29,7 +28,7 @@ public:
2928 ASAdapter();
3029 ~ASAdapter();
3130
32− void syncItems(QList<LauncherItem*> m_list);
31+ void syncItems(const QList<LauncherItem*> &list);
3332
3433private:
3534 QVariantMap itemToVariant(LauncherItem *item) const;
@@ -38,8 +37,6 @@ private:
3837 AccountsServiceDBusAdaptor *m_accounts;
3938 QString m_user;
4039
41− QDBusInterface *m_userInterface;
42−
4340 friend class LauncherModelTest;
4441};
4542
plugins/Unity/Launcher/dbusinterface.cpp+42−12
@@ -62,6 +62,8 @@ QString DBusInterface::introspect(const QString &path) const
6262 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
6363 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
6464 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
65+ "<property name=\"progress\" type=\"i\" access=\"readwrite\" />"
66+ "<method name=\"Alert\" />"
6567 "</interface>";
6668 return nodeiface;
6769}
@@ -122,6 +124,20 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
122124 return false;
123125 }
124126
127+ /* Break down the path to just the app id */
128+ bool validpath = true;
129+ QString pathtemp = message.path();
130+ if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
131+ validpath = false;
132+ }
133+ pathtemp.remove("/com/canonical/Unity/Launcher/");
134+ if (pathtemp.indexOf('/') >= 0) {
135+ validpath = false;
136+ }
137+
138+ /* Find ourselves an appid */
139+ QString appid = decodeAppId(pathtemp);
140+
125141 // First handle methods of the Launcher interface
126142 if (message.interface() == "com.canonical.Unity.Launcher") {
127143 if (message.member() == "Refresh") {
@@ -129,6 +145,13 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
129145 Q_EMIT refreshCalled();
130146 return connection.send(reply);
131147 }
148+ } else if (message.interface() == "com.canonical.Unity.Launcher.Item") {
149+ // Handle methods of the Launcher-Item interface
150+ if (message.member() == "Alert" && validpath) {
151+ QDBusMessage reply = message.createReply();
152+ Q_EMIT alertCalled(appid);
153+ return connection.send(reply);
154+ }
132155 }
133156
134157 // Now handle dynamic properties (for launcher emblems)
@@ -136,54 +159,61 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
136159 return false;
137160 }
138161
139− if (message.member() != "GetAll" && message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item") {
162+ if (message.member() == "Get" && (message.arguments().count() != 2 || message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item")) {
140163 return false;
141164 }
142165
143− /* Break down the path to just the app id */
144− QString pathtemp = message.path();
145− if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
166+ if (message.member() == "Set" && (message.arguments().count() != 3 || message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item")) {
146167 return false;
147168 }
148− pathtemp.remove("/com/canonical/Unity/Launcher/");
149− if (pathtemp.indexOf('/') >= 0) {
169+
170+ if (!validpath) {
150171 return false;
151172 }
152173
153− /* Find ourselves an appid */
154− QString appid = decodeAppId(pathtemp);
155174 int index = m_launcherModel->findApplication(appid);
156175 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
157176
158177 QVariantList retval;
159178 if (message.member() == "Get") {
179+ QString cachedString = message.arguments()[1].toString();
160180 if (!item) {
161181 return false;
162182 }
163− if (message.arguments()[1].toString() == "count") {
183+ if (cachedString == "count") {
164184 retval.append(QVariant::fromValue(QDBusVariant(item->count())));
165− } else if (message.arguments()[1].toString() == "countVisible") {
185+ } else if (cachedString == "countVisible") {
166186 retval.append(QVariant::fromValue(QDBusVariant(item->countVisible())));
187+ } else if (cachedString == "progress") {
188+ retval.append(QVariant::fromValue(QDBusVariant(item->progress())));
167189 }
168190 } else if (message.member() == "Set") {
169− if (message.arguments()[1].toString() == "count") {
191+ QString cachedString = message.arguments()[1].toString();
192+ if (cachedString == "count") {
170193 int newCount = message.arguments()[2].value<QDBusVariant>().variant().toInt();
171194 if (!item || newCount != item->count()) {
172195 Q_EMIT countChanged(appid, newCount);
173196 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "count", QVariant(newCount));
174197 }
175− } else if (message.arguments()[1].toString() == "countVisible") {
198+ } else if (cachedString == "countVisible") {
176199 bool newVisible = message.arguments()[2].value<QDBusVariant>().variant().toBool();
177200 if (!item || newVisible != item->countVisible()) {
178201 Q_EMIT countVisibleChanged(appid, newVisible);
179202 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "countVisible", newVisible);
180203 }
204+ } else if (cachedString == "progress") {
205+ int newProgress = message.arguments()[2].value<QDBusVariant>().variant().toInt();
206+ if (!item || newProgress != item->progress()) {
207+ Q_EMIT progressChanged(appid, newProgress);
208+ notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "progress", QVariant(newProgress));
209+ }
181210 }
182211 } else if (message.member() == "GetAll") {
183212 if (item) {
184213 QVariantMap all;
185214 all.insert("count", item->count());
186215 all.insert("countVisible", item->countVisible());
216+ all.insert("progress", item->progress());
187217 retval.append(all);
188218 }
189219 } else {
plugins/Unity/Launcher/dbusinterface.h+2
@@ -36,7 +36,9 @@ public:
3636Q_SIGNALS:
3737 void countChanged(const QString &appId, int count);
3838 void countVisibleChanged(const QString &appId, bool countVisible);
39+ void progressChanged(const QString &appId, int progress);
3940 void refreshCalled();
41+ void alertCalled(const QString &appId);
4042
4143private:
4244 static QString decodeAppId(const QString& path);
plugins/Unity/Launcher/gsettings.cpp+1−1
@@ -68,7 +68,7 @@ void GSettings::setStoredApplications(const QStringList &storedApplications)
6868void GSettings::onSettingsChanged(const QString &key)
6969{
7070 if (key == "items") {
71− QStringList cachedItems = m_gSettings->get("items").toStringList();
71+ const QStringList cachedItems = m_gSettings->get("items").toStringList();
7272 if (m_cachedItems != cachedItems) {
7373 m_cachedItems = cachedItems;
7474 Q_EMIT changed();
plugins/Unity/Launcher/launcheritem.cpp+33
@@ -34,16 +34,22 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
3434 m_count(0),
3535 m_countVisible(false),
3636 m_focused(false),
37+ m_alerting(false),
3738 m_quickList(new QuickListModel(this))
3839{
3940 QuickListEntry nameAction;
4041 nameAction.setActionId("launch_item");
4142 nameAction.setText(m_name);
4243 m_quickList->appendAction(nameAction);
44+
4345 QuickListEntry pinningAction;
4446 pinningAction.setActionId("pin_item");
4547 pinningAction.setText(gettext("Pin shortcut"));
4648 m_quickList->appendAction(pinningAction);
49+
50+ m_quitAction.setActionId("stop_item");
51+ m_quitAction.setIcon("application-exit");
52+ m_quitAction.setText(gettext("Quit"));
4753}
4854
4955QString LauncherItem::appId() const
@@ -110,6 +116,11 @@ void LauncherItem::setRunning(bool running)
110116{
111117 if (m_running != running) {
112118 m_running = running;
119+ if (m_running) { // add the quit action
120+ m_quickList->appendAction(m_quitAction);
121+ } else { // remove the quit action
122+ m_quickList->removeAction(m_quitAction);
123+ }
113124 Q_EMIT runningChanged(running);
114125 }
115126}
@@ -150,6 +161,9 @@ void LauncherItem::setCount(int count)
150161 if (m_count != count) {
151162 m_count = count;
152163 Q_EMIT countChanged(count);
164+ if (m_countVisible) {
165+ setAlerting(true);
166+ }
153167 }
154168}
155169
@@ -163,6 +177,9 @@ void LauncherItem::setCountVisible(bool countVisible)
163177 if (m_countVisible != countVisible) {
164178 m_countVisible = countVisible;
165179 Q_EMIT countVisibleChanged(countVisible);
180+ if (countVisible) {
181+ setAlerting(true);
182+ }
166183 }
167184}
168185
@@ -175,10 +192,26 @@ void LauncherItem::setFocused(bool focused)
175192{
176193 if (m_focused != focused) {
177194 m_focused = focused;
195+ if (focused) {
196+ setAlerting(false);
197+ }
178198 Q_EMIT focusedChanged(focused);
179199 }
180200}
181201
202+bool LauncherItem::alerting() const
203+{
204+ return m_alerting;
205+}
206+
207+void LauncherItem::setAlerting(bool alerting)
208+{
209+ if (m_alerting != alerting) {
210+ m_alerting = alerting;
211+ Q_EMIT alertingChanged(alerting);
212+ }
213+}
214+
182215unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
183216{
184217 return m_quickList;
plugins/Unity/Launcher/launcheritem.h+4
@@ -44,6 +44,7 @@ public:
4444 int count() const override;
4545 bool countVisible() const override;
4646 bool focused() const override;
47+ bool alerting() const override;
4748
4849 unity::shell::launcher::QuickListModelInterface *quickList() const override;
4950
@@ -57,6 +58,7 @@ private:
5758 void setCount(int count);
5859 void setCountVisible(bool countVisible);
5960 void setFocused(bool focused);
61+ void setAlerting(bool alerting);
6062
6163private:
6264 QString m_appId;
@@ -69,7 +71,9 @@ private:
6971 int m_count;
7072 bool m_countVisible;
7173 bool m_focused;
74+ bool m_alerting;
7275 QuickListModel *m_quickList;
76+ QuickListEntry m_quitAction;
7377
7478 friend class LauncherModel;
7579};
plugins/Unity/Launcher/launchermodel.cpp+77−26
@@ -36,11 +36,13 @@ LauncherModel::LauncherModel(QObject *parent):
3636 m_settings(new GSettings(this)),
3737 m_dbusIface(new DBusInterface(this)),
3838 m_asAdapter(new ASAdapter()),
39− m_appManager(0)
39+ m_appManager(nullptr)
4040{
4141 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
4242 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
43+ connect(m_dbusIface, &DBusInterface::progressChanged, this, &LauncherModel::progressChanged);
4344 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
45+ connect(m_dbusIface, &DBusInterface::alertCalled, this, &LauncherModel::alert);
4446
4547 connect(m_settings, &GSettings::changed, this, &LauncherModel::refresh);
4648
@@ -82,11 +84,30 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
8284 return item->progress();
8385 case RoleFocused:
8486 return item->focused();
87+ case RoleAlerting:
88+ return item->alerting();
89+ case RoleRunning:
90+ return item->running();
91+ default:
92+ qWarning() << Q_FUNC_INFO << "missing role, implement me";
93+ return QVariant();
8594 }
8695
8796 return QVariant();
8897}
8998
99+void LauncherModel::setAlerting(const QString &appId, bool alerting) {
100+ int index = findApplication(appId);
101+ if (index >= 0) {
102+ QModelIndex modelIndex = this->index(index);
103+ LauncherItem *item = m_list.at(index);
104+ if (!item->focused()) {
105+ item->setAlerting(alerting);
106+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
107+ }
108+ }
109+}
110+
90111unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
91112{
92113 if (index < 0 || index >= m_list.count()) {
@@ -134,7 +155,7 @@ void LauncherModel::pin(const QString &appId, int index)
134155 if (index == -1 || index == currentIndex) {
135156 m_list.at(currentIndex)->setPinned(true);
136157 QModelIndex modelIndex = this->index(currentIndex);
137− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
158+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
138159 } else {
139160 move(currentIndex, index);
140161 // move() will store the list to the backend itself, so just exit at this point.
@@ -147,7 +168,7 @@ void LauncherModel::pin(const QString &appId, int index)
147168
148169 DesktopFileHandler desktopFile(appId);
149170 if (!desktopFile.isValid()) {
150− qWarning() << "Can't pin this application, there is no .destkop file available.";
171+ qWarning() << "Can't pin this application, there is no .desktop file available.";
151172 return;
152173 }
153174
@@ -172,7 +193,7 @@ void LauncherModel::requestRemove(const QString &appId)
172193
173194void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
174195{
175− int index = findApplication(appId);
196+ const int index = findApplication(appId);
176197 if (index < 0) {
177198 return;
178199 }
@@ -180,7 +201,7 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
180201 LauncherItem *item = m_list.at(index);
181202 QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
182203 if (model) {
183− QString actionId = model->get(actionIndex).actionId();
204+ const QString actionId = model->get(actionIndex).actionId();
184205
185206 // Check if this is one of the launcher actions we handle ourselves
186207 if (actionId == "pin_item") {
@@ -191,7 +212,10 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
191212 }
192213 } else if (actionId == "launch_item") {
193214 QDesktopServices::openUrl(getUrlForAppId(appId));
194−
215+ } else if (actionId == "stop_item") { // Quit
216+ if (m_appManager) {
217+ m_appManager->stopApplication(appId);
218+ }
195219 // Nope, we don't know this action, let the backend forward it to the application
196220 } else {
197221 // TODO: forward quicklist action to app, possibly via m_dbusIface
@@ -289,7 +313,7 @@ void LauncherModel::storeAppList()
289313
290314void LauncherModel::unpin(const QString &appId)
291315{
292− int index = findApplication(appId);
316+ const int index = findApplication(appId);
293317 if (index < 0) {
294318 return;
295319 }
@@ -298,7 +322,7 @@ void LauncherModel::unpin(const QString &appId)
298322 if (m_list.at(index)->pinned()) {
299323 m_list.at(index)->setPinned(false);
300324 QModelIndex modelIndex = this->index(index);
301− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
325+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
302326 }
303327 } else {
304328 beginRemoveRows(QModelIndex(), index, index);
@@ -320,32 +344,38 @@ int LauncherModel::findApplication(const QString &appId)
320344
321345void LauncherModel::progressChanged(const QString &appId, int progress)
322346{
323− int idx = findApplication(appId);
347+ const int idx = findApplication(appId);
324348 if (idx >= 0) {
325349 LauncherItem *item = m_list.at(idx);
326350 item->setProgress(progress);
327− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
351+ Q_EMIT dataChanged(index(idx), index(idx), {RoleProgress});
328352 }
329353}
330354
331355void LauncherModel::countChanged(const QString &appId, int count)
332356{
333− int idx = findApplication(appId);
357+ const int idx = findApplication(appId);
334358 if (idx >= 0) {
335359 LauncherItem *item = m_list.at(idx);
336360 item->setCount(count);
337− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
361+ if (item->countVisible()) {
362+ setAlerting(item->appId(), true);
363+ }
338364 m_asAdapter->syncItems(m_list);
365+ Q_EMIT dataChanged(index(idx), index(idx), {RoleCount});
339366 }
340367}
341368
342−void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
369+void LauncherModel::countVisibleChanged(const QString &appId, bool countVisible)
343370{
344371 int idx = findApplication(appId);
345372 if (idx >= 0) {
346373 LauncherItem *item = m_list.at(idx);
347374 item->setCountVisible(countVisible);
348− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCountVisible);
375+ if (countVisible) {
376+ setAlerting(item->appId(), true);
377+ }
378+ Q_EMIT dataChanged(index(idx), index(idx), {RoleCountVisible});
349379
350380 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
351381 if (!countVisible && !item->pinned() && !item->recent()) {
@@ -364,7 +394,6 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
364394 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
365395 m_list.append(item);
366396 endInsertRows();
367− Q_EMIT hint();
368397 }
369398 }
370399 m_asAdapter->syncItems(m_list);
@@ -387,7 +416,8 @@ void LauncherModel::refresh()
387416 item->setName(desktopFile.displayName());
388417 item->setIcon(desktopFile.icon());
389418 item->setPinned(item->pinned()); // update pinned text if needed
390− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
419+ item->setRunning(item->running());
420+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleRunning});
391421 }
392422 }
393423
@@ -405,7 +435,7 @@ void LauncherModel::refresh()
405435
406436 // Now walk through settings and see if we need to add something
407437 for (int settingsIndex = 0; settingsIndex < m_settings->storedApplications().count(); ++settingsIndex) {
408− QString entry = m_settings->storedApplications().at(settingsIndex);
438+ const QString entry = m_settings->storedApplications().at(settingsIndex);
409439 int itemIndex = -1;
410440 for (int i = 0; i < m_list.count(); ++i) {
411441 if (m_list.at(i)->appId() == entry) {
@@ -453,6 +483,16 @@ void LauncherModel::refresh()
453483 m_asAdapter->syncItems(m_list);
454484}
455485
486+void LauncherModel::alert(const QString &appId)
487+{
488+ int idx = findApplication(appId);
489+ if (idx >= 0) {
490+ LauncherItem *item = m_list.at(idx);
491+ setAlerting(item->appId(), true);
492+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleAlerting);
493+ }
494+}
495+
456496void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
457497{
458498 Q_UNUSED(parent);
@@ -468,25 +508,26 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
468508 return;
469509 }
470510
471− int itemIndex = findApplication(app->appId());
511+ const int itemIndex = findApplication(app->appId());
472512 if (itemIndex != -1) {
473513 LauncherItem *item = m_list.at(itemIndex);
474514 if (!item->recent()) {
475515 item->setRecent(true);
476− m_asAdapter->syncItems(m_list);
477− Q_EMIT dataChanged(index(itemIndex), index(itemIndex), QVector<int>() << RoleRecent);
516+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRecent});
478517 }
479− // Shall we paint some running/recent app highlight? If yes, do it here.
518+ item->setRunning(true);
480519 } else {
481520 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
482521 item->setRecent(true);
522+ item->setRunning(true);
483523 item->setFocused(app->focused());
484524
485525 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
486526 m_list.append(item);
487527 endInsertRows();
488− m_asAdapter->syncItems(m_list);
489528 }
529+ m_asAdapter->syncItems(m_list);
530+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRunning});
490531}
491532
492533void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
@@ -501,25 +542,35 @@ void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
501542 }
502543 }
503544
504− if (appIndex > -1 && !m_list.at(appIndex)->pinned()) {
545+ if (appIndex < 0) {
546+ qWarning() << Q_FUNC_INFO << "appIndex not found";
547+ return;
548+ }
549+
550+ LauncherItem * item = m_list.at(appIndex);
551+ item->setRunning(false);
552+
553+ if (!item->pinned()) {
505554 beginRemoveRows(QModelIndex(), appIndex, appIndex);
506555 m_list.takeAt(appIndex)->deleteLater();
507556 endRemoveRows();
508557 m_asAdapter->syncItems(m_list);
558+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RolePinned});
509559 }
560+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RoleRunning});
510561}
511562
512563void LauncherModel::focusedAppIdChanged()
513564{
514− QString appId = m_appManager->focusedApplicationId();
565+ const QString appId = m_appManager->focusedApplicationId();
515566 for (int i = 0; i < m_list.count(); ++i) {
516567 LauncherItem *item = m_list.at(i);
517568 if (!item->focused() && item->appId() == appId) {
518569 item->setFocused(true);
519− Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
570+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
520571 } else if (item->focused() && item->appId() != appId) {
521572 item->setFocused(false);
522− Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
573+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
523574 }
524575 }
525576}
plugins/Unity/Launcher/launchermodel.h+3−1
@@ -45,6 +45,7 @@ public:
4545
4646 QVariant data(const QModelIndex &index, int role) const override;
4747
48+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
4849 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
4950 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
5051 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
@@ -63,6 +64,7 @@ public:
6364public Q_SLOTS:
6465 void requestRemove(const QString &appId) override;
6566 Q_INVOKABLE void refresh();
67+ Q_INVOKABLE void alert(const QString &appId);
6668
6769private:
6870 void storeAppList();
@@ -71,7 +73,7 @@ private:
7173
7274private Q_SLOTS:
7375 void countChanged(const QString &appId, int count);
74− void countVisibleChanged(const QString &appId, int count);
76+ void countVisibleChanged(const QString &appId, bool count);
7577 void progressChanged(const QString &appId, int progress);
7678
7779 void applicationAdded(const QModelIndex &parent, int row);
plugins/Unity/Launcher/quicklistentry.cpp+6−2
@@ -1,4 +1,4 @@
1−/* Copyright (C) 2013 Canonical, Ltd.
1+/* Copyright (C) 2013, 2015 Canonical, Ltd.
22 *
33 * Authors:
44 * Michael Zanetti <michael.zanetti@canonical.com>
@@ -20,7 +20,6 @@
2020
2121QuickListEntry::QuickListEntry()
2222{
23−
2423}
2524
2625QString QuickListEntry::actionId() const
@@ -57,3 +56,8 @@ bool QuickListEntry::clickable() const
5756{
5857 return !m_actionId.isEmpty();
5958}
59+
60+bool QuickListEntry::operator==(const QuickListEntry &other)
61+{
62+ return !other.actionId().isEmpty() && other.actionId() == m_actionId;
63+}
plugins/Unity/Launcher/quicklistentry.h+3−1
@@ -1,4 +1,4 @@
1−/* Copyright (C) 2013 Canonical, Ltd.
1+/* Copyright (C) 2013, 2015 Canonical, Ltd.
22 *
33 * Authors:
44 * Michael Zanetti <michael.zanetti@canonical.com>
@@ -37,6 +37,8 @@ public:
3737
3838 bool clickable() const;
3939
40+ bool operator==(const QuickListEntry & other);
41+
4042private:
4143 QString m_actionId;
4244 QString m_text;
plugins/Unity/Launcher/quicklistmodel.cpp+12−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013, 2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -48,6 +48,17 @@ void QuickListModel::updateAction(const QuickListEntry &entry)
4848 }
4949}
5050
51+void QuickListModel::removeAction(const QuickListEntry &entry)
52+{
53+ const int start = m_list.indexOf(entry);
54+ if (start > -1) {
55+ beginRemoveRows(QModelIndex(), start, start);
56+ m_list.removeOne(entry);
57+ Q_EMIT dataChanged(index(start), index(start));
58+ endRemoveRows();
59+ }
60+}
61+
5162QuickListEntry QuickListModel::get(int index) const
5263{
5364 return m_list.at(index);
plugins/Unity/Launcher/quicklistmodel.h+3−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 201, 2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -46,6 +46,8 @@ public:
4646 */
4747 void updateAction(const QuickListEntry &entry);
4848
49+ void removeAction(const QuickListEntry &entry);
50+
4951 QuickListEntry get(int index) const;
5052
5153 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
plugins/Unity/Session/CMakeLists.txt+1
@@ -2,6 +2,7 @@ pkg_search_module(GIO REQUIRED gio-2.0)
22
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
5+ ${CMAKE_CURRENT_BINARY_DIR}
56 ${GIO_INCLUDE_DIRS}
67 ${libunity8-private_SOURCE_DIR}
78)
plugins/Unity/Session/dbusunitysessionservice.cpp+372−24
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014, 2015 Canonical, Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it under
55 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -17,17 +17,186 @@
1717// local
1818#include "dbusunitysessionservice.h"
1919
20+// system
21+#include <sys/types.h>
22+#include <unistd.h>
23+#include <pwd.h>
24+
2025// Qt
26+#include <QDebug>
2127#include <QDBusPendingCall>
28+#include <QDBusReply>
29+#include <QElapsedTimer>
30+#include <QDateTime>
31+
32+#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
33+#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
34+#define LOGIN1_IFACE QStringLiteral("org.freedesktop.login1.Manager")
35+#define LOGIN1_SESSION_IFACE QStringLiteral("org.freedesktop.login1.Session")
36+
37+#define ACTIVE_KEY QStringLiteral("Active")
38+#define IDLE_SINCE_KEY QStringLiteral("IdleSinceHint")
39+
40+class DBusUnitySessionServicePrivate: public QObject
41+{
42+ Q_OBJECT
43+public:
44+ QString logindSessionPath;
45+ bool isSessionActive = true;
46+ QElapsedTimer screensaverActiveTimer;
47+
48+ DBusUnitySessionServicePrivate(): QObject() {
49+ init();
50+ checkActive();
51+ }
52+
53+ void init()
54+ {
55+ // get our logind session path
56+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
57+ LOGIN1_PATH,
58+ LOGIN1_IFACE,
59+ "GetSessionByPID");
60+ msg << (quint32) getpid();
61+
62+ QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().asyncCall(msg);
63+ if (reply.isValid()) {
64+ logindSessionPath = reply.value().path();
65+
66+ // start watching the Active property
67+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, "org.freedesktop.DBus.Properties", "PropertiesChanged",
68+ this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
69+ } else {
70+ qWarning() << "Failed to get logind session path" << reply.error().message();
71+ }
72+ }
73+
74+ bool checkLogin1Call(const QString &method) const
75+ {
76+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
77+ QDBusReply<QString> reply = QDBusConnection::systemBus().asyncCall(msg);
78+ return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
79+ }
80+
81+ void makeLogin1Call(const QString &method, const QVariantList &args)
82+ {
83+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
84+ LOGIN1_PATH,
85+ LOGIN1_IFACE,
86+ method);
87+ msg.setArguments(args);
88+ QDBusConnection::systemBus().asyncCall(msg);
89+ }
90+
91+ void checkActive()
92+ {
93+ if (logindSessionPath.isEmpty()) {
94+ qWarning() << "Invalid session path";
95+ return;
96+ }
97+
98+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
99+ logindSessionPath,
100+ "org.freedesktop.DBus.Properties",
101+ "Get");
102+ msg << LOGIN1_SESSION_IFACE;
103+ msg << ACTIVE_KEY;
104+
105+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
106+ if (reply.isValid()) {
107+ isSessionActive = reply.value().toBool();
108+ qDebug() << "Session" << logindSessionPath << "is active:" << isSessionActive;
109+ } else {
110+ qWarning() << "Failed to get Active property" << reply.error().message();
111+ }
112+ }
113+
114+ quint32 screensaverActiveTime() const
115+ {
116+ if (!isSessionActive && screensaverActiveTimer.isValid()) {
117+ return screensaverActiveTimer.elapsed() / 1000;
118+ }
119+
120+ return 0;
121+ }
122+
123+ quint64 idleSinceUSecTimestamp() const
124+ {
125+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
126+ logindSessionPath,
127+ "org.freedesktop.DBus.Properties",
128+ "Get");
129+ msg << LOGIN1_SESSION_IFACE;
130+ msg << IDLE_SINCE_KEY;
131+
132+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
133+ if (reply.isValid()) {
134+ return reply.value().value<quint64>();
135+ } else {
136+ qWarning() << "Failed to get IdleSinceHint property" << reply.error().message();
137+ }
138+
139+ return 0;
140+ }
141+
142+ void setIdleHint(bool idle)
143+ {
144+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
145+ logindSessionPath,
146+ LOGIN1_SESSION_IFACE,
147+ "SetIdleHint");
148+ msg << idle;
149+ QDBusConnection::systemBus().asyncCall(msg);
150+ }
151+
152+private Q_SLOTS:
153+ void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
154+ {
155+ Q_UNUSED(iface)
156+
157+ if (changedProps.contains(ACTIVE_KEY) || invalidatedProps.contains(ACTIVE_KEY)) {
158+ if (changedProps.value(ACTIVE_KEY).isValid()) {
159+ isSessionActive = changedProps.value(ACTIVE_KEY).toBool();
160+ } else {
161+ checkActive();
162+ }
163+
164+ Q_EMIT screensaverActiveChanged(!isSessionActive);
165+
166+ if (isSessionActive) {
167+ screensaverActiveTimer.invalidate();
168+ setIdleHint(false);
169+ } else {
170+ screensaverActiveTimer.start();
171+ setIdleHint(true);
172+ }
173+ }
174+ }
175+
176+Q_SIGNALS:
177+ void screensaverActiveChanged(bool active);
178+};
179+
180+Q_GLOBAL_STATIC(DBusUnitySessionServicePrivate, d)
22181
23182DBusUnitySessionService::DBusUnitySessionService()
24183 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
25184{
185+ if (!d->logindSessionPath.isEmpty()) {
186+ // connect our Lock() slot to the logind's session Lock() signal
187+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Lock", this, SLOT(Lock()));
188+ // ... and our Unlocked() signal to the logind's session Unlock() signal
189+ // (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
190+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Unlock", this, SIGNAL(Unlocked()));
191+ } else {
192+ qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
193+ }
26194}
27195
28196void DBusUnitySessionService::Logout()
29197{
30198 // TODO ask the apps to quit and then emit the signal
199+ Q_EMIT LogoutReady();
31200 Q_EMIT logoutReady();
32201}
33202
@@ -40,38 +209,139 @@ void DBusUnitySessionService::EndSession()
40209 QDBusConnection::sessionBus().asyncCall(msg);
41210}
42211
212+bool DBusUnitySessionService::CanHibernate() const
213+{
214+ return d->checkLogin1Call("CanHibernate");
215+}
216+
217+bool DBusUnitySessionService::CanSuspend() const
218+{
219+ return d->checkLogin1Call("CanSuspend");
220+}
221+
222+bool DBusUnitySessionService::CanHybridSleep() const
223+{
224+ return d->checkLogin1Call("CanHybridSleep");
225+}
226+
227+bool DBusUnitySessionService::CanReboot() const
228+{
229+ return d->checkLogin1Call("CanReboot");
230+}
231+
232+bool DBusUnitySessionService::CanShutdown() const
233+{
234+ return d->checkLogin1Call("CanPowerOff");
235+}
236+
237+bool DBusUnitySessionService::CanLock() const
238+{
239+ return true; // FIXME
240+}
241+
242+QString DBusUnitySessionService::UserName() const
243+{
244+ struct passwd *p = getpwuid(geteuid());
245+ if (p) {
246+ return QString::fromUtf8(p->pw_name);
247+ }
248+
249+ return QString();
250+}
251+
252+QString DBusUnitySessionService::RealName() const
253+{
254+ struct passwd *p = getpwuid(geteuid());
255+ if (p) {
256+ const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
257+ if (!gecos.isEmpty()) {
258+ return gecos.split(QLatin1Char(',')).first();
259+ }
260+ }
261+
262+ return QString();
263+}
264+
265+QString DBusUnitySessionService::HostName() const
266+{
267+ char hostName[512];
268+ if (gethostname(hostName, sizeof(hostName)) == -1) {
269+ qWarning() << "Could not determine local hostname";
270+ return QString();
271+ }
272+ hostName[sizeof(hostName) - 1] = '\0';
273+ return QString::fromLocal8Bit(hostName);
274+}
275+
276+void DBusUnitySessionService::PromptLock()
277+{
278+ Q_EMIT LockRequested();
279+ Q_EMIT lockRequested();
280+}
281+
282+void DBusUnitySessionService::Lock()
283+{
284+ // lock the session using the org.freedesktop.DisplayManager system DBUS service
285+ const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
286+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DisplayManager",
287+ sessionPath,
288+ "org.freedesktop.DisplayManager.Session",
289+ "Lock");
290+ qDebug() << "Locking session" << msg.path();
291+ QDBusReply<void> reply = QDBusConnection::systemBus().asyncCall(msg);
292+ if (!reply.isValid()) {
293+ qWarning() << "Lock call failed" << reply.error().message();
294+ } else {
295+ // emit Locked when the call succeeds
296+ Q_EMIT Locked();
297+ }
298+}
299+
300+bool DBusUnitySessionService::IsLocked() const
301+{
302+ return !d->isSessionActive;
303+}
304+
43305void DBusUnitySessionService::RequestLogout()
44306{
307+ Q_EMIT LogoutRequested(false);
45308 Q_EMIT logoutRequested(false);
46309}
47310
48311void DBusUnitySessionService::Reboot()
49312{
50− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
51− "/org/freedesktop/login1",
52− "org.freedesktop.login1.Manager",
53− "Reboot");
54− msg << false;
55− QDBusConnection::systemBus().asyncCall(msg);
313+ d->makeLogin1Call("Reboot", {false});
56314}
57315
58316void DBusUnitySessionService::RequestReboot()
59317{
318+ Q_EMIT RebootRequested(false);
60319 Q_EMIT rebootRequested(false);
61320}
62321
63322void DBusUnitySessionService::Shutdown()
64323{
65− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
66− "/org/freedesktop/login1",
67− "org.freedesktop.login1.Manager",
68− "PowerOff");
69− msg << false;
70− QDBusConnection::systemBus().asyncCall(msg);
324+ d->makeLogin1Call("PowerOff", {false});
325+}
326+
327+void DBusUnitySessionService::Suspend()
328+{
329+ d->makeLogin1Call("Suspend", {false});
330+}
331+
332+void DBusUnitySessionService::Hibernate()
333+{
334+ d->makeLogin1Call("Hibernate", {false});
335+}
336+
337+void DBusUnitySessionService::HybridSleep()
338+{
339+ d->makeLogin1Call("HybridSleep", {false});
71340}
72341
73342void DBusUnitySessionService::RequestShutdown()
74343{
344+ Q_EMIT ShutdownRequested(false);
75345 Q_EMIT shutdownRequested(false);
76346}
77347
@@ -83,12 +353,8 @@ enum class Action : unsigned
83353 NONE
84354};
85355
86−DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
87− : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
88−{
89−}
90356
91−void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
357+void performAsyncUnityCall(const QString &method)
92358{
93359 const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
94360 "/com/canonical/Unity/Session",
@@ -97,29 +363,111 @@ void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
97363 QDBusConnection::sessionBus().asyncCall(msg);
98364}
99365
366+
367+DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
368+ : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
369+{
370+}
371+
100372void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
101373{
102374 Q_UNUSED(arg_1);
103375 Q_UNUSED(max_wait);
104376 Q_UNUSED(inhibitors);
105377
106− const Action action = (Action)type;
107−
108− switch (action)
378+ switch (static_cast<Action>(type))
109379 {
110380 case Action::LOGOUT:
111− performAsyncCall("RequestLogout");
381+ performAsyncUnityCall("RequestLogout");
112382 break;
113383
114384 case Action::REBOOT:
115− performAsyncCall("RequestReboot");
385+ performAsyncUnityCall("RequestReboot");
116386 break;
117387
118388 case Action::SHUTDOWN:
119− performAsyncCall("RequestShutdown");
389+ performAsyncUnityCall("RequestShutdown");
120390 break;
121391
122392 default:
123393 break;
124394 }
125395}
396+
397+
398+DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
399+ : UnityDBusObject("/org/gnome/ScreenSaver", "org.gnome.ScreenSaver")
400+{
401+ connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusGnomeScreensaverWrapper::ActiveChanged);
402+}
403+
404+bool DBusGnomeScreensaverWrapper::GetActive() const
405+{
406+ return !d->isSessionActive; // return whether the session is not active
407+}
408+
409+void DBusGnomeScreensaverWrapper::SetActive(bool lock)
410+{
411+ if (lock) {
412+ Lock();
413+ }
414+}
415+
416+void DBusGnomeScreensaverWrapper::Lock()
417+{
418+ performAsyncUnityCall("Lock");
419+}
420+
421+quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
422+{
423+ return d->screensaverActiveTime();
424+}
425+
426+void DBusGnomeScreensaverWrapper::SimulateUserActivity()
427+{
428+ d->setIdleHint(false);
429+}
430+
431+
432+DBusScreensaverWrapper::DBusScreensaverWrapper()
433+ : UnityDBusObject("/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver")
434+{
435+ QDBusConnection::sessionBus().registerObject("/ScreenSaver", this, QDBusConnection::ExportScriptableContents); // compat path, also register here
436+ connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusScreensaverWrapper::ActiveChanged);
437+}
438+
439+bool DBusScreensaverWrapper::GetActive() const
440+{
441+ return !d->isSessionActive; // return whether the session is not active
442+}
443+
444+bool DBusScreensaverWrapper::SetActive(bool lock)
445+{
446+ if (lock) {
447+ Lock();
448+ return true;
449+ }
450+ return false;
451+}
452+
453+void DBusScreensaverWrapper::Lock()
454+{
455+ performAsyncUnityCall("Lock");
456+}
457+
458+quint32 DBusScreensaverWrapper::GetActiveTime() const
459+{
460+ return d->screensaverActiveTime();
461+}
462+
463+quint32 DBusScreensaverWrapper::GetSessionIdleTime() const
464+{
465+ return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
466+}
467+
468+void DBusScreensaverWrapper::SimulateUserActivity()
469+{
470+ d->setIdleHint(false);
471+}
472+
473+#include "dbusunitysessionservice.moc"
plugins/Unity/Session/dbusunitysessionservice.h+200−16
@@ -1,5 +1,5 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
1+/*
2+ * Copyright (C) 2014, 2015 Canonical, Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it under
55 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -29,7 +29,7 @@ Q_DECLARE_METATYPE(QList<QDBusObjectPath>)
2929 * interface.
3030 *
3131 * com.canonical.Unity.Session interface provides public methods
32− * and signals to handle Logout/Reboot/Shutdown.
32+ * and signals to handle eg. Logout/Reboot/Shutdown.
3333 */
3434class DBusUnitySessionService : public UnityDBusObject
3535{
@@ -46,46 +46,67 @@ public:
4646 Q_INVOKABLE void shutdown() { Shutdown(); }
4747 Q_INVOKABLE void endSession() { EndSession(); }
4848
49+ // TODO: remove duplicate signals and split D-Bus and QML API's
50+ // Apparently QML needs the signals in lowercase, while DBUS spec needs the uppercase version
4951Q_SIGNALS:
5052 /**
51− * logoutRequested signal
53+ * LogoutRequested signal
5254 *
5355 * This signal is emitted when some applications request the system to
5456 * logout.
5557 * @param have_inhibitors if there are any special running applications
5658 * which inhibit the logout.
5759 */
58− Q_SCRIPTABLE void logoutRequested(bool have_inhibitors);
60+ Q_SCRIPTABLE void LogoutRequested(bool have_inhibitors);
61+ void logoutRequested(bool have_inhibitors);
5962
6063 /**
61− * rebootRequested signal
64+ * RebootRequested signal
6265 *
6366 * This signal is emitted when some applications request the system to
6467 * reboot.
6568 * @param have_inhibitors if there are any special running applications
6669 * which inhibit the reboot.
6770 */
68− Q_SCRIPTABLE void rebootRequested(bool have_inhibitors);
71+ Q_SCRIPTABLE void RebootRequested(bool have_inhibitors);
72+ void rebootRequested(bool have_inhibitors);
6973
7074 /**
71− * shutdownRequested signal
75+ * ShutdownRequested signal
7276 *
7377 * This signal is emitted when some applications request the system to
7478 * shutdown.
7579 * @param have_inhibitors if there are any special running applications
7680 * which inhibit the shutdown.
7781 */
78− Q_SCRIPTABLE void shutdownRequested(bool have_inhibitors);
79−
82+ Q_SCRIPTABLE void ShutdownRequested(bool have_inhibitors);
83+ void shutdownRequested(bool have_inhibitors);
8084
8185 /**
82− * logoutReady signal
86+ * LogoutReady signal
8387 *
8488 * This signal is emitted when all the apps are closed. And the system
8589 * is safe to logout.
8690 */
91+ Q_SCRIPTABLE void LogoutReady();
8792 void logoutReady();
8893
94+ /**
95+ * Emitted as a result of calling PromptLock()
96+ */
97+ Q_SCRIPTABLE void LockRequested();
98+ void lockRequested();
99+
100+ /**
101+ * Emitted after the session has been locked.
102+ */
103+ Q_SCRIPTABLE void Locked();
104+
105+ /**
106+ * Emitted after the session has been unlocked.
107+ */
108+ Q_SCRIPTABLE void Unlocked();
109+
89110public Q_SLOTS:
90111 /**
91112 * Logout the system.
@@ -114,10 +135,33 @@ public Q_SLOTS:
114135 */
115136 Q_SCRIPTABLE void Shutdown();
116137
138+ /**
139+ * Suspend the system
140+ *
141+ * This method puts the system into sleep without user's confirmation.
142+ */
143+ Q_SCRIPTABLE void Suspend();
144+
145+ /**
146+ * Hibernate the system
147+ *
148+ * This method puts the system into hibernation without user's confirmation.
149+ */
150+ Q_SCRIPTABLE void Hibernate();
151+
152+ /**
153+ * Hybrid sleep
154+ *
155+ * This method puts the system into hybrid sleep without user's confirmation.
156+ *
157+ * @since unity8
158+ */
159+ Q_SCRIPTABLE void HybridSleep();
160+
117161 /**
118162 * Issue a logout request.
119163 *
120− * This method emits the logoutRequested signal to the shell with a boolean
164+ * This method emits the LogoutRequested signal to the shell with a boolean
121165 * which indicates if there's any inhibitors. The shell should receive
122166 * this signal and display a dialog to ask the user to confirm the logout
123167 * action. If confirmed, shell can call Logout() method to logout.
@@ -127,21 +171,21 @@ public Q_SLOTS:
127171 /**
128172 * Issue a reboot request.
129173 *
130− * This method emits the rebootRequested signal to the shell with a boolean
174+ * This method emits the RebootRequested signal to the shell with a boolean
131175 * which indicates if there's any inhibitors. The shell should receive
132176 * this signal and display a dialog to ask the user to confirm the reboot
133− * action. If confirmed, shell can call Reboot() method to reboot
177+ * action. If confirmed, shell can call Reboot() method to reboot.
134178 */
135179 Q_SCRIPTABLE void RequestReboot();
136180
137181 /**
138182 * Issue a shutdown request.
139183 *
140− * This method emits the shutdownRequested signal to the shell with a
184+ * This method emits the ShutdownRequested signal to the shell with a
141185 * boolean which indicates if there's any inhibitors.
142186 * The shell should receive
143187 * this signal and display a dialog to ask the user to confirm the reboot
144− * action. If confirmed, shell can call Shutdown() method to reboot
188+ * action. If confirmed, shell can call Shutdown() method to shutdown.
145189 */
146190 Q_SCRIPTABLE void RequestShutdown();
147191
@@ -152,6 +196,68 @@ public Q_SLOTS:
152196 * current DBus session bus.
153197 */
154198 Q_SCRIPTABLE void EndSession();
199+
200+ /**
201+ * @return whether the system is capable of hibernating
202+ */
203+ Q_SCRIPTABLE bool CanHibernate() const;
204+
205+ /**
206+ * @return whether the system is capable of suspending
207+ */
208+ Q_SCRIPTABLE bool CanSuspend() const;
209+
210+ /**
211+ * @return whether the system is capable of hybrid sleep
212+ * @since unity8
213+ */
214+ Q_SCRIPTABLE bool CanHybridSleep() const;
215+
216+ /**
217+ * @return whether the system is capable of rebooting
218+ * @since unity8
219+ */
220+ Q_SCRIPTABLE bool CanReboot() const;
221+
222+ /**
223+ * @return whether the system is capable of shutting down
224+ */
225+ Q_SCRIPTABLE bool CanShutdown() const;
226+
227+ /**
228+ * @return whether the system is capable of locking the session
229+ */
230+ Q_SCRIPTABLE bool CanLock() const;
231+
232+ /**
233+ * @return the login name of the current user
234+ */
235+ Q_SCRIPTABLE QString UserName() const;
236+
237+ /**
238+ * @return the real name of the current user
239+ */
240+ Q_SCRIPTABLE QString RealName() const;
241+
242+ /**
243+ * @return the local hostname
244+ */
245+ Q_SCRIPTABLE QString HostName() const;
246+
247+ /**
248+ * Request that the session get locked, emits signal LockRequested()
249+ */
250+ Q_SCRIPTABLE void PromptLock();
251+
252+ /**
253+ * Locks the session immediately
254+ */
255+ Q_SCRIPTABLE void Lock();
256+
257+ /**
258+ * @return whether the session is currently locked
259+ */
260+ Q_SCRIPTABLE bool IsLocked() const;
155261};
156262
157263class DBusGnomeSessionManagerWrapper : public UnityDBusObject
@@ -169,4 +275,82 @@ public Q_SLOTS:
169275private:
170276 void performAsyncCall(const QString &method);
171277};
278+
279+class DBusGnomeScreensaverWrapper: public UnityDBusObject
280+{
281+ Q_OBJECT
282+ Q_CLASSINFO("D-Bus Interface", "org.gnome.ScreenSaver")
283+
284+public:
285+ DBusGnomeScreensaverWrapper();
286+ ~DBusGnomeScreensaverWrapper() = default;
287+
288+public Q_SLOTS:
289+ /**
290+ * @return whether the session is currently locked (screensaver is on)
291+ */
292+ Q_SCRIPTABLE bool GetActive() const;
293+
294+ /**
295+ * Locks the session immediately if @p lock is true
296+ */
297+ Q_SCRIPTABLE void SetActive(bool lock);
298+
299+ /**
300+ * Locks the session immediately
301+ */
302+ Q_SCRIPTABLE void Lock();
303+
304+ /**
305+ * @return the number of seconds elapsed since the screensaver had been active
306+ */
307+ Q_SCRIPTABLE quint32 GetActiveTime() const;
308+
309+ Q_SCRIPTABLE void SimulateUserActivity();
310+
311+Q_SIGNALS:
312+ void ActiveChanged(bool active);
313+};
314+
315+class DBusScreensaverWrapper: public UnityDBusObject
316+{
317+ Q_OBJECT
318+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.ScreenSaver")
319+
320+public:
321+ DBusScreensaverWrapper();
322+ ~DBusScreensaverWrapper() = default;
323+
324+public Q_SLOTS:
325+ /**
326+ * @return whether the session is currently locked (screensaver is on)
327+ */
328+ Q_SCRIPTABLE bool GetActive() const;
329+
330+ /**
331+ * Locks the session immediately if @p lock is true
332+ */
333+ Q_SCRIPTABLE bool SetActive(bool lock);
334+
335+ /**
336+ * Locks the session immediately
337+ */
338+ Q_SCRIPTABLE void Lock();
339+
340+ /**
341+ * @return the number of seconds elapsed since the screensaver had been active
342+ */
343+ Q_SCRIPTABLE quint32 GetActiveTime() const;
344+
345+ /**
346+ * @return the number of seconds that this session has been idle
347+ */
348+ Q_SCRIPTABLE quint32 GetSessionIdleTime() const;
349+
350+ Q_SCRIPTABLE void SimulateUserActivity();
351+
352+Q_SIGNALS:
353+ void ActiveChanged(bool active);
354+};
355+
172356#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Unity/Session/plugin.cpp+2
@@ -27,6 +27,8 @@
2727static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
2828{
2929 new DBusGnomeSessionManagerWrapper();
30+ new DBusGnomeScreensaverWrapper();
31+ new DBusScreensaverWrapper();
3032 return new DBusUnitySessionService();
3133}
3234
plugins/Utils/CMakeLists.txt+1
@@ -4,6 +4,7 @@ include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
55 ${CMAKE_CURRENT_BINARY_DIR}
66 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
78 ${GIO_INCLUDE_DIRS}
89)
910
po/am.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ar.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
1818"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2015-07-30 06:14+0000\n"
20−"X-Generator: Launchpad (build 17646)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/ast.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/az.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/be.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/bg.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/br.po+5−5
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-07-17 10:10+0200\n"
11−"PO-Revision-Date: 2015-07-04 13:51+0000\n"
12−"Last-Translator: Rouxel <vincent.rouxel@mailoo.org>\n"
11+"PO-Revision-Date: 2015-08-25 14:12+0000\n"
12+"Last-Translator: Fohanno Thierry <thierry.fohanno@ofis-bzh.org>\n"
1313"Language-Team: Breton <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"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-26 06:12+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -374,7 +374,7 @@ msgstr "Siwazh, frazenn-dremen direizh"
374374#: qml/Greeter/NarrowView.qml:121
375375msgctxt "passphrase"
376376msgid "Please re-enter"
377−msgstr ""
377+msgstr "Adverkañ, mar plij"
378378
379379#: qml/Greeter/NarrowView.qml:122
380380msgid "Sorry, incorrect passcode"
po/bs.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/ca.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -376,7 +376,7 @@ msgstr "La contrasenya no és correcta"
376376#: qml/Greeter/NarrowView.qml:121
377377msgctxt "passphrase"
378378msgid "Please re-enter"
379−msgstr ""
379+msgstr "Torneu-ho a introduir"
380380
381381#: qml/Greeter/NarrowView.qml:122
382382msgid "Sorry, incorrect passcode"
po/ca@valencia.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ce.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ckb.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/cs.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020"Language: cs\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
po/cy.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==2 ? 1 : (n != 8 && n != 11) ? "
1818"2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-08-01 05:45+0000\n"
20−"X-Generator: Launchpad (build 17656)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/da.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/de.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/el.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -376,7 +376,7 @@ msgstr "Συγνώμη, μη έγκυρο συνθηματικό"
376376#: qml/Greeter/NarrowView.qml:121
377377msgctxt "passphrase"
378378msgid "Please re-enter"
379−msgstr ""
379+msgstr "Παρακαλώ πληκτρολογήστε ξανά"
380380
381381#: qml/Greeter/NarrowView.qml:122
382382msgid "Sorry, incorrect passcode"
po/en_AU.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/en_GB.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -370,7 +370,7 @@ msgstr "Sorry, incorrect passphrase"
370370#: qml/Greeter/NarrowView.qml:121
371371msgctxt "passphrase"
372372msgid "Please re-enter"
373−msgstr ""
373+msgstr "Please re-enter"
374374
375375#: qml/Greeter/NarrowView.qml:122
376376msgid "Sorry, incorrect passcode"
po/eo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/es.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -374,7 +374,7 @@ msgstr "La contraseña es incorrecta"
374374#: qml/Greeter/NarrowView.qml:121
375375msgctxt "passphrase"
376376msgid "Please re-enter"
377−msgstr ""
377+msgstr "Reintroduzca"
378378
379379#: qml/Greeter/NarrowView.qml:122
380380msgid "Sorry, incorrect passcode"
po/eu.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/fa.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/fi.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/fo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/fr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-28 05:40+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ga.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/gd.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
1818"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121"Language: gd\n"
2222
2323#: plugins/LightDM/Greeter.cpp:112
po/gl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-08-02 05:36+0000\n"
19−"X-Generator: Launchpad (build 17656)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/gv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : (n == 2 ? 1 : 2);\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/he.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/hi.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/hr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/hu.po+7−7
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-07-17 10:10+0200\n"
11−"PO-Revision-Date: 2015-08-01 06:47+0000\n"
11+"PO-Revision-Date: 2015-08-25 18:11+0000\n"
1212"Last-Translator: Richard Somlói <ricsipontaz@gmail.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"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-08-02 05:36+0000\n"
19−"X-Generator: Launchpad (build 17656)\n"
18+"X-Launchpad-Export-Date: 2015-08-26 06:13+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -117,11 +117,11 @@ msgstr "Eszköz lezárva"
117117
118118#: qml/Components/DelayedLockscreen.qml:64
119119msgid "You have been locked out due to too many failed passphrase attempts."
120−msgstr "A túl sok hibás jelmondat próbálkozás miatt le lett zárva az eszköz."
120+msgstr "A túl sok hibás jelmondat próbálkozás miatt az eszköz zárolva lett."
121121
122122#: qml/Components/DelayedLockscreen.qml:65
123123msgid "You have been locked out due to too many failed passcode attempts."
124−msgstr "A túl sok hibás jelkód próbálkozás miatt le lett zárva az eszköz."
124+msgstr "A túl sok hibás jelkód próbálkozás miatt az eszköz zárolva lett."
125125
126126#: qml/Components/DelayedLockscreen.qml:74
127127#, qt-format
@@ -219,7 +219,7 @@ msgstr "Vissza a híváshoz"
219219
220220#: qml/Components/Lockscreen.qml:245
221221msgid "Emergency Call"
222−msgstr "Vészhívás"
222+msgstr "Segélyhívás"
223223
224224#: qml/Components/Lockscreen.qml:277
225225msgid "OK"
@@ -656,7 +656,7 @@ msgid ""
656656"Privacy</b>"
657657msgstr ""
658658"Ezt kikapcsolhatja a <b>Rendszerbeállítások</b> alatt található <b>Biztonság "
659−"és adatvédelem</b> alatt"
659+"és adatvédelem</b> menüpontnál"
660660
661661#: qml/Wizard/Pages/80-finished.qml:24
662662msgid "All done"
po/hy.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ia.po+22−22
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-07-17 10:10+0200\n"
11−"PO-Revision-Date: 2015-06-08 18:58+0000\n"
11+"PO-Revision-Date: 2015-08-25 19:31+0000\n"
1212"Last-Translator: karm <melo@carmu.com>\n"
1313"Language-Team: Interlingua <ia@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-26 06:13+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -37,14 +37,14 @@ msgstr "Via-breve a levar pin"
3737#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
3838#: plugins/Utils/relativetimeformatter.cpp:157
3939msgid "Yesterday %l:%M %p"
40−msgstr ""
40+msgstr "Heri"
4141
4242#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
4343#. This format string is used for showing, on a 12-hour clock, times that happened today.
4444#. en_US example: "%l:%M %p" --> "1:00 PM"
4545#: plugins/Utils/relativetimeformatter.cpp:164
4646msgid "%l:%M %p"
47−msgstr ""
47+msgstr "%l:%M %p"
4848
4949#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
5050#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
@@ -52,7 +52,7 @@ msgstr ""
5252#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
5353#: plugins/Utils/relativetimeformatter.cpp:172
5454msgid "Tomorrow %l:%M %p"
55−msgstr ""
55+msgstr "Deman %l:%M %p"
5656
5757#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
5858#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
@@ -60,7 +60,7 @@ msgstr ""
6060#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
6161#: plugins/Utils/relativetimeformatter.cpp:181
6262msgid "%a %l:%M %p"
63−msgstr ""
63+msgstr "%a %l:%M %p"
6464
6565#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
6666#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
@@ -69,7 +69,7 @@ msgstr ""
6969#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM"
7070#: plugins/Utils/relativetimeformatter.cpp:190
7171msgid "%a %d %b %l:%M %p"
72−msgstr ""
72+msgstr "%a %d %b %l:%M %p"
7373
7474#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
7575#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
@@ -77,14 +77,14 @@ msgstr ""
7777#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
7878#: plugins/Utils/relativetimeformatter.cpp:201
7979msgid "Yesterday %H:%M"
80−msgstr ""
80+msgstr "Heri %H:%M"
8181
8282#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
8383#. This format string is used for showing, on a 24-hour clock, times that happened today.
8484#. en_US example: "%H:%M" --> "13:00"
8585#: plugins/Utils/relativetimeformatter.cpp:208
8686msgid "%H:%M"
87−msgstr ""
87+msgstr "%H:%M"
8888
8989#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
9090#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
@@ -92,7 +92,7 @@ msgstr ""
9292#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
9393#: plugins/Utils/relativetimeformatter.cpp:216
9494msgid "Tomorrow %H:%M"
95−msgstr ""
95+msgstr "Deman %H:%M"
9696
9797#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
9898#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
@@ -100,7 +100,7 @@ msgstr ""
100100#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
101101#: plugins/Utils/relativetimeformatter.cpp:225
102102msgid "%a %H:%M"
103−msgstr ""
103+msgstr "%a %H:%M"
104104
105105#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
106106#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
@@ -109,7 +109,7 @@ msgstr ""
109109#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
110110#: plugins/Utils/relativetimeformatter.cpp:234
111111msgid "%a %d %b %H:%M"
112−msgstr ""
112+msgstr "%a %d %b %H:%M"
113113
114114#: qml/Components/DelayedLockscreen.qml:49
115115msgid "Device Locked"
@@ -372,7 +372,7 @@ msgstr "Displacente, phrase-contrasigno incorrecte"
372372#: qml/Greeter/NarrowView.qml:121
373373msgctxt "passphrase"
374374msgid "Please re-enter"
375−msgstr ""
375+msgstr "Per favor re-insere"
376376
377377#: qml/Greeter/NarrowView.qml:122
378378msgid "Sorry, incorrect passcode"
@@ -398,32 +398,32 @@ msgstr "Nil es jocante"
398398#, qt-format
399399msgid "%1 hour"
400400msgid_plural "%1 hours"
401−msgstr[0] ""
402−msgstr[1] ""
401+msgstr[0] "%1 hora"
402+msgstr[1] "%1 horas"
403403
404404#: qml/Panel/Indicators/MenuItemFactory.qml:772
405405#, qt-format
406406msgid "%1 minute"
407407msgid_plural "%1 minutes"
408−msgstr[0] ""
409−msgstr[1] ""
408+msgstr[0] "%1 minuta"
409+msgstr[1] "%1 minutas"
410410
411411#: qml/Panel/Indicators/MenuItemFactory.qml:777
412412#, qt-format
413413msgid "%1 second"
414414msgid_plural "%1 seconds"
415−msgstr[0] ""
416−msgstr[1] ""
415+msgstr[0] "%1 secunda"
416+msgstr[1] "%1 secundas"
417417
418418#: qml/Panel/Indicators/MenuItemFactory.qml:780
419419msgid "0 seconds"
420−msgstr ""
420+msgstr "0 secundas"
421421
422422#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
423423#: qml/Panel/Indicators/MenuItemFactory.qml:782
424424#, qt-format
425425msgid "%1 remaining"
426−msgstr ""
426+msgstr "%1 restante"
427427
428428#: qml/Panel/Indicators/MenuItemFactory.qml:788
429429msgid "In queue…"
po/id.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/is.po+2−2
@@ -17,8 +17,8 @@ msgstr ""
1717"Content-Type: text/plain; charset=UTF-8\n"
1818"Content-Transfer-Encoding: 8bit\n"
1919"Plural-Forms: nplurals=2; plural=n != 1;\n"
20−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
21−"X-Generator: Launchpad (build 17628)\n"
20+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
21+"X-Generator: Launchpad (build 17690)\n"
2222"Language: is\n"
2323
2424#: plugins/LightDM/Greeter.cpp:112
po/it.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -374,7 +374,7 @@ msgstr "Passphrase errata"
374374#: qml/Greeter/NarrowView.qml:121
375375msgctxt "passphrase"
376376msgid "Please re-enter"
377−msgstr ""
377+msgstr "Inserire nuovamente"
378378
379379#: qml/Greeter/NarrowView.qml:122
380380msgid "Sorry, incorrect passcode"
po/ja.po+5−5
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -192,7 +192,7 @@ msgstr "はい"
192192#: qml/Components/Dialogs.qml:152
193193msgctxt "Title: Power off/Restart dialog"
194194msgid "Power"
195−msgstr ""
195+msgstr "電源"
196196
197197#: qml/Components/Dialogs.qml:153
198198msgid ""
@@ -203,12 +203,12 @@ msgstr "本当に電源を切りますか?"
203203#: qml/Components/Dialogs.qml:155
204204msgctxt "Button: Power off the system"
205205msgid "Power off"
206−msgstr ""
206+msgstr "電源を切る"
207207
208208#: qml/Components/Dialogs.qml:164
209209msgctxt "Button: Restart the system"
210210msgid "Restart"
211−msgstr ""
211+msgstr "再起動"
212212
213213#: qml/Components/Lockscreen.qml:245
214214msgid "Return to Call"
po/km.po+40−40
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -133,7 +133,7 @@ msgstr[1] ""
133133#: qml/Components/Dialogs.qml:69
134134msgctxt "Title: Lock/Log out dialog"
135135msgid "Log out"
136−msgstr ""
136+msgstr "ចេញ"
137137
138138#: qml/Components/Dialogs.qml:70
139139msgid "Are you sure you want to log out?"
@@ -142,12 +142,12 @@ msgstr "តើ​អ្នក​ពិតជា​ចង់​ចេញ​មែ
142142#: qml/Components/Dialogs.qml:72
143143msgctxt "Button: Lock the system"
144144msgid "Lock"
145−msgstr ""
145+msgstr "ចាក់សោ"
146146
147147#: qml/Components/Dialogs.qml:79
148148msgctxt "Button: Log out from the system"
149149msgid "Log Out"
150−msgstr ""
150+msgstr "ចេញ"
151151
152152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153153#: qml/Components/Dialogs.qml:173
@@ -157,7 +157,7 @@ msgstr "បោះបង់"
157157#: qml/Components/Dialogs.qml:98
158158msgctxt "Title: Reboot/Shut down dialog"
159159msgid "Shut down"
160−msgstr ""
160+msgstr "បិទ"
161161
162162#: qml/Components/Dialogs.qml:99
163163msgid "Are you sure you want to shut down?"
@@ -166,17 +166,17 @@ msgstr "តើ​អ្នក​ពិតជា​ចង់​បិទ​មែ
166166#: qml/Components/Dialogs.qml:101
167167msgctxt "Button: Reboot the system"
168168msgid "Reboot"
169−msgstr ""
169+msgstr "ចាប់ផ្ដើម​ឡើងវិញ"
170170
171171#: qml/Components/Dialogs.qml:109
172172msgctxt "Button: Shut down the system"
173173msgid "Shut down"
174−msgstr ""
174+msgstr "បិទ"
175175
176176#: qml/Components/Dialogs.qml:129
177177msgctxt "Title: Reboot dialog"
178178msgid "Reboot"
179−msgstr ""
179+msgstr "ចាប់ផ្ដើម​ឡើងវិញ"
180180
181181#: qml/Components/Dialogs.qml:130
182182msgid "Are you sure you want to reboot?"
@@ -193,7 +193,7 @@ msgstr "បាទ/ចាស"
193193#: qml/Components/Dialogs.qml:152
194194msgctxt "Title: Power off/Restart dialog"
195195msgid "Power"
196−msgstr ""
196+msgstr "ថាមពល"
197197
198198#: qml/Components/Dialogs.qml:153
199199msgid ""
@@ -206,12 +206,12 @@ msgstr ""
206206#: qml/Components/Dialogs.qml:155
207207msgctxt "Button: Power off the system"
208208msgid "Power off"
209−msgstr ""
209+msgstr "បិទ"
210210
211211#: qml/Components/Dialogs.qml:164
212212msgctxt "Button: Restart the system"
213213msgid "Restart"
214−msgstr ""
214+msgstr "ចាប់ផ្ដើម​ឡើងវិញ"
215215
216216#: qml/Components/Lockscreen.qml:245
217217msgid "Return to Call"
@@ -236,22 +236,22 @@ msgstr "មើល​ទាំងអស់"
236236#: qml/Dash/GenericScopeView.qml:601
237237msgctxt "Label: Hint for dash search line edit"
238238msgid "Search"
239−msgstr ""
239+msgstr "ស្វែងរក"
240240
241241#: qml/Dash/PageHeader.qml:269
242242msgctxt "Button: Open the Ubuntu Store"
243243msgid "Store"
244−msgstr ""
244+msgstr "រក្សាទុក"
245245
246246#: qml/Dash/PageHeader.qml:276
247247msgctxt "Button: Start a search in the current dash scope"
248248msgid "Search"
249−msgstr ""
249+msgstr "ស្វែងរក"
250250
251251#: qml/Dash/PageHeader.qml:286
252252msgctxt "Button: Show the current dash scope settings"
253253msgid "Settings"
254−msgstr ""
254+msgstr "ការ​កំណត់"
255255
256256#: qml/Dash/PageHeader.qml:293
257257msgid "Remove from Favorites"
@@ -295,11 +295,11 @@ msgstr ""
295295
296296#: qml/Dash/ScopesList.qml:67
297297msgid "Manage"
298−msgstr ""
298+msgstr "គ្រប់គ្រង"
299299
300300#: qml/Dash/ScopesList.qml:113
301301msgid "Home"
302−msgstr ""
302+msgstr "ផ្ទះ"
303303
304304#: qml/Dash/ScopesList.qml:114
305305msgid "Also installed"
@@ -307,11 +307,11 @@ msgstr ""
307307
308308#: qml/Greeter/CoverPage.qml:107
309309msgid "Unlock"
310−msgstr ""
310+msgstr "ដោះ​សោ"
311311
312312#: qml/Greeter/LoginList.qml:236
313313msgid "Retry"
314−msgstr ""
314+msgstr "ព្យាយាម​ម្ដងទៀត"
315315
316316#: qml/Greeter/LoginList.qml:237
317317msgid "Tap to unlock"
@@ -452,7 +452,7 @@ msgstr "បាន​បរាជ័យ ប៉ះ​ ដើម្បី​ព្
452452#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
453453msgctxt "Label: Hint in message indicator line edit"
454454msgid "Reply"
455−msgstr ""
455+msgstr "ឆ្លើយតប"
456456
457457#: qml/Panel/Indicators/ModemInfoItem.qml:105
458458msgid "Unlock SIM"
@@ -522,26 +522,26 @@ msgstr ""
522522#: qml/Wizard/Page.qml:89
523523msgctxt "Button: Go back one page in the Wizard"
524524msgid "Back"
525−msgstr ""
525+msgstr "ថយក្រោយ"
526526
527527#: qml/Wizard/Pages/10-welcome.qml:27
528528msgid "Hi!"
529−msgstr ""
529+msgstr "សួស្ដី!"
530530
531531#: qml/Wizard/Pages/10-welcome.qml:44
532532msgid "Welcome to your Ubuntu phone."
533−msgstr ""
533+msgstr "សូម​ស្វាគមន៍​មកកាន់​ទូរស័ព្ទ Ubuntu ។"
534534
535535#: qml/Wizard/Pages/10-welcome.qml:52
536536msgid "Let’s get started."
537−msgstr ""
537+msgstr "តោះ​ចាប់ផ្ដើម។"
538538
539539#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
540540#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
541541#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
542542#: qml/Wizard/Pages/passwd-set.qml:90
543543msgid "Continue"
544−msgstr ""
544+msgstr "បន្ត"
545545
546546#: qml/Wizard/Pages/20-sim.qml:25
547547msgid "Add a SIM card and restart your device"
@@ -553,11 +553,11 @@ msgstr ""
553553
554554#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
555555msgid "Skip"
556−msgstr ""
556+msgstr "រំលង"
557557
558558#: qml/Wizard/Pages/30-passwd-type.qml:39
559559msgid "Lock security"
560−msgstr ""
560+msgstr "ចាក់សោ​សុវត្ថិភាព"
561561
562562#: qml/Wizard/Pages/30-passwd-type.qml:74
563563msgid "Please select how you’d like to unlock your phone."
@@ -566,7 +566,7 @@ msgstr ""
566566#: qml/Wizard/Pages/30-passwd-type.qml:97
567567msgctxt "Label: Type of security method"
568568msgid "Swipe"
569−msgstr ""
569+msgstr "រុញ"
570570
571571#: qml/Wizard/Pages/30-passwd-type.qml:98
572572msgctxt "Label: Description of security method"
@@ -586,7 +586,7 @@ msgstr ""
586586#: qml/Wizard/Pages/30-passwd-type.qml:103
587587msgctxt "Label: Type of security method"
588588msgid "Passphrase"
589−msgstr ""
589+msgstr "ឃ្លាសម្ងាត់"
590590
591591#: qml/Wizard/Pages/30-passwd-type.qml:104
592592msgctxt "Label: Description of security method"
@@ -607,7 +607,7 @@ msgstr ""
607607
608608#: qml/Wizard/Pages/50-location.qml:27
609609msgid "Location"
610−msgstr ""
610+msgstr "ទីតាំង"
611611
612612#: qml/Wizard/Pages/50-location.qml:62
613613msgid "Let the phone detect your location:"
@@ -654,27 +654,27 @@ msgstr ""
654654
655655#: qml/Wizard/Pages/80-finished.qml:24
656656msgid "All done"
657−msgstr ""
657+msgstr "ធ្វើ​រួច​ទាំងអស់"
658658
659659#: qml/Wizard/Pages/80-finished.qml:39
660660msgid "Nice work!"
661−msgstr ""
661+msgstr "ដំណើរការ​ល្អ!"
662662
663663#: qml/Wizard/Pages/80-finished.qml:46
664664msgid "Your phone is now ready to use."
665−msgstr ""
665+msgstr "ទូរស័ព្ទ​របស់​អ្នក​ឥឡូវ​អាច​ប្រើ​បាន។"
666666
667667#: qml/Wizard/Pages/80-finished.qml:53
668668msgid "Finish"
669−msgstr ""
669+msgstr "បញ្ចប់"
670670
671671#: qml/Wizard/Pages/here-terms.qml:27
672672msgid "Terms & Conditions"
673−msgstr ""
673+msgstr "លក្ខខណ្ឌ​ប្រើប្រាស់"
674674
675675#: qml/Wizard/Pages/passwd-confirm.qml:49
676676msgid "Confirm passphrase"
677−msgstr ""
677+msgstr "បញ្ជាក់​ឃ្លា​សម្ងាត់"
678678
679679#: qml/Wizard/Pages/passwd-confirm.qml:50
680680msgid "Confirm passcode"
@@ -683,7 +683,7 @@ msgstr ""
683683#: qml/Wizard/Pages/passwd-confirm.qml:53
684684#: qml/Wizard/Pages/passwd-confirm.qml:54
685685msgid "Please try again."
686−msgstr ""
686+msgstr "សូម​ព្យាយាម​ម្ដងទៀត។"
687687
688688#: qml/Wizard/Pages/passwd-set.qml:61
689689msgid "Choose your passcode"
@@ -697,13 +697,13 @@ msgstr ""
697697#: qml/Wizard/StackButton.qml:39
698698#, qt-format
699699msgid "〈 %1"
700−msgstr ""
700+msgstr "〈 %1"
701701
702702#. Translators: This is the arrow for "Forward" buttons
703703#: qml/Wizard/StackButton.qml:42
704704#, qt-format
705705msgid "%1 〉"
706−msgstr ""
706+msgstr "%1 〉"
707707
708708#~ msgid "Try swiping from the right edge to unlock the phone"
709709#~ msgstr "សាកល្បង​​អូស​ពី​គែម​ស្ដាំ​ ដើម្បី​ដោះ​សោ​ទូរស័ព្ទ"
po/kn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ko.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ku.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ky.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/lo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/lt.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"(n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/lv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ml.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/mr.po+12−12
@@ -15,12 +15,12 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
23−msgstr ""
23+msgstr "पासवर्ड: "
2424
2525#: plugins/Unity/Launcher/launcheritem.cpp:45
2626#: plugins/Unity/Launcher/launcheritem.cpp:100
@@ -142,7 +142,7 @@ msgstr ""
142142#: qml/Components/Dialogs.qml:72
143143msgctxt "Button: Lock the system"
144144msgid "Lock"
145−msgstr ""
145+msgstr "लॉक करा"
146146
147147#: qml/Components/Dialogs.qml:79
148148msgctxt "Button: Log out from the system"
@@ -152,7 +152,7 @@ msgstr ""
152152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153153#: qml/Components/Dialogs.qml:173
154154msgid "Cancel"
155−msgstr ""
155+msgstr "रद्द"
156156
157157#: qml/Components/Dialogs.qml:98
158158msgctxt "Title: Reboot/Shut down dialog"
@@ -184,11 +184,11 @@ msgstr ""
184184
185185#: qml/Components/Dialogs.qml:132
186186msgid "No"
187−msgstr ""
187+msgstr "नाही"
188188
189189#: qml/Components/Dialogs.qml:138
190190msgid "Yes"
191−msgstr ""
191+msgstr "हो"
192192
193193#: qml/Components/Dialogs.qml:152
194194msgctxt "Title: Power off/Restart dialog"
@@ -234,7 +234,7 @@ msgstr ""
234234#: qml/Dash/GenericScopeView.qml:601
235235msgctxt "Label: Hint for dash search line edit"
236236msgid "Search"
237−msgstr ""
237+msgstr "शोध"
238238
239239#: qml/Dash/PageHeader.qml:269
240240msgctxt "Button: Open the Ubuntu Store"
@@ -244,7 +244,7 @@ msgstr ""
244244#: qml/Dash/PageHeader.qml:276
245245msgctxt "Button: Start a search in the current dash scope"
246246msgid "Search"
247−msgstr ""
247+msgstr "शोध"
248248
249249#: qml/Dash/PageHeader.qml:286
250250msgctxt "Button: Show the current dash scope settings"
@@ -253,11 +253,11 @@ msgstr ""
253253
254254#: qml/Dash/PageHeader.qml:293
255255msgid "Remove from Favorites"
256−msgstr ""
256+msgstr "आवड पासून काढूण टाका"
257257
258258#: qml/Dash/PageHeader.qml:293
259259msgid "Add to Favorites"
260−msgstr ""
260+msgstr "आवड मध्ये जोडा"
261261
262262#: qml/Dash/Previews/PreviewActionCombo.qml:35
263263msgid "More..."
@@ -277,7 +277,7 @@ msgstr ""
277277
278278#: qml/Dash/Previews/PreviewRatingInput.qml:169
279279msgid "Send"
280−msgstr ""
280+msgstr "पाठवा"
281281
282282#: qml/Dash/PullToRefreshScopeStyle.qml:55
283283msgid "Pull to refresh…"
po/ms.po+81−73
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -135,7 +135,7 @@ msgstr[1] "Sila tunggu %1 minit kemudian cuba lagi..."
135135#: qml/Components/Dialogs.qml:69
136136msgctxt "Title: Lock/Log out dialog"
137137msgid "Log out"
138−msgstr ""
138+msgstr "Daftar keluar"
139139
140140#: qml/Components/Dialogs.qml:70
141141msgid "Are you sure you want to log out?"
@@ -144,12 +144,12 @@ msgstr "Anda pasti mahu mendaftar keluar?"
144144#: qml/Components/Dialogs.qml:72
145145msgctxt "Button: Lock the system"
146146msgid "Lock"
147−msgstr ""
147+msgstr "Kunci"
148148
149149#: qml/Components/Dialogs.qml:79
150150msgctxt "Button: Log out from the system"
151151msgid "Log Out"
152−msgstr ""
152+msgstr "Daftar Keluar"
153153
154154#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
155155#: qml/Components/Dialogs.qml:173
@@ -159,7 +159,7 @@ msgstr "Batal"
159159#: qml/Components/Dialogs.qml:98
160160msgctxt "Title: Reboot/Shut down dialog"
161161msgid "Shut down"
162−msgstr ""
162+msgstr "Matikan"
163163
164164#: qml/Components/Dialogs.qml:99
165165msgid "Are you sure you want to shut down?"
@@ -168,17 +168,17 @@ msgstr "Anda pasti mahu matikan?"
168168#: qml/Components/Dialogs.qml:101
169169msgctxt "Button: Reboot the system"
170170msgid "Reboot"
171−msgstr ""
171+msgstr "But Semula"
172172
173173#: qml/Components/Dialogs.qml:109
174174msgctxt "Button: Shut down the system"
175175msgid "Shut down"
176−msgstr ""
176+msgstr "Matikan"
177177
178178#: qml/Components/Dialogs.qml:129
179179msgctxt "Title: Reboot dialog"
180180msgid "Reboot"
181−msgstr ""
181+msgstr "But Semula"
182182
183183#: qml/Components/Dialogs.qml:130
184184msgid "Are you sure you want to reboot?"
@@ -195,7 +195,7 @@ msgstr "Ya"
195195#: qml/Components/Dialogs.qml:152
196196msgctxt "Title: Power off/Restart dialog"
197197msgid "Power"
198−msgstr ""
198+msgstr "Kuasa"
199199
200200#: qml/Components/Dialogs.qml:153
201201msgid ""
@@ -206,16 +206,16 @@ msgstr "Anda pasti mahu tutup?"
206206#: qml/Components/Dialogs.qml:155
207207msgctxt "Button: Power off the system"
208208msgid "Power off"
209−msgstr ""
209+msgstr "Padam"
210210
211211#: qml/Components/Dialogs.qml:164
212212msgctxt "Button: Restart the system"
213213msgid "Restart"
214−msgstr ""
214+msgstr "Mula Semula"
215215
216216#: qml/Components/Lockscreen.qml:245
217217msgid "Return to Call"
218−msgstr ""
218+msgstr "Kembali ke Panggilan"
219219
220220#: qml/Components/Lockscreen.qml:245
221221msgid "Emergency Call"
@@ -236,22 +236,22 @@ msgstr "Lihat semua"
236236#: qml/Dash/GenericScopeView.qml:601
237237msgctxt "Label: Hint for dash search line edit"
238238msgid "Search"
239−msgstr ""
239+msgstr "Gelintar"
240240
241241#: qml/Dash/PageHeader.qml:269
242242msgctxt "Button: Open the Ubuntu Store"
243243msgid "Store"
244−msgstr ""
244+msgstr "Kedai"
245245
246246#: qml/Dash/PageHeader.qml:276
247247msgctxt "Button: Start a search in the current dash scope"
248248msgid "Search"
249−msgstr ""
249+msgstr "Gelintar"
250250
251251#: qml/Dash/PageHeader.qml:286
252252msgctxt "Button: Show the current dash scope settings"
253253msgid "Settings"
254−msgstr ""
254+msgstr "Tetapan"
255255
256256#: qml/Dash/PageHeader.qml:293
257257msgid "Remove from Favorites"
@@ -291,19 +291,19 @@ msgstr "Lepas untuk segar semula..."
291291
292292#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
293293msgid "Enable location data"
294−msgstr ""
294+msgstr "Benarkan data lokasi"
295295
296296#: qml/Dash/ScopesList.qml:67
297297msgid "Manage"
298−msgstr ""
298+msgstr "Urus"
299299
300300#: qml/Dash/ScopesList.qml:113
301301msgid "Home"
302−msgstr ""
302+msgstr "Rumah"
303303
304304#: qml/Dash/ScopesList.qml:114
305305msgid "Also installed"
306−msgstr ""
306+msgstr "Juga dipasang"
307307
308308#: qml/Greeter/CoverPage.qml:107
309309msgid "Unlock"
@@ -311,11 +311,11 @@ msgstr "Buka"
311311
312312#: qml/Greeter/LoginList.qml:236
313313msgid "Retry"
314−msgstr ""
314+msgstr "Cuba Lagi"
315315
316316#: qml/Greeter/LoginList.qml:237
317317msgid "Tap to unlock"
318−msgstr ""
318+msgstr "Ketik untuk buka"
319319
320320#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:53
321321msgid "Sorry, incorrect passphrase."
@@ -450,7 +450,7 @@ msgstr "Gagal, ketuk untuk cuba lagi"
450450#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
451451msgctxt "Label: Hint in message indicator line edit"
452452msgid "Reply"
453−msgstr ""
453+msgstr "Balas"
454454
455455#: qml/Panel/Indicators/ModemInfoItem.qml:105
456456msgid "Unlock SIM"
@@ -462,24 +462,24 @@ msgstr "Perayauan"
462462
463463#: qml/Tutorial/TutorialBottomFinish.qml:24
464464msgid "This action does different things for different apps"
465−msgstr ""
465+msgstr "Tindakan ini melakukan perkara yang berlainan untuk apl berbeza"
466466
467467#: qml/Tutorial/TutorialBottomFinish.qml:25
468468msgid "Tap here to finish."
469−msgstr ""
469+msgstr "Ketik di sini untuk selesaikan."
470470
471471#: qml/Tutorial/TutorialBottom.qml:28
472472msgid "Open special menus"
473−msgstr ""
473+msgstr "Buka menu khas"
474474
475475#: qml/Tutorial/TutorialBottom.qml:29
476476msgid "Swipe up from the bottom edge."
477−msgstr ""
477+msgstr "Leret ke ata dari pinggir bawah."
478478
479479#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
480480#: qml/Tutorial/TutorialRight.qml:144
481481msgid "Tap here to continue."
482−msgstr ""
482+msgstr "Ketik di sini untuk teruskan."
483483
484484#: qml/Tutorial/TutorialContent.qml:81
485485msgid "Click here to finish."
@@ -487,137 +487,139 @@ msgstr ""
487487
488488#: qml/Tutorial/TutorialLeftFinish.qml:24
489489msgid "These are the shortcuts to favorite apps"
490−msgstr ""
490+msgstr "Ini adalah pintasan ke apl kegemaran"
491491
492492#: qml/Tutorial/TutorialLeft.qml:26
493493msgid "Open the launcher"
494−msgstr ""
494+msgstr "Buka pelancar"
495495
496496#: qml/Tutorial/TutorialLeft.qml:27
497497msgid "Short swipe from the left edge."
498−msgstr ""
498+msgstr "Leret pendek dari pinggir kiri."
499499
500500#: qml/Tutorial/TutorialPage.qml:178
501501msgid "You almost got it!"
502−msgstr ""
502+msgstr "Anda hampir berjaya!"
503503
504504#: qml/Tutorial/TutorialPage.qml:195
505505msgid "Try again."
506−msgstr ""
506+msgstr "Cuba lagi."
507507
508508#: qml/Tutorial/TutorialRight.qml:31
509509msgid "To view open apps"
510−msgstr ""
510+msgstr "Untuk melihat apl dibuka"
511511
512512#: qml/Tutorial/TutorialRight.qml:32
513513msgid "Long swipe from the right edge."
514−msgstr ""
514+msgstr "Leret panjang dari pinggir kanan."
515515
516516#: qml/Tutorial/TutorialRight.qml:122
517517msgid "View all your running tasks."
518−msgstr ""
518+msgstr "Lihat semua tugas yang berjalan."
519519
520520#: qml/Wizard/Page.qml:89
521521msgctxt "Button: Go back one page in the Wizard"
522522msgid "Back"
523−msgstr ""
523+msgstr "Kembali"
524524
525525#: qml/Wizard/Pages/10-welcome.qml:27
526526msgid "Hi!"
527−msgstr ""
527+msgstr "Hai"
528528
529529#: qml/Wizard/Pages/10-welcome.qml:44
530530msgid "Welcome to your Ubuntu phone."
531−msgstr ""
531+msgstr "Selamat datang ke telefon Ubuntu anda."
532532
533533#: qml/Wizard/Pages/10-welcome.qml:52
534534msgid "Let’s get started."
535−msgstr ""
535+msgstr "Mari mulakan."
536536
537537#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
538538#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
539539#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
540540#: qml/Wizard/Pages/passwd-set.qml:90
541541msgid "Continue"
542−msgstr ""
542+msgstr "Teruskan"
543543
544544#: qml/Wizard/Pages/20-sim.qml:25
545545msgid "Add a SIM card and restart your device"
546−msgstr ""
546+msgstr "Tambah kad SIM dan mulakan semula peranti anda"
547547
548548#: qml/Wizard/Pages/20-sim.qml:59
549549msgid "Without it, you won’t be able to make calls or use text messaging."
550550msgstr ""
551+"Tanpanya, anda tidak boleh membuat panggilan atau guna pemesejan teks."
551552
552553#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
553554msgid "Skip"
554−msgstr ""
555+msgstr "Langkau"
555556
556557#: qml/Wizard/Pages/30-passwd-type.qml:39
557558msgid "Lock security"
558−msgstr ""
559+msgstr "Kunci keselamatan"
559560
560561#: qml/Wizard/Pages/30-passwd-type.qml:74
561562msgid "Please select how you’d like to unlock your phone."
562−msgstr ""
563+msgstr "Sila pilih bagaimana anda mahu membuka telefon anda."
563564
564565#: qml/Wizard/Pages/30-passwd-type.qml:97
565566msgctxt "Label: Type of security method"
566567msgid "Swipe"
567−msgstr ""
568+msgstr "Leret"
568569
569570#: qml/Wizard/Pages/30-passwd-type.qml:98
570571msgctxt "Label: Description of security method"
571572msgid "No security"
572−msgstr ""
573+msgstr "Tanpa keselamatan"
573574
574575#: qml/Wizard/Pages/30-passwd-type.qml:100
575576msgctxt "Label: Type of security method"
576577msgid "Passcode"
577−msgstr ""
578+msgstr "Kod laluan"
578579
579580#: qml/Wizard/Pages/30-passwd-type.qml:101
580581msgctxt "Label: Description of security method"
581582msgid "4 digits only"
582−msgstr ""
583+msgstr "4 digit sahaja"
583584
584585#: qml/Wizard/Pages/30-passwd-type.qml:103
585586msgctxt "Label: Type of security method"
586587msgid "Passphrase"
587−msgstr ""
588+msgstr "Frasa laluan"
588589
589590#: qml/Wizard/Pages/30-passwd-type.qml:104
590591msgctxt "Label: Description of security method"
591592msgid "Numbers and letters"
592−msgstr ""
593+msgstr "Nombor dan abjad"
593594
594595#: qml/Wizard/Pages/40-wifi.qml:29
595596msgid "Connect to Wi‑Fi"
596−msgstr ""
597+msgstr "Sambung ke Wi-Fi"
597598
598599#: qml/Wizard/Pages/40-wifi.qml:168
599600msgid "Available networks…"
600−msgstr ""
601+msgstr "Rangkaian tersedia..."
601602
602603#: qml/Wizard/Pages/40-wifi.qml:169
603604msgid "No available networks."
604−msgstr ""
605+msgstr "Tiada rangkaian tersedia."
605606
606607#: qml/Wizard/Pages/50-location.qml:27
607608msgid "Location"
608−msgstr ""
609+msgstr "Lokasi"
609610
610611#: qml/Wizard/Pages/50-location.qml:62
611612msgid "Let the phone detect your location:"
612−msgstr ""
613+msgstr "Biarkan telefon mengesan lokasi anda:"
613614
614615#: qml/Wizard/Pages/50-location.qml:69
615616msgid "Using GPS only (less accurate)"
616−msgstr ""
617+msgstr "Hanya gunakan GPS (kurang tepat)"
617618
618619#: qml/Wizard/Pages/50-location.qml:86
619620msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
620621msgstr ""
622+"Gunakan GPS, Wi-Fi awanama dan maklumat rangkaian selular (disarankan)"
621623
622624#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
623625#: qml/Wizard/Pages/50-location.qml:103
@@ -625,83 +627,89 @@ msgid ""
625627"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
626628"conditions</a>."
627629msgstr ""
630+"Dengan memilih pilihan ini anda menyetujui <a href='#'>terma dan syarat</a> "
631+"Nokia HERE."
628632
629633#: qml/Wizard/Pages/50-location.qml:112
630634msgid "Not at all"
631−msgstr ""
635+msgstr "Tidak sama sekali"
632636
633637#: qml/Wizard/Pages/50-location.qml:124
634638msgid "You can change your mind later in <b>System Settings</b>."
635−msgstr ""
639+msgstr "Anda boleh mengubahnya kemudian di dalam <b>Tetapan Sistem</b>."
636640
637641#: qml/Wizard/Pages/60-reporting.qml:24
638642msgid "Improving your experience"
639−msgstr ""
643+msgstr "Menambahbaik pengalaman anda"
640644
641645#: qml/Wizard/Pages/60-reporting.qml:36
642646msgid ""
643647"Your phone is set up to automatically report errors to Canonical and its "
644648"partners, the makers of the operating system."
645649msgstr ""
650+"Telefon anda ditetapkan secara automatik melaporkan ralat kepada Canonical "
651+"dan rakannya, pencipta sistem pengoperasian ini."
646652
647653#: qml/Wizard/Pages/60-reporting.qml:43
648654msgid ""
649655"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
650656"Privacy</b>"
651657msgstr ""
658+"Ia boleh dilumpuhkan di dalam <b>Tetapan Sistem</b> di bawah <b>Keselamatan "
659+"&amp; Kerahsiaan</b>"
652660
653661#: qml/Wizard/Pages/80-finished.qml:24
654662msgid "All done"
655−msgstr ""
663+msgstr "Semua selesai"
656664
657665#: qml/Wizard/Pages/80-finished.qml:39
658666msgid "Nice work!"
659−msgstr ""
667+msgstr "Selesai!"
660668
661669#: qml/Wizard/Pages/80-finished.qml:46
662670msgid "Your phone is now ready to use."
663−msgstr ""
671+msgstr "Telefon anda kini boleh digunakan."
664672
665673#: qml/Wizard/Pages/80-finished.qml:53
666674msgid "Finish"
667−msgstr ""
675+msgstr "Selesai"
668676
669677#: qml/Wizard/Pages/here-terms.qml:27
670678msgid "Terms & Conditions"
671−msgstr ""
679+msgstr "Terma & Syarat"
672680
673681#: qml/Wizard/Pages/passwd-confirm.qml:49
674682msgid "Confirm passphrase"
675−msgstr ""
683+msgstr "Sahkan frasa laluan"
676684
677685#: qml/Wizard/Pages/passwd-confirm.qml:50
678686msgid "Confirm passcode"
679−msgstr ""
687+msgstr "Sahkan kod laluan"
680688
681689#: qml/Wizard/Pages/passwd-confirm.qml:53
682690#: qml/Wizard/Pages/passwd-confirm.qml:54
683691msgid "Please try again."
684−msgstr ""
692+msgstr "Sila cuba lagi."
685693
686694#: qml/Wizard/Pages/passwd-set.qml:61
687695msgid "Choose your passcode"
688−msgstr ""
696+msgstr "Pilih kod laluan anda"
689697
690698#: qml/Wizard/Pages/passwd-set.qml:68
691699msgid "Passphrase must be 4 characters long"
692−msgstr ""
700+msgstr "Frasa laluan mestilah sebanyak 4 aksara"
693701
694702#. Translators: This is the arrow for "Back" buttons
695703#: qml/Wizard/StackButton.qml:39
696704#, qt-format
697705msgid "〈 %1"
698−msgstr ""
706+msgstr "〈 %1"
699707
700708#. Translators: This is the arrow for "Forward" buttons
701709#: qml/Wizard/StackButton.qml:42
702710#, qt-format
703711msgid "%1 〉"
704−msgstr ""
712+msgstr "%1 〉"
705713
706714#~ msgid "Top edge"
707715#~ msgstr "Pinggir atas"
po/my.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/nb.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/nl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/nn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/oc.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/or.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n!=1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/pa.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/pl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
1818"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/pt.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-30 06:14+0000\n"
19−"X-Generator: Launchpad (build 17646)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/pt_BR.po+12−12
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-07-17 10:10+0200\n"
11−"PO-Revision-Date: 2015-05-04 21:50+0000\n"
12−"Last-Translator: Tiago Hillebrandt <tiagohillebrandt@gmail.com>\n"
11+"PO-Revision-Date: 2015-08-05 04:40+0000\n"
12+"Last-Translator: Julio Alexander Sieg <julio.sieg@gmail.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"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -398,32 +398,32 @@ msgstr "Nada está tocando"
398398#, qt-format
399399msgid "%1 hour"
400400msgid_plural "%1 hours"
401−msgstr[0] ""
402−msgstr[1] ""
401+msgstr[0] "%1 hora"
402+msgstr[1] "%1 horas"
403403
404404#: qml/Panel/Indicators/MenuItemFactory.qml:772
405405#, qt-format
406406msgid "%1 minute"
407407msgid_plural "%1 minutes"
408−msgstr[0] ""
409−msgstr[1] ""
408+msgstr[0] "%1 minuto"
409+msgstr[1] "%1 minutos"
410410
411411#: qml/Panel/Indicators/MenuItemFactory.qml:777
412412#, qt-format
413413msgid "%1 second"
414414msgid_plural "%1 seconds"
415−msgstr[0] ""
416−msgstr[1] ""
415+msgstr[0] "%1 segundo"
416+msgstr[1] "%1 segundos"
417417
418418#: qml/Panel/Indicators/MenuItemFactory.qml:780
419419msgid "0 seconds"
420−msgstr ""
420+msgstr "0 segundo"
421421
422422#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
423423#: qml/Panel/Indicators/MenuItemFactory.qml:782
424424#, qt-format
425425msgid "%1 remaining"
426−msgstr ""
426+msgstr "%1 restantes"
427427
428428#: qml/Panel/Indicators/MenuItemFactory.qml:788
429429msgid "In queue…"
po/ro.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
1818"== 0) && (n != 0))) ? 2: 1));\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/ru.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/shn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=(n == 1) ? 0 : 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/si.po+75−13
@@ -15,12 +15,12 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
23−msgstr ""
23+msgstr "මුරපදය: "
2424
2525#: plugins/Unity/Launcher/launcheritem.cpp:45
2626#: plugins/Unity/Launcher/launcheritem.cpp:100
@@ -152,7 +152,7 @@ msgstr ""
152152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153153#: qml/Components/Dialogs.qml:173
154154msgid "Cancel"
155−msgstr ""
155+msgstr "අවලංගු කරන්න"
156156
157157#: qml/Components/Dialogs.qml:98
158158msgctxt "Title: Reboot/Shut down dialog"
@@ -161,7 +161,7 @@ msgstr ""
161161
162162#: qml/Components/Dialogs.qml:99
163163msgid "Are you sure you want to shut down?"
164−msgstr ""
164+msgstr "නිවාදැමිම තහවුරු කරන්න"
165165
166166#: qml/Components/Dialogs.qml:101
167167msgctxt "Button: Reboot the system"
@@ -180,15 +180,15 @@ msgstr ""
180180
181181#: qml/Components/Dialogs.qml:130
182182msgid "Are you sure you want to reboot?"
183−msgstr ""
183+msgstr "යළි පණගැන්වීම තහවුරු කරන්න"
184184
185185#: qml/Components/Dialogs.qml:132
186186msgid "No"
187−msgstr ""
187+msgstr "නැත"
188188
189189#: qml/Components/Dialogs.qml:138
190190msgid "Yes"
191−msgstr ""
191+msgstr "ඔව්"
192192
193193#: qml/Components/Dialogs.qml:152
194194msgctxt "Title: Power off/Restart dialog"
@@ -200,6 +200,8 @@ msgid ""
200200"Are you sure you would like\n"
201201"to power off?"
202202msgstr ""
203+"පරිගණකය වසාදැමිම\n"
204+"ඔබ තහවුරු කරන්නේද?"
203205
204206#: qml/Components/Dialogs.qml:155
205207msgctxt "Button: Power off the system"
@@ -217,7 +219,7 @@ msgstr ""
217219
218220#: qml/Components/Lockscreen.qml:245
219221msgid "Emergency Call"
220−msgstr ""
222+msgstr "හදිසි ඇමතුම"
221223
222224#: qml/Components/Lockscreen.qml:277
223225msgid "OK"
@@ -269,15 +271,15 @@ msgstr ""
269271
270272#: qml/Dash/Previews/PreviewRatingInput.qml:84
271273msgid "Rate this"
272−msgstr ""
274+msgstr "මෙය අගය කරන්න"
273275
274276#: qml/Dash/Previews/PreviewRatingInput.qml:129
275277msgid "Add a review"
276−msgstr ""
278+msgstr "විචාරයක් ලියන්න"
277279
278280#: qml/Dash/Previews/PreviewRatingInput.qml:169
279281msgid "Send"
280−msgstr ""
282+msgstr "යවන්න"
281283
282284#: qml/Dash/PullToRefreshScopeStyle.qml:55
283285msgid "Pull to refresh…"
@@ -372,7 +374,7 @@ msgstr ""
372374
373375#: qml/Notifications/NotificationMenuItemFactory.qml:128
374376msgid "Show password"
375−msgstr ""
377+msgstr "මුරපදය පෙන්වන්න"
376378
377379#: qml/Panel/ActiveCallHint.qml:79
378380msgid "Tap to return to call..."
@@ -696,3 +698,63 @@ msgstr ""
696698#, qt-format
697699msgid "%1 〉"
698700msgstr ""
701+
702+#~ msgid "Left edge"
703+#~ msgstr "වම් අන්තය"
704+
705+#~ msgid "Try swiping from the right edge to unlock the phone"
706+#~ msgstr "දුරකථනය අගුලුහරින්න දකුණු අන්තයේ සිට අදින්න"
707+
708+#~ msgid "Right edge"
709+#~ msgstr "දකුණු අන්තය"
710+
711+#~ msgid "Close"
712+#~ msgstr "වසන්න"
713+
714+#~ msgid "Skip intro"
715+#~ msgstr "හැඳින්වීම මඟහරින්න"
716+
717+#~ msgid "Loading. Please Wait..."
718+#~ msgstr "පූරණය වෙමින්. මඳක් රැඳෙන්න..."
719+
720+#~ msgid "Speaking..."
721+#~ msgstr "කතාකරමින්..."
722+
723+#~ msgid "Speak Now..."
724+#~ msgstr "දැන් කථා කරන්න..."
725+
726+#~ msgid "Confirm"
727+#~ msgstr "ස්ථීර කරන්න"
728+
729+#~ msgid "Top edge"
730+#~ msgstr "ඉහළ අන්තය"
731+
732+#~ msgid "Try swiping from the top edge to access the indicators"
733+#~ msgstr "දර්ශකවලට ප්‍රවේශවන්න ඉහළ අන්තයේ සිට අදින්න"
734+
735+#~ msgid "Well done"
736+#~ msgstr "කදිමයි"
737+
738+#~ msgid "Search"
739+#~ msgstr "සෙවුම"
740+
741+#~ msgid "Type or say a command"
742+#~ msgstr "විධානයක් ඇතුල්කරන්න හෝ කියන්න"
743+
744+#~ msgid "Log out"
745+#~ msgstr "ඉවත් වන්න"
746+
747+#~ msgid "Reboot"
748+#~ msgstr "යළි පණගන්වන්න"
749+
750+#~ msgid "Shut down"
751+#~ msgstr "පරිගණකය නිවා දමන්න"
752+
753+#~ msgid "Power"
754+#~ msgstr "බලය"
755+
756+#~ msgid "Restart"
757+#~ msgstr "පරිගණකය යළි පණගන්වන්න"
758+
759+#~ msgid "Power off"
760+#~ msgstr "පරිගණකය නිවා දමන්න"
po/sk.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/sl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
1818"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
po/sq.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/sr.po+2−2
@@ -17,8 +17,8 @@ msgstr ""
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1919"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
20−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
21−"X-Generator: Launchpad (build 17628)\n"
20+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
21+"X-Generator: Launchpad (build 17690)\n"
2222"Language: sr\n"
2323
2424#: plugins/LightDM/Greeter.cpp:112
po/sv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020"Language: sv\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
po/sw.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/ta.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020"Language: ta\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
po/te.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/th.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/tr.po+40−40
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -37,14 +37,14 @@ msgstr "Kısayol iğnesini kaldır"
3737#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
3838#: plugins/Utils/relativetimeformatter.cpp:157
3939msgid "Yesterday %l:%M %p"
40−msgstr ""
40+msgstr "Dün %l:%M %p"
4141
4242#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
4343#. This format string is used for showing, on a 12-hour clock, times that happened today.
4444#. en_US example: "%l:%M %p" --> "1:00 PM"
4545#: plugins/Utils/relativetimeformatter.cpp:164
4646msgid "%l:%M %p"
47−msgstr ""
47+msgstr "%l:%M %p"
4848
4949#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
5050#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
@@ -52,7 +52,7 @@ msgstr ""
5252#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
5353#: plugins/Utils/relativetimeformatter.cpp:172
5454msgid "Tomorrow %l:%M %p"
55−msgstr ""
55+msgstr "Yarın %l:%M %p"
5656
5757#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
5858#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
@@ -60,7 +60,7 @@ msgstr ""
6060#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
6161#: plugins/Utils/relativetimeformatter.cpp:181
6262msgid "%a %l:%M %p"
63−msgstr ""
63+msgstr "%l:%M %p %a"
6464
6565#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
6666#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
@@ -69,7 +69,7 @@ msgstr ""
6969#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM"
7070#: plugins/Utils/relativetimeformatter.cpp:190
7171msgid "%a %d %b %l:%M %p"
72−msgstr ""
72+msgstr "%a %d %b %l:%M %p"
7373
7474#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
7575#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
@@ -77,14 +77,14 @@ msgstr ""
7777#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
7878#: plugins/Utils/relativetimeformatter.cpp:201
7979msgid "Yesterday %H:%M"
80−msgstr ""
80+msgstr "Dün %H:%M"
8181
8282#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
8383#. This format string is used for showing, on a 24-hour clock, times that happened today.
8484#. en_US example: "%H:%M" --> "13:00"
8585#: plugins/Utils/relativetimeformatter.cpp:208
8686msgid "%H:%M"
87−msgstr ""
87+msgstr "%H:%M"
8888
8989#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
9090#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
@@ -92,7 +92,7 @@ msgstr ""
9292#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
9393#: plugins/Utils/relativetimeformatter.cpp:216
9494msgid "Tomorrow %H:%M"
95−msgstr ""
95+msgstr "Yarın %H:%M"
9696
9797#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
9898#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
@@ -100,7 +100,7 @@ msgstr ""
100100#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
101101#: plugins/Utils/relativetimeformatter.cpp:225
102102msgid "%a %H:%M"
103−msgstr ""
103+msgstr "%H:%M %a"
104104
105105#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
106106#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
@@ -109,7 +109,7 @@ msgstr ""
109109#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
110110#: plugins/Utils/relativetimeformatter.cpp:234
111111msgid "%a %d %b %H:%M"
112−msgstr ""
112+msgstr "%a %b %d\\u2003%H:%M"
113113
114114#: qml/Components/DelayedLockscreen.qml:49
115115msgid "Device Locked"
@@ -133,7 +133,7 @@ msgstr[1] "Lütfen %1 dakika bekleyin ve yeniden deneyin..."
133133#: qml/Components/Dialogs.qml:69
134134msgctxt "Title: Lock/Log out dialog"
135135msgid "Log out"
136−msgstr ""
136+msgstr "Oturumu kapat"
137137
138138#: qml/Components/Dialogs.qml:70
139139msgid "Are you sure you want to log out?"
@@ -142,12 +142,12 @@ msgstr "Oturumu kapatmak istediğinize emin misiniz?"
142142#: qml/Components/Dialogs.qml:72
143143msgctxt "Button: Lock the system"
144144msgid "Lock"
145−msgstr ""
145+msgstr "Kilitle"
146146
147147#: qml/Components/Dialogs.qml:79
148148msgctxt "Button: Log out from the system"
149149msgid "Log Out"
150−msgstr ""
150+msgstr "Oturumu Kapat"
151151
152152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153153#: qml/Components/Dialogs.qml:173
@@ -157,7 +157,7 @@ msgstr "İptal"
157157#: qml/Components/Dialogs.qml:98
158158msgctxt "Title: Reboot/Shut down dialog"
159159msgid "Shut down"
160−msgstr ""
160+msgstr "Kapat"
161161
162162#: qml/Components/Dialogs.qml:99
163163msgid "Are you sure you want to shut down?"
@@ -171,7 +171,7 @@ msgstr "Yeniden başlat"
171171#: qml/Components/Dialogs.qml:109
172172msgctxt "Button: Shut down the system"
173173msgid "Shut down"
174−msgstr ""
174+msgstr "Kapat"
175175
176176#: qml/Components/Dialogs.qml:129
177177msgctxt "Title: Reboot dialog"
@@ -204,7 +204,7 @@ msgstr "Bilgisayarı kapatmak istediğinize emin misiniz?"
204204#: qml/Components/Dialogs.qml:155
205205msgctxt "Button: Power off the system"
206206msgid "Power off"
207−msgstr ""
207+msgstr "Sistemi Kapat"
208208
209209#: qml/Components/Dialogs.qml:164
210210msgctxt "Button: Restart the system"
@@ -309,7 +309,7 @@ msgstr "Kilidi Aç"
309309
310310#: qml/Greeter/LoginList.qml:236
311311msgid "Retry"
312−msgstr ""
312+msgstr "Tekrar Dene"
313313
314314#: qml/Greeter/LoginList.qml:237
315315msgid "Tap to unlock"
@@ -368,7 +368,7 @@ msgstr "Üzgünüm, hatalı parola"
368368#: qml/Greeter/NarrowView.qml:121
369369msgctxt "passphrase"
370370msgid "Please re-enter"
371−msgstr ""
371+msgstr "Lütfen yeniden girin"
372372
373373#: qml/Greeter/NarrowView.qml:122
374374msgid "Sorry, incorrect passcode"
@@ -394,32 +394,32 @@ msgstr "Hiçbir şey çalmıyor"
394394#, qt-format
395395msgid "%1 hour"
396396msgid_plural "%1 hours"
397−msgstr[0] ""
398−msgstr[1] ""
397+msgstr[0] "%1 saat"
398+msgstr[1] "%1 saat"
399399
400400#: qml/Panel/Indicators/MenuItemFactory.qml:772
401401#, qt-format
402402msgid "%1 minute"
403403msgid_plural "%1 minutes"
404−msgstr[0] ""
405−msgstr[1] ""
404+msgstr[0] "%1 dakika"
405+msgstr[1] "%1 dakika"
406406
407407#: qml/Panel/Indicators/MenuItemFactory.qml:777
408408#, qt-format
409409msgid "%1 second"
410410msgid_plural "%1 seconds"
411−msgstr[0] ""
412−msgstr[1] ""
411+msgstr[0] "%1 saniye"
412+msgstr[1] "%1 saniye"
413413
414414#: qml/Panel/Indicators/MenuItemFactory.qml:780
415415msgid "0 seconds"
416−msgstr ""
416+msgstr "0 saniye"
417417
418418#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
419419#: qml/Panel/Indicators/MenuItemFactory.qml:782
420420#, qt-format
421421msgid "%1 remaining"
422−msgstr ""
422+msgstr "%1 kaldı"
423423
424424#: qml/Panel/Indicators/MenuItemFactory.qml:788
425425msgid "In queue…"
@@ -481,23 +481,23 @@ msgstr "Devam etmek için buraya dokunun."
481481
482482#: qml/Tutorial/TutorialContent.qml:81
483483msgid "Click here to finish."
484−msgstr ""
484+msgstr "Bitirmek için buraya tıklayın."
485485
486486#: qml/Tutorial/TutorialLeftFinish.qml:24
487487msgid "These are the shortcuts to favorite apps"
488−msgstr ""
488+msgstr "Bunlar gözde uygulamalarınız için kısayollardır"
489489
490490#: qml/Tutorial/TutorialLeft.qml:26
491491msgid "Open the launcher"
492−msgstr ""
492+msgstr "Başlatıcıyı aç"
493493
494494#: qml/Tutorial/TutorialLeft.qml:27
495495msgid "Short swipe from the left edge."
496−msgstr ""
496+msgstr "Sol kenardan kısa kaydırma"
497497
498498#: qml/Tutorial/TutorialPage.qml:178
499499msgid "You almost got it!"
500−msgstr ""
500+msgstr "Neredeyse yaptınız!"
501501
502502#: qml/Tutorial/TutorialPage.qml:195
503503msgid "Try again."
@@ -509,7 +509,7 @@ msgstr "Açık uygulamaları görmek için"
509509
510510#: qml/Tutorial/TutorialRight.qml:32
511511msgid "Long swipe from the right edge."
512−msgstr ""
512+msgstr "Sağ kenardan uzan kaydırma"
513513
514514#: qml/Tutorial/TutorialRight.qml:122
515515msgid "View all your running tasks."
@@ -518,7 +518,7 @@ msgstr "Çalışan tüm görevleri incele."
518518#: qml/Wizard/Page.qml:89
519519msgctxt "Button: Go back one page in the Wizard"
520520msgid "Back"
521−msgstr ""
521+msgstr "Geri"
522522
523523#: qml/Wizard/Pages/10-welcome.qml:27
524524msgid "Hi!"
@@ -563,27 +563,27 @@ msgstr "Lütfen telefonunuzun kilidini nasıl açmak istediğinizi seçin."
563563#: qml/Wizard/Pages/30-passwd-type.qml:97
564564msgctxt "Label: Type of security method"
565565msgid "Swipe"
566−msgstr ""
566+msgstr "Kaydır"
567567
568568#: qml/Wizard/Pages/30-passwd-type.qml:98
569569msgctxt "Label: Description of security method"
570570msgid "No security"
571−msgstr ""
571+msgstr "Güvenlik yok"
572572
573573#: qml/Wizard/Pages/30-passwd-type.qml:100
574574msgctxt "Label: Type of security method"
575575msgid "Passcode"
576−msgstr ""
576+msgstr "Parola"
577577
578578#: qml/Wizard/Pages/30-passwd-type.qml:101
579579msgctxt "Label: Description of security method"
580580msgid "4 digits only"
581−msgstr ""
581+msgstr "Yalnızca 4 rakam"
582582
583583#: qml/Wizard/Pages/30-passwd-type.qml:103
584584msgctxt "Label: Type of security method"
585585msgid "Passphrase"
586−msgstr ""
586+msgstr "Parola Metni"
587587
588588#: qml/Wizard/Pages/30-passwd-type.qml:104
589589msgctxt "Label: Description of security method"
po/ug.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/uk.po+3−3
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
20+"X-Generator: Launchpad (build 17690)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
@@ -376,7 +376,7 @@ msgstr "Вибачте, помилковий пароль"
376376#: qml/Greeter/NarrowView.qml:121
377377msgctxt "passphrase"
378378msgid "Please re-enter"
379−msgstr ""
379+msgstr "Введіть ще раз"
380380
381381#: qml/Greeter/NarrowView.qml:122
382382msgid "Sorry, incorrect passcode"
po/ur.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/uz.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/vi.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/xh.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/zh_CN.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-28 05:40+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/zh_HK.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
po/zh_TW.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-08-01 05:45+0000\n"
19−"X-Generator: Launchpad (build 17656)\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19+"X-Generator: Launchpad (build 17690)\n"
2020"Language: zh_TW\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
qml/Components/PhysicalKeysMapper.qml+24
@@ -41,14 +41,23 @@ Item {
4141 signal volumeUpTriggered;
4242 signal screenshotTriggered;
4343
44+ readonly property bool altTabPressed: d.altTabPressed
45+
4446 property int powerKeyLongPressTime: 2000
4547
48+ // For testing. If running windowed (e.g. tryShell), Alt+Tab is taken by the
49+ // running desktop, set this to true to use Ctrl+Tab instead.
50+ property bool controlInsteadOfAlt: false
51+
4652 QtObject {
4753 id: d
4854
4955 property bool volumeDownKeyPressed: false
5056 property bool volumeUpKeyPressed: false
5157 property bool ignoreVolumeEvents: false
58+
59+ property bool altPressed: false
60+ property bool altTabPressed: false
5261 }
5362
5463 Timer {
@@ -93,6 +102,15 @@ Item {
93102 }
94103 d.volumeUpKeyPressed = true;
95104 }
105+ } else if (event.key == Qt.Key_Alt || (root.controlInsteadOfAlt && event.key == Qt.Key_Control)) {
106+ d.altPressed = true;
107+ } else if (event.key == Qt.Key_Tab) {
108+ if (d.altPressed && !d.altTabPressed) {
109+ d.altTabPressed = true;
110+ event.accepted = true;
111+ }
112+ } else if (event.key == Qt.Key_Print) {
113+ root.screenshotTriggered();
96114 }
97115 }
98116
@@ -108,6 +126,12 @@ Item {
108126 if (!d.ignoreVolumeEvents) root.volumeUpTriggered();
109127 d.volumeUpKeyPressed = false;
110128 if (!d.volumeDownKeyPressed) d.ignoreVolumeEvents = false;
129+ } else if (event.key == Qt.Key_Alt || (root.controlInsteadOfAlt && event.key == Qt.Key_Control)) {
130+ d.altPressed = false;
131+ if (d.altTabPressed) {
132+ d.altTabPressed = false;
133+ event.accepted = true;
134+ }
111135 }
112136 }
113137}
qml/Components/ScreenGrabber.qml+1−5
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -20,7 +20,6 @@ import ScreenGrabber 0.1
2020
2121Rectangle {
2222 id: root
23− enabled: true
2423 visible: false
2524 color: "white"
2625 anchors.fill: parent
@@ -38,9 +37,6 @@ Rectangle {
3837 }
3938
4039 function capture() {
41− if (!enabled)
42− return;
43−
4440 visible = true;
4541 shutterSound.stop();
4642 shutterSound.play();
qml/Components/Unity8Settings.qmlnew file+50
@@ -0,0 +1,50 @@
1+/*
2+ * Copyright (C) 2015 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 GSettings 1.0
19+
20+QtObject {
21+ id: root
22+
23+ property string usageMode: "Staged"
24+
25+ // FIXME: Works around a bug where if we change a Loader's source in response to a GSettings
26+ // property change in the same event loop, the Loader's previously loaded component does not
27+ // get destroyed and its bindings continue to operate!
28+ //
29+ // Shouldn't be needed after
30+ // https://code.launchpad.net/~lukas-kde/gsettings-qt/queued-processing/+merge/259883 gets
31+ // merged.
32+ property var timer: Timer {
33+ interval: 1
34+ onTriggered: { root.usageMode = root.wrapped.usageMode; }
35+ }
36+ property var wrappedConnections: Connections {
37+ target: root.wrapped
38+ ignoreUnknownSignals: true // don't spam us
39+ onUsageModeChanged: { root.timer.start(); }
40+ }
41+ property var wrapped: GSettings {
42+ schema.id: "com.canonical.Unity8"
43+ Component.onCompleted: {
44+ // init the value. it's a dynamic prop, so we have to check first
45+ if (root.usageMode) {
46+ root.usageMode = root.wrapped.usageMode;
47+ }
48+ }
49+ }
50+}
qml/Components/UnityInputInfo/UnityInputInfo.qml+2
@@ -18,6 +18,8 @@ pragma Singleton
1818
1919import QtQuick 2.3
2020import Unity.InputInfo 0.1
21+// Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
22+import Ubuntu.Components 1.2
2123
2224Item {
2325 id: root
qml/Dash/Dash.qml+1−1
@@ -41,7 +41,7 @@ Showable {
4141 onSetCurrentScopeRequested: {
4242 if (!isSwipe || !window.active || bottomEdgeController.progress != 0 || scopeItem.scope || dashContent.subPageShown) {
4343 if (bottomEdgeController.progress != 0 && window.active) animate = false;
44− dashContent.setCurrentScopeAtIndex(index, animate, isSwipe)
44+ dashContent.setCurrentScopeAtIndex(index, animate, true)
4545 backToDashContent()
4646 }
4747 }
qml/Dash/DashContent.qml+1
@@ -84,6 +84,7 @@ Item {
8484
8585 if (reset) {
8686 dashContentList.currentItem.item.positionAtBeginning()
87+ dashContentList.currentItem.item.resetSearch()
8788 }
8889 }
8990
qml/Dash/GenericScopeView.qml+66−31
@@ -67,38 +67,57 @@ FocusScope {
6767 subPageLoader.closeSubPage()
6868 }
6969
70− function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
71− if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope" || (scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
72− // TODO Technically it is possible that calling activate() will make the scope emit
73− // previewRequested so that we show a preview but there's no scope that does that yet
74− // so it's not implemented
75− scope.activate(result)
76− } else {
77− if (scope.preview(result)) {
78− openPreview(index, resultsModel, limitedCategoryItemCount);
79− }
80− }
70+ function resetSearch() {
71+ if(pageHeaderLoader.item && showPageHeader)
72+ pageHeaderLoader.item.resetSearch()
8173 }
8274
83− function itemPressedAndHeld(index, result, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
84− if (itemModel.uri.indexOf("scope://") !== 0 && !(scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
85− if (scope.preview(result)) {
86− openPreview(index, resultsModel, limitedCategoryItemCount);
87− }
88− }
75+ property var maybePreviewResult;
76+ property int maybePreviewIndex;
77+ property var maybePreviewResultsModel;
78+ property int maybePreviewLimitedCategoryItemCount;
79+ property string maybePreviewCategoryId;
80+
81+ function clearMaybePreviewData() {
82+ scopeView.maybePreviewResult = undefined;
83+ scopeView.maybePreviewIndex = -1;
84+ scopeView.maybePreviewResultsModel = undefined;
85+ scopeView.maybePreviewLimitedCategoryItemCount = -1;
86+ scopeView.maybePreviewCategoryId = "";
87+ }
88+
89+ function itemClicked(index, result, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
90+ scopeView.maybePreviewResult = result;
91+ scopeView.maybePreviewIndex = index;
92+ scopeView.maybePreviewResultsModel = resultsModel;
93+ scopeView.maybePreviewLimitedCategoryItemCount = limitedCategoryItemCount;
94+ scopeView.maybePreviewCategoryId = categoryId;
95+
96+ scope.activate(result, categoryId);
8997 }
9098
91− function openPreview(index, resultsModel, limitedCategoryItemCount) {
92− if (limitedCategoryItemCount > 0) {
93− previewLimitModel.model = resultsModel;
94− previewLimitModel.limit = limitedCategoryItemCount;
95− subPageLoader.model = previewLimitModel;
96− } else {
97− subPageLoader.model = resultsModel;
99+ function itemPressedAndHeld(index, result, resultsModel, limitedCategoryItemCount, categoryId) {
100+ clearMaybePreviewData();
101+
102+ openPreview(result, index, resultsModel, limitedCategoryItemCount, categoryId);
103+ }
104+
105+ function openPreview(result, index, resultsModel, limitedCategoryItemCount, categoryId) {
106+ var previewStack = scope.preview(result, categoryId);
107+ if (previewStack) {
108+ if (limitedCategoryItemCount > 0) {
109+ previewLimitModel.model = resultsModel;
110+ previewLimitModel.limit = limitedCategoryItemCount;
111+ subPageLoader.model = previewLimitModel;
112+ } else {
113+ subPageLoader.model = resultsModel;
114+ }
115+ subPageLoader.initialIndex = -1;
116+ subPageLoader.initialIndex = index;
117+ subPageLoader.categoryId = categoryId;
118+ subPageLoader.previewStack = previewStack;
119+ subPageLoader.openSubPage("preview");
98120 }
99− subPageLoader.initialIndex = -1;
100− subPageLoader.initialIndex = index;
101− subPageLoader.openSubPage("preview");
102121 }
103122
104123 Binding {
@@ -144,6 +163,17 @@ FocusScope {
144163 target: scopeView.scope
145164 onShowDash: subPageLoader.closeSubPage()
146165 onHideDash: subPageLoader.closeSubPage()
166+ onPreviewRequested: { // (QVariant const& result)
167+ if (result === scopeView.maybePreviewResult) {
168+ openPreview(result,
169+ scopeView.maybePreviewIndex,
170+ scopeView.maybePreviewResultsModel,
171+ scopeView.maybePreviewLimitedCategoryItemCount,
172+ scopeView.maybePreviewCategoryId);
173+
174+ clearMaybePreviewData();
175+ }
176+ }
147177 }
148178
149179 Connections {
@@ -352,12 +382,12 @@ FocusScope {
352382
353383 Connections {
354384 target: rendererLoader.item
355− onClicked: {
356− scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount(), baseItem.category);
385+ onClicked: { // (int index, var result, var item, var itemModel)
386+ scopeView.itemClicked(index, result, itemModel, target.model, categoryItemCount(), baseItem.category);
357387 }
358388
359− onPressAndHold: {
360− scopeView.itemPressedAndHeld(index, result, itemModel, target.model, categoryItemCount(), baseItem.category);
389+ onPressAndHold: { // (int index, var result, var itemModel)
390+ scopeView.itemPressedAndHeld(index, result, target.model, categoryItemCount(), baseItem.category);
361391 }
362392
363393 function categoryItemCount() {
@@ -731,6 +761,8 @@ FocusScope {
731761 property var scope: scopeView.scope
732762 property var scopeStyle: scopeView.scopeStyle
733763 property int initialIndex: -1
764+ property var previewStack;
765+ property string categoryId
734766 property var model: null
735767
736768 readonly property bool processing: item && item.processing || false
@@ -762,6 +794,9 @@ FocusScope {
762794 item.open = Qt.binding(function() { return subPageLoader.open; } )
763795 item.initialIndex = Qt.binding(function() { return subPageLoader.initialIndex; } )
764796 item.model = Qt.binding(function() { return subPageLoader.model; } )
797+ item.categoryId = Qt.binding(function() { return subPageLoader.categoryId; } )
798+ item.initialIndexPreviewStack = subPageLoader.previewStack;
799+ subPageLoader.previewStack = null;
765800 }
766801 open = true;
767802 }
qml/Dash/PreviewListView.qml+17−6
@@ -24,8 +24,11 @@ Item {
2424 id: root
2525
2626 property int initialIndex: -1
27+ property var initialIndexPreviewStack: null
2728 property var scope: null
2829 property var scopeStyle: null
30+ property string categoryId
31+ property bool usedInitialIndex: false
2932
3033 property alias showSignatureLine: header.showSignatureLine
3134
@@ -83,10 +86,10 @@ Item {
8386 }
8487 }
8588
86− onCountChanged: {
87− if (count > 0 && initialIndex >= 0) {
88− currentIndex = initialIndex;
89− initialIndex = -1;
89+ onModelChanged: {
90+ if (count > 0 && initialIndex >= 0 && !usedInitialIndex) {
91+ usedInitialIndex = true;
92+ previewListView.positionViewAtIndex(initialIndex, ListView.SnapPosition);
9093 }
9194 }
9295
@@ -99,8 +102,16 @@ Item {
99102 isCurrent: ListView.isCurrentItem
100103
101104 previewModel: {
102− var previewStack = root.scope.preview(result);
103− return previewStack.getPreviewModel(0);
105+ if (root.open) {
106+ if (index === root.initialIndex) {
107+ return root.initialIndexPreviewStack.getPreviewModel(0);
108+ } else {
109+ var previewStack = root.scope.preview(result, root.categoryId);
110+ return previewStack.getPreviewModel(0);
111+ }
112+ } else {
113+ return null;
114+ }
104115 }
105116 scopeStyle: root.scopeStyle
106117 }
qml/Dash/Previews/PreviewActions.qml+1−1
@@ -25,7 +25,7 @@ import Ubuntu.Components 1.1
2525PreviewWidget {
2626 id: root
2727
28− height: row.height + units.gu(1)
28+ implicitHeight: row.height + units.gu(1)
2929
3030 Row {
3131 id: row
qml/Dash/Previews/PreviewAudioPlayback.qml+1−1
@@ -34,7 +34,7 @@ import Ubuntu.Components 0.1
3434
3535PreviewWidget {
3636 id: root
37− height: childrenRect.height
37+ implicitHeight: childrenRect.height
3838
3939 onIsCurrentPreviewChanged: if (!isCurrentPreview) audio.stop()
4040
qml/Dash/Previews/PreviewHeader.qml+1−1
@@ -30,7 +30,7 @@ import "../"
3030PreviewWidget {
3131 id: root
3232
33− height: childrenRect.height
33+ implicitHeight: childrenRect.height
3434
3535 Item {
3636 id: headerRoot
qml/Dash/Previews/PreviewRatingDisplay.qml+1−1
@@ -38,7 +38,7 @@ import "../../Components"
3838
3939PreviewWidget {
4040 id: root
41− height: childrenRect.height
41+ implicitHeight: childrenRect.height
4242
4343 Column {
4444 anchors { left: parent.left; right: parent.right; }
qml/Dash/ScopesList.qml+1−1
@@ -124,7 +124,7 @@ Item {
124124 onRequestFavorite: root.requestFavorite(scopeId, favorite);
125125 onRequestEditMode: root.state = "edit";
126126 onRequestScopeMoveTo: root.requestFavoriteMoveTo(scopeId, index);
127− onRequestActivate: root.scope.activate(result);
127+ onRequestActivate: root.scope.activate(result, categoryId);
128128 onRequestRestore: root.requestRestore(scopeId);
129129 }
130130 }
qml/Launcher/Launcher.qml+2−1
@@ -177,7 +177,8 @@ Item {
177177 }
178178
179179 }
180− MouseArea {
180+
181+ MultiPointTouchArea {
181182 id: closeMouseArea
182183 anchors {
183184 left: launcherDragArea.right
qml/Launcher/LauncherDelegate.qml+113−15
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
1919
2020Item {
2121 id: root
@@ -24,9 +24,12 @@ Item {
2424 property int count: 0
2525 property bool countVisible: false
2626 property int progress: -1
27+ property bool itemRunning: false
2728 property bool itemFocused: false
2829 property real maxAngle: 0
2930 property bool inverted: false
31+ property bool alerting: false
32+ readonly property alias wiggling: wiggleAnim.running
3033
3134 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
3235 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
@@ -39,6 +42,82 @@ Item {
3942 property real offset: 0
4043 property real itemOpacity: 1
4144 property real brightness: 0
45+ property double maxWiggleAngle: 5.0
46+
47+ QtObject {
48+ id: priv
49+
50+ readonly property int wiggleDuration: UbuntuAnimation.SnapDuration
51+ property real wiggleAngle: 0
52+ }
53+
54+ SequentialAnimation {
55+ id: wiggleAnim
56+
57+ running: alerting
58+ loops: 1
59+ alwaysRunToEnd: true
60+
61+ NumberAnimation {
62+ target: priv
63+ property: "wiggleAngle"
64+ from: 0
65+ to: maxWiggleAngle
66+ duration: priv.wiggleDuration
67+ easing.type: Easing.InQuad
68+ }
69+
70+ NumberAnimation {
71+ target: priv
72+ property: "wiggleAngle"
73+ from: maxWiggleAngle
74+ to: -maxWiggleAngle
75+ duration: priv.wiggleDuration
76+ easing.type: Easing.InOutQuad
77+ }
78+
79+ NumberAnimation {
80+ target: priv
81+ property: "wiggleAngle"
82+ from: -maxWiggleAngle
83+ to: maxWiggleAngle
84+ duration: priv.wiggleDuration
85+ easing.type: Easing.InOutQuad
86+ }
87+
88+ NumberAnimation {
89+ target: priv
90+ property: "wiggleAngle"
91+ from: maxWiggleAngle
92+ to: -maxWiggleAngle
93+ duration: priv.wiggleDuration
94+ easing.type: Easing.InOutQuad
95+ }
96+
97+ NumberAnimation {
98+ target: priv
99+ property: "wiggleAngle"
100+ from: -maxWiggleAngle
101+ to: maxWiggleAngle
102+ duration: priv.wiggleDuration
103+ easing.type: Easing.InOutQuad
104+ }
105+
106+ NumberAnimation {
107+ target: priv
108+ property: "wiggleAngle"
109+ from: maxWiggleAngle
110+ to: 0
111+ duration: priv.wiggleDuration
112+ easing.type: Easing.OutQuad
113+ }
114+
115+ UbuntuNumberAnimation {
116+ target: root
117+ property: "alerting"
118+ to: 0
119+ }
120+ }
42121
43122 Item {
44123 id: iconItem
@@ -75,7 +154,7 @@ Item {
75154 objectName: "countEmblem"
76155 anchors {
77156 right: parent.right
78− top: parent.top
157+ bottom: parent.bottom
79158 margins: units.dp(3)
80159 }
81160 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
@@ -100,23 +179,21 @@ Item {
100179 }
101180 }
102181
103− BorderImage {
182+ UbuntuShape {
104183 id: progressOverlay
105184 objectName: "progressOverlay"
185+
106186 anchors {
107187 left: iconItem.left
108188 right: iconItem.right
109− bottom: iconItem.bottom
110− leftMargin: units.gu(1)
111− rightMargin: units.gu(1)
112− bottomMargin: units.gu(1)
189+ verticalCenter: parent.verticalCenter
190+ leftMargin: units.gu(1.5)
191+ rightMargin: units.gu(1.5)
113192 }
114− height: units.gu(1.5)
193+ height: units.gu(1)
115194 visible: root.progress > -1
116− source: "graphics/progressbar-trough.sci"
117−
118− // For fill calculation we need to remove the 2 units of border defined in .sci file
119− property int adjustedWidth: width - units.gu(2)
195+ color: UbuntuColors.darkGrey
196+ borderSource: "none"
120197
121198 Item {
122199 anchors {
@@ -124,20 +201,33 @@ Item {
124201 top: parent.top
125202 bottom: parent.bottom
126203 }
127− width: Math.min(100, root.progress) / 100 * parent.adjustedWidth + units.gu(1)
204+ width: Math.min(100, root.progress) / 100 * parent.width
128205 clip: true
129206
130− BorderImage {
207+ UbuntuShape {
131208 anchors {
132209 left: parent.left
133210 top: parent.top
134211 bottom: parent.bottom
135212 }
213+ color: "white"
214+ borderSource: "none"
136215 width: progressOverlay.width
137− source: "graphics/progressbar-fill.sci"
138216 }
139217 }
140218 }
219+
220+ Image {
221+ objectName: "runningHighlight"
222+ anchors {
223+ left: parent.left
224+ verticalCenter: parent.verticalCenter
225+ }
226+ visible: root.itemRunning
227+ rotation: 180
228+ source: "graphics/focused_app_arrow.png"
229+ }
230+
141231 Image {
142232 objectName: "focusedHighlight"
143233 anchors {
@@ -167,6 +257,14 @@ Item {
167257 }
168258
169259 transform: [
260+ // The rotation about the icon's center/z-axis for the wiggle
261+ // needs to happen here too, because there's no other way to
262+ // align the wiggle with the icon-folding otherwise
263+ Rotation {
264+ axis { x: 0; y: 0; z: 1 }
265+ origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
266+ angle: priv.wiggleAngle
267+ },
170268 // Rotating 3 times at top/bottom because that increases the perspective.
171269 // This is a hack, but as QML does not support real 3D coordinates
172270 // getting a higher perspective can only be done by a hack. This is the most
qml/Launcher/LauncherPanel.qml+95−14
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.3
18−import Ubuntu.Components 1.1
18+import Ubuntu.Components 1.2
1919import Ubuntu.Components.ListItems 1.0 as ListItems
2020import Unity.Launcher 0.1
2121import Ubuntu.Components.Popups 0.1
@@ -86,6 +86,7 @@ Rectangle {
8686 height: parent.height - dashItem.height - parent.spacing*2
8787
8888 Item {
89+ id: launcherListViewItem
8990 anchors.fill: parent
9091 clip: true
9192
@@ -109,6 +110,9 @@ Rectangle {
109110 preferredHighlightBegin: (height - itemHeight) / 2
110111 preferredHighlightEnd: (height + itemHeight) / 2
111112
113+ // for the single peeking icon, when alert-state is set on delegate
114+ property int peekingIndex: -1
115+
112116 // The size of the area the ListView is extended to make sure items are not
113117 // destroyed when dragging them outside the list. This needs to be at least
114118 // itemHeight to prevent folded items from disappearing and DragArea limits
@@ -159,6 +163,17 @@ Rectangle {
159163 to: launcherListView.contentHeight - launcherListView.height + launcherListView.originY - launcherListView.topMargin
160164 }
161165
166+ UbuntuNumberAnimation {
167+ id: moveAnimation
168+ target: launcherListView
169+ property: "contentY"
170+ function moveTo(contentY) {
171+ from = launcherListView.contentY;
172+ to = contentY;
173+ start();
174+ }
175+ }
176+
162177 displaced: Transition {
163178 NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
164179 }
@@ -178,12 +193,70 @@ Rectangle {
178193 count: model.count
179194 countVisible: model.countVisible
180195 progress: model.progress
196+ itemRunning: model.running
181197 itemFocused: model.focused
182198 inverted: root.inverted
199+ alerting: model.alerting
183200 z: -Math.abs(offset)
184201 maxAngle: 55
185202 property bool dragging: false
186203
204+ SequentialAnimation {
205+ id: peekingAnimation
206+
207+ // revealing
208+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 1 : 0 }
209+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 0 }
210+
211+ UbuntuNumberAnimation {
212+ target: launcherDelegate
213+ alwaysRunToEnd: true
214+ loops: 1
215+ properties: "x"
216+ to: (units.gu(.5) + launcherListView.width * .5) * (root.inverted ? -1 : 1)
217+ duration: UbuntuAnimation.BriskDuration
218+ }
219+
220+ // hiding
221+ UbuntuNumberAnimation {
222+ target: launcherDelegate
223+ alwaysRunToEnd: true
224+ loops: 1
225+ properties: "x"
226+ to: 0
227+ duration: UbuntuAnimation.BriskDuration
228+ }
229+
230+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 1 }
231+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 0 : 1 }
232+ }
233+
234+ onAlertingChanged: {
235+ if(alerting) {
236+ if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
237+ var itemPosition = index * launcherListView.itemHeight;
238+ var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
239+ var distanceToEnd = index == 0 || index == launcherListView.count - 1 ? 0 : launcherListView.itemHeight
240+ if (itemPosition + launcherListView.itemHeight + distanceToEnd > launcherListView.contentY + launcherListView.topMargin + height) {
241+ moveAnimation.moveTo(itemPosition + launcherListView.itemHeight - launcherListView.topMargin - height + distanceToEnd);
242+ } else if (itemPosition - distanceToEnd < launcherListView.contentY + launcherListView.topMargin) {
243+ moveAnimation.moveTo(itemPosition - distanceToEnd - launcherListView.topMargin);
244+ }
245+ if (!dragging && launcher.state !== "visible") {
246+ peekingAnimation.start()
247+ }
248+ }
249+
250+ if (launcherListView.peekingIndex === -1) {
251+ launcherListView.peekingIndex = index
252+ }
253+ } else {
254+ if (launcherListView.peekingIndex === index) {
255+ launcherListView.peekingIndex = -1
256+ }
257+ }
258+ }
259+
187260 ThinDivider {
188261 id: dropIndicator
189262 objectName: "dropIndicator"
@@ -286,6 +359,7 @@ Rectangle {
286359 MouseArea {
287360 id: dndArea
288361 objectName: "dndArea"
362+ acceptedButtons: Qt.LeftButton | Qt.RightButton
289363 anchors {
290364 fill: parent
291365 topMargin: launcherListView.topMargin
@@ -297,7 +371,7 @@ Rectangle {
297371 property int draggedIndex: -1
298372 property var selectedItem
299373 property bool preDragging: false
300− property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging
374+ property bool dragging: !!selectedItem && selectedItem.dragging
301375 property bool postDragging: false
302376 property int startX
303377 property int startY
@@ -315,6 +389,15 @@ Rectangle {
315389 return;
316390 }
317391
392+ if (mouse.button & Qt.RightButton) { // context menu
393+ // Opening QuickList
394+ quickList.item = clickedItem;
395+ quickList.model = launcherListView.model.get(index).quickList;
396+ quickList.appId = launcherListView.model.get(index).appId;
397+ quickList.state = "open";
398+ return
399+ }
400+
318401 // First/last item do the scrolling at more than 12 degrees
319402 if (index == 0 || index == launcherListView.count - 1) {
320403 if (clickedItem.angle > 12) {
@@ -508,7 +591,7 @@ Rectangle {
508591 id: quickListShape
509592 objectName: "quickListShape"
510593 anchors.fill: quickList
511− opacity: quickList.state === "open" ? 0.96 : 0
594+ opacity: quickList.state === "open" ? 0.8 : 0
512595 visible: opacity > 0
513596 rotation: root.rotation
514597
@@ -520,15 +603,15 @@ Rectangle {
520603
521604 Image {
522605 anchors {
523− left: parent.left
524− leftMargin: quickList.item ? (quickList.item.width - units.gu(1)) / 2 - width / 2 : 0
606+ right: parent.left
607+ rightMargin: -units.dp(4)
525608 verticalCenter: parent.verticalCenter
526− verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
609+ verticalCenterOffset: -quickList.offset * (root.inverted ? -1 : 1)
527610 }
528611 height: units.gu(1)
529612 width: units.gu(2)
530613 source: "graphics/quicklist_tooltip.png"
531− rotation: (quickList.offset > 0 ? 0 : 180) + (root.inverted ? 0 : 180)
614+ rotation: 90
532615 }
533616
534617 InverseMouseArea {
@@ -551,11 +634,11 @@ Rectangle {
551634 height: quickListColumn.height
552635 visible: quickListShape.visible
553636 anchors {
554− left: root.inverted ? undefined : parent.left
555− right: root.inverted ? parent.right : undefined
637+ left: root.inverted ? undefined : parent.right
638+ right: root.inverted ? parent.left : undefined
556639 margins: units.gu(1)
557640 }
558− y: itemCenter + offset
641+ y: itemCenter - (height / 2) + offset
559642 rotation: root.rotation
560643
561644 property var model
@@ -564,10 +647,8 @@ Rectangle {
564647
565648 // internal
566649 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
567− property int offset: item ? itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
568− -(item.height/2) - height - units.gu(.5) :
569− (item.height/2) + units.gu(.5) :
570− 0
650+ property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
651+ itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
571652
572653 Column {
573654 id: quickListColumn
qml/Launcher/graphics/progressbar-fill@30.pngdeleted

Binary file not shown.

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

Binary file not shown.

qml/Launcher/graphics/progressbar-trough@30.scideleted−5
@@ -1,5 +0,0 @@
1−border.left: 36
2−border.top: 16
3−border.bottom: 26
4−border.right: 36
5−source: progressbar-trough@30.png
qml/OrientedShell.qml+6−3
@@ -21,6 +21,9 @@ import GSettings 1.0
2121import "Components"
2222import "Components/UnityInputInfo"
2323import "Rotation"
24+// Backup Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
25+// in case we remove the UnityInputInfo import
26+import Ubuntu.Components 1.2
2427
2528Rectangle {
2629 id: root
@@ -43,7 +46,7 @@ Rectangle {
4346
4447
4548 // to be overwritten by tests
46− property var usageModeSettings: GSettings { schema.id: "com.canonical.Unity8" }
49+ property var unity8Settings: Unity8Settings {}
4750 property var oskSettings: GSettings { schema.id: "com.canonical.keyboard.maliit" }
4851
4952 property int physicalOrientation: Screen.orientation
@@ -160,9 +163,9 @@ Rectangle {
160163 // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
161164 // what's the output device (eg: big TV, desktop monitor, phone display), etc.
162165 usageScenario: {
163− if (root.usageModeSettings.usageMode === "Windowed") {
166+ if (root.unity8Settings.usageMode === "Windowed") {
164167 return "desktop";
165− } else if (root.usageModeSettings.usageMode === "Staged") {
168+ } else if (root.unity8Settings.usageMode === "Staged") {
166169 if (deviceConfiguration.category === "phone") {
167170 return "phone";
168171 } else {
qml/Panel/IndicatorItem.qml+3−3
@@ -31,9 +31,9 @@ IndicatorDelegate {
3131 property bool selected: false
3232 property real iconHeight: units.gu(2)
3333 readonly property color color: {
34− if (!expanded) return "#ededed";
35− if (!selected) return "#4c4c4c";
36− return "#ededed";
34+ if (!expanded) return "#ffffff";
35+ if (!selected) return "#888888";
36+ return "#ffffff";
3737 }
3838
3939 signal clicked()
qml/Panel/IndicatorItemRow.qml+1−1
@@ -31,7 +31,7 @@ Item {
3131 property real unitProgress: 0.0
3232 property real selectionChangeBuffer: units.gu(2)
3333 property bool enableLateralChanges: false
34− property color hightlightColor: "#ededed"
34+ property color hightlightColor: "#ffffff"
3535
3636 property real lateralPosition: -1
3737 onLateralPositionChanged: {
qml/Panel/IndicatorsBar.qml+1−1
@@ -123,7 +123,7 @@ Item {
123123 width: parent.width
124124 anchors.bottom: parent.bottom
125125
126− color: "#4c4c4c"
126+ color: "#888888"
127127 opacity: expanded ? 1.0 : 0.0
128128 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
129129 }
qml/Panel/IndicatorsMenu.qml+3−1
@@ -36,6 +36,7 @@ Showable {
3636 readonly property bool fullyClosed: unitProgress == 0
3737 property bool enableHint: true
3838 property bool contentEnabled: true
39+ property bool showOnClick: true
3940 property color panelColor: "black"
4041
4142 signal showTapped(point position)
@@ -68,6 +69,7 @@ Showable {
6869 // eater
6970 MouseArea {
7071 anchors.fill: parent
72+ hoverEnabled: true
7173 }
7274
7375 MenuContent {
@@ -168,7 +170,7 @@ Showable {
168170 anchors.left: parent.left
169171 anchors.right: parent.right
170172 height: minimizedPanelHeight
171− enabled: __showDragHandle.enabled
173+ enabled: __showDragHandle.enabled && showOnClick
172174 onClicked: {
173175 bar.selectItemAt(mouseX)
174176 root.show()
qml/Panel/Panel.qml+1
@@ -147,6 +147,7 @@ Item {
147147 return root.width
148148 }
149149 enableHint: !callHint.active && !fullscreenMode
150+ showOnClick: !callHint.visible
150151 panelColor: indicatorAreaBackground.color
151152
152153 onShowTapped: {
qml/Shell.qml+23−21
@@ -178,6 +178,7 @@ Item {
178178
179179 PhysicalKeysMapper {
180180 id: physicalKeysMapper
181+ objectName: "physicalKeysMapper"
181182
182183 onPowerKeyLongPressed: dialogs.showPowerDialog()
183184 onVolumeDownTriggered: volumeControl.volumeDown();
@@ -188,13 +189,6 @@ Item {
188189 ScreenGrabber {
189190 id: screenGrabber
190191 z: dialogs.z + 10
191− enabled: Powerd.status === Powerd.On
192− }
193−
194− Binding {
195− target: ApplicationManager
196− property: "forceDashActive"
197− value: launcher.shown || launcher.dashSwipe
198192 }
199193
200194 WindowKeysFilter {
@@ -352,26 +346,40 @@ Item {
352346 property: "beingResized"
353347 value: shell.beingResized
354348 }
349+ Binding {
350+ target: applicationsDisplayLoader.item
351+ property: "keepDashRunning"
352+ value: launcher.shown || launcher.dashSwipe
353+ }
354+ Binding {
355+ target: applicationsDisplayLoader.item
356+ property: "suspended"
357+ value: greeter.shown
358+ }
359+ Binding {
360+ target: applicationsDisplayLoader.item
361+ property: "altTabPressed"
362+ value: physicalKeysMapper.altTabPressed
363+ }
355364 }
356365
357366 Tutorial {
358367 id: tutorial
359368 objectName: "tutorial"
360369 anchors.fill: parent
361− active: AccountsService.demoEdges
362− paused: LightDM.Greeter.active
363− launcher: launcher
364− panel: panel
365− edgeSize: shell.edgeSize
366370
367371 // EdgeDragAreas don't work with mice. So to avoid trapping the user,
368− // we'll tell the tutorial to avoid using them on the Desktop. The
372+ // we skip the tutorial on the Desktop to avoid using them. The
369373 // Desktop doesn't use the same spread design anyway. The tutorial is
370374 // all a bit of a placeholder on non-phone form factors right now.
371375 // When the design team gives us more guidance, we can do something
372376 // more clever here.
373− // TODO: use DeviceConfiguration instead of checking source
374− useEdgeDragArea: applicationsDisplayLoader.source != Qt.resolvedUrl("Stages/DesktopStage.qml")
377+ active: usageScenario != "desktop" && AccountsService.demoEdges
378+
379+ paused: LightDM.Greeter.active
380+ launcher: launcher
381+ panel: panel
382+ edgeSize: shell.edgeSize
375383
376384 onFinished: {
377385 AccountsService.demoEdges = false;
@@ -454,12 +462,6 @@ Item {
454462 }
455463
456464 onEmergencyCall: startLockedApp("dialer-app")
457−
458− Binding {
459− target: ApplicationManager
460− property: "suspended"
461− value: greeter.shown
462− }
463465 }
464466 }
465467
qml/Stages/ApplicationWindow.qml+3
@@ -135,6 +135,9 @@ FocusScope {
135135 onSurfaceChanged: {
136136 if (sessionContainer.surface) {
137137 surfaceInitTimer.start();
138+ if (ApplicationManager.focusedApplicationId == application.appId) {
139+ root.forceActiveFocus();
140+ }
138141 } else {
139142 d.surfaceInitialized = false;
140143 }
qml/Stages/DecoratedWindow.qml+72−8
@@ -23,21 +23,67 @@ import Unity.Application 0.1
2323FocusScope {
2424 id: root
2525
26+ property alias window: applicationWindow
2627 property alias application: applicationWindow.application
2728 property alias active: decoration.active
2829
30+ property bool decorationShown: true
31+ property bool highlightShown: false
32+ property real shadowOpacity: 1
33+
34+ property int windowWidth: width
35+ property int windowHeight: height
36+
2937 signal close();
3038 signal maximize();
3139 signal minimize();
3240
41+ state: "normal"
42+ states: [
43+ State {
44+ name: "normal"
45+ PropertyChanges {
46+ target: root
47+ width: windowWidth
48+ height: windowHeight
49+ }
50+ },
51+ State {
52+ name: "transformed"
53+ PropertyChanges {
54+ target: applicationWindow
55+ itemScale: Math.max(root.width / root.windowWidth, root.height / root.windowHeight)
56+ interactive: false
57+ }
58+ PropertyChanges {
59+ target: clipper
60+ clip: true
61+ }
62+ }
63+ ]
64+
3365 BorderImage {
3466 anchors {
3567 fill: root
3668 margins: -units.gu(2)
3769 }
3870 source: "graphics/dropshadow2gu.sci"
39− opacity: .3
40− Behavior on opacity { UbuntuNumberAnimation {} }
71+ opacity: root.shadowOpacity * .3
72+ }
73+
74+ Rectangle {
75+ id: selectionHighlight
76+ anchors.fill: parent
77+ anchors.margins: -units.gu(1)
78+ color: "white"
79+ opacity: highlightShown ? 0.15 : 0
80+ }
81+
82+ Rectangle {
83+ anchors { left: selectionHighlight.left; right: selectionHighlight.right; bottom: selectionHighlight.bottom; }
84+ height: units.dp(2)
85+ color: UbuntuColors.orange
86+ visible: root.highlightShown
4187 }
4288
4389 WindowDecoration {
@@ -49,14 +95,32 @@ FocusScope {
4995 onClose: root.close();
5096 onMaximize: root.maximize();
5197 onMinimize: root.minimize();
98+ visible: decorationShown
5299 }
53100
54− ApplicationWindow {
55− id: applicationWindow
56− objectName: application ? "appWindow_" + application.appId : "appWindow_null"
101+ Item {
102+ id: clipper
57103 anchors.fill: parent
58− anchors.topMargin: units.gu(3)
59− interactive: true
60− focus: true
104+
105+ ApplicationWindow {
106+ id: applicationWindow
107+ objectName: application ? "appWindow_" + application.appId : "appWindow_null"
108+ anchors.top: parent.top
109+ anchors.topMargin: root.decorationShown ? decoration.height : 0
110+ anchors.left: parent.left
111+ width: root.windowWidth
112+ height: root.windowHeight - (root.decorationShown ? decoration.height : 0)
113+ interactive: true
114+ focus: true
115+
116+ property real itemScale: 1
117+ transform: [
118+ Scale {
119+ origin.x: 0; origin.y: 0
120+ xScale: applicationWindow.itemScale
121+ yScale: applicationWindow.itemScale
122+ }
123+ ]
124+ }
61125 }
62126}
qml/Stages/DesktopStage.qml+511−57
@@ -17,13 +17,15 @@
1717 */
1818
1919import QtQuick 2.3
20+import QtQuick.Layouts 1.1
2021import Ubuntu.Components 1.1
2122import Unity.Application 0.1
2223import "../Components/PanelState"
24+import Utils 0.1
25+import Ubuntu.Gestures 0.1
2326
2427Rectangle {
2528 id: root
26−
2729 anchors.fill: parent
2830
2931 // Controls to be set from outside
@@ -37,6 +39,8 @@ Rectangle {
3739 property int shellPrimaryOrientation
3840 property int nativeOrientation
3941 property bool beingResized: false
42+ property bool keepDashRunning: true
43+ property bool suspended: false
4044
4145 // functions to be called from outside
4246 function updateFocusedAppOrientation() { /* TODO */ }
@@ -50,6 +54,7 @@ Rectangle {
5054 readonly property bool orientationChangesEnabled: false
5155
5256 property alias background: wallpaper.source
57+ property bool altTabPressed: false
5358
5459 CrossFadeImage {
5560 id: wallpaper
@@ -61,15 +66,18 @@ Rectangle {
6166 Connections {
6267 target: ApplicationManager
6368 onApplicationAdded: {
64− ApplicationManager.requestFocusApplication(ApplicationManager.get(ApplicationManager.count-1).appId)
69+ if (root.state == "altTab") {
70+ root.state = "";
71+ }
72+
73+ ApplicationManager.requestFocusApplication(appId)
6574 }
6675
6776 onFocusRequested: {
6877 var appIndex = priv.indexOf(appId);
6978 var appDelegate = appRepeater.itemAt(appIndex);
70− if (appDelegate.state === "minimized") {
71− appDelegate.state = "normal"
72− }
79+ appDelegate.minimized = false;
80+ appDelegate.focus = true;
7381 ApplicationManager.focusApplication(appId);
7482 }
7583 }
@@ -104,8 +112,8 @@ Rectangle {
104112 onClose: {
105113 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
106114 }
107− onMinimize: appRepeater.itemAt(0).state = "minimized"
108− onMaximize: appRepeater.itemAt(0).state = "normal"
115+ onMinimize: appRepeater.itemAt(0).minimize();
116+ onMaximize: appRepeater.itemAt(0).unmaximize();
109117 }
110118
111119 Binding {
@@ -114,67 +122,513 @@ Rectangle {
114122 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
115123 }
116124
117− Repeater {
118− id: appRepeater
119− model: ApplicationManager
125+ Rectangle {
126+ id: spreadBackground
127+ anchors.fill: parent
128+ color: "#55000000"
129+ visible: false
130+ }
120131
121− delegate: FocusScope {
122− id: appDelegate
123− z: ApplicationManager.count - index
124− y: units.gu(3)
125− width: units.gu(60)
126− height: units.gu(50)
132+ FocusScope {
133+ id: appContainer
134+ objectName: "appContainer"
135+ anchors.fill: parent
127136
128− onFocusChanged: {
129− if (focus) {
130− ApplicationManager.requestFocusApplication(model.appId);
131− }
137+ Keys.onPressed: {
138+ switch (event.key) {
139+ case Qt.Key_Left:
140+ case Qt.Key_Backtab:
141+ selectPrevious(event.isAutoRepeat)
142+ break;
143+ case Qt.Key_Right:
144+ case Qt.Key_Tab:
145+ selectNext(event.isAutoRepeat)
146+ break;
147+ case Qt.Key_Escape:
148+ appRepeater.highlightedIndex = -1
149+ case Qt.Key_Enter:
150+ case Qt.Key_Return:
151+ case Qt.Key_Space:
152+ root.state = ""
153+ }
154+ }
155+
156+ function selectNext(isAutoRepeat) {
157+ if (isAutoRepeat && appRepeater.highlightedIndex >= ApplicationManager.count -1) {
158+ return; // AutoRepeat is not allowed to wrap around
159+ }
160+
161+ appRepeater.highlightedIndex = (appRepeater.highlightedIndex + 1) % ApplicationManager.count;
162+ var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, appRepeater.highlightedIndex - 3));
163+ if (spreadFlickable.contentX < newContentX || appRepeater.highlightedIndex == 0) {
164+ spreadFlickable.snapTo(newContentX)
165+ }
166+ }
167+
168+ function selectPrevious(isAutoRepeat) {
169+ if (isAutoRepeat && appRepeater.highlightedIndex == 0) {
170+ return; // AutoRepeat is not allowed to wrap around
171+ }
172+
173+ var newIndex = appRepeater.highlightedIndex - 1 >= 0 ? appRepeater.highlightedIndex - 1 : ApplicationManager.count - 1;
174+ appRepeater.highlightedIndex = newIndex;
175+ var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, appRepeater.highlightedIndex - 1));
176+ if (spreadFlickable.contentX > newContentX || newIndex == ApplicationManager.count -1) {
177+ spreadFlickable.snapTo(newContentX)
178+ }
179+ }
180+
181+ function focusSelected() {
182+ if (appRepeater.highlightedIndex != -1) {
183+ appRepeater.itemAt(appRepeater.highlightedIndex).focus = true;
132184 }
185+ }
186+
187+ Repeater {
188+ id: appRepeater
189+ model: ApplicationManager
190+ objectName: "appRepeater"
191+
192+ property int highlightedIndex: -1
193+ property int closingIndex: -1
194+
195+ delegate: FocusScope {
196+ id: appDelegate
197+ z: ApplicationManager.count - index
198+ y: units.gu(3)
199+ width: units.gu(60)
200+ height: units.gu(50)
201+
202+ readonly property int minWidth: units.gu(10)
203+ readonly property int minHeight: units.gu(10)
204+
205+ property bool maximized: false
206+ property bool minimized: false
207+
208+ onFocusChanged: {
209+ if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
210+ ApplicationManager.requestFocusApplication(model.appId);
211+ decoratedWindow.forceActiveFocus();
212+ }
213+ }
214+ Component.onCompleted: {
215+ if (ApplicationManager.focusedApplicationId == model.appId) {
216+ decoratedWindow.forceActiveFocus();
217+ }
218+ }
219+
220+ Binding {
221+ target: ApplicationManager.get(index)
222+ property: "requestedState"
223+ // TODO: figure out some lifecycle policy, like suspending minimized apps
224+ // if running on a tablet or something.
225+ // TODO: If the device has a dozen suspended apps because it was running
226+ // in staged mode, when it switches to Windowed mode it will suddenly
227+ // resume all those apps at once. We might want to avoid that.
228+ value: ApplicationInfoInterface.RequestedRunning // Always running for now
229+ }
230+
231+ function maximize() {
232+ minimized = false;
233+ maximized = true;
234+ }
235+ function minimize() {
236+ maximized = false;
237+ minimized = true;
238+ }
239+ function unmaximize() {
240+ minimized = false;
241+ maximized = false;
242+ }
243+
244+ Behavior on x {
245+ id: closeBehavior
246+ enabled: appRepeater.closingIndex >= 0
247+ UbuntuNumberAnimation {
248+ onRunningChanged: if (!running) appRepeater.closingIndex = -1
249+ }
250+ }
251+
252+ states: [
253+ State {
254+ name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized && root.state !== "altTab"
255+ },
256+ State {
257+ name: "maximized"; when: appDelegate.maximized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
258+ PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
259+ },
260+ State {
261+ name: "minimized"; when: appDelegate.minimized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
262+ PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
263+ },
264+ State {
265+ name: "altTab"; when: root.state == "altTab" && root.workspacesUpdated
266+ PropertyChanges {
267+ target: appDelegate
268+ x: spreadMaths.animatedX
269+ y: spreadMaths.animatedY + (appDelegate.height - decoratedWindow.height)
270+ angle: spreadMaths.animatedAngle
271+ itemScale: spreadMaths.scale
272+ itemScaleOriginY: decoratedWindow.height / 2;
273+ z: index
274+ visible: spreadMaths.itemVisible
275+ }
276+ PropertyChanges {
277+ target: decoratedWindow
278+ decorationShown: false
279+ highlightShown: index == appRepeater.highlightedIndex
280+ state: "transformed"
281+ width: spreadMaths.spreadHeight
282+ height: spreadMaths.spreadHeight
283+ shadowOpacity: spreadMaths.shadowOpacity
284+ }
285+ PropertyChanges {
286+ target: tileInfo
287+ visible: true
288+ opacity: spreadMaths.tileInfoOpacity
289+ }
290+ PropertyChanges {
291+ target: spreadSelectArea
292+ enabled: true
293+ }
294+ PropertyChanges {
295+ target: windowMoveResizeArea
296+ enabled: false
297+ }
298+ }
299+ ]
300+ transitions: [
301+ Transition {
302+ from: "maximized,minimized,normal,"
303+ to: "maximized,minimized,normal,"
304+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
305+ }
306+ ]
307+ property real angle: 0
308+ property real itemScale: 1
309+ property int itemScaleOriginX: 0
310+ property int itemScaleOriginY: 0
311+
312+ SpreadMaths {
313+ id: spreadMaths
314+ flickable: spreadFlickable
315+ itemIndex: index
316+ totalItems: Math.max(6, ApplicationManager.count)
317+ sceneHeight: root.height
318+ itemHeight: appDelegate.height
319+ }
320+
321+ WindowMoveResizeArea {
322+ id: windowMoveResizeArea
323+ target: appDelegate
324+ minWidth: appDelegate.minWidth
325+ minHeight: appDelegate.minHeight
326+ resizeHandleWidth: units.gu(2)
327+ windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
133328
134− readonly property int minWidth: units.gu(10)
135− readonly property int minHeight: units.gu(10)
136−
137− states: [
138− State {
139− name: "normal"
140− },
141− State {
142− name: "maximized"
143− PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
144− },
145− State {
146− name: "minimized"
147− PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
329+ onPressed: appDelegate.focus = true;
148330 }
149− ]
150− transitions: [
151− Transition {
152− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
331+
332+ DecoratedWindow {
333+ id: decoratedWindow
334+ objectName: "decoratedWindow"
335+ anchors.left: appDelegate.left
336+ anchors.top: appDelegate.top
337+ windowWidth: appDelegate.width
338+ windowHeight: appDelegate.height
339+ application: ApplicationManager.get(index)
340+ active: ApplicationManager.focusedApplicationId === model.appId
341+ focus: false
342+
343+ onClose: ApplicationManager.stopApplication(model.appId)
344+ onMaximize: appDelegate.maximize()
345+ onMinimize: appDelegate.minimize()
346+
347+ transform: [
348+ Scale {
349+ origin.x: itemScaleOriginX
350+ origin.y: itemScaleOriginY
351+ xScale: itemScale
352+ yScale: itemScale
353+ },
354+ Rotation {
355+ origin { x: 0; y: (decoratedWindow.height - (decoratedWindow.height * itemScale / 2)) }
356+ axis { x: 0; y: 1; z: 0 }
357+ angle: appDelegate.angle
358+ }
359+ ]
360+
361+ MouseArea {
362+ id: spreadSelectArea
363+ anchors.fill: parent
364+ anchors.margins: -units.gu(2)
365+ enabled: false
366+ hoverEnabled: enabled
367+
368+ // There is a bug in MouseArea where containsMouse doesn't
369+ // return to false if the MouseArea is disabled while
370+ // containing the mouse. Let's manage the property our own.
371+ property bool upperThirdContainsMouse: false
372+ onContainsMouseChanged: evaluateContainsMouse()
373+ onMouseYChanged: evaluateContainsMouse()
374+ function evaluateContainsMouse() {
375+ if (containsMouse) {
376+ appRepeater.highlightedIndex = index
377+ }
378+
379+ if (containsMouse && mouseY < height / 3) {
380+ spreadSelectArea.upperThirdContainsMouse = true
381+ } else {
382+ spreadSelectArea.upperThirdContainsMouse = false;
383+ }
384+ }
385+ onEnabledChanged: {
386+ if (!enabled) {
387+ spreadSelectArea.upperThirdContainsMouse = false
388+ }
389+ }
390+
391+ onClicked: {
392+ root.state = ""
393+ }
394+ }
395+ }
396+
397+ Image {
398+ id: closeImage
399+ anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
400+ source: "graphics/window-close.svg"
401+ visible: spreadSelectArea.upperThirdContainsMouse
402+ height: units.gu(1.5)
403+ width: height
404+ sourceSize.width: width
405+ sourceSize.height: height
406+ }
407+
408+ MouseArea {
409+ id: closeMouseArea
410+ objectName: "closeMouseArea"
411+ anchors.fill: closeImage
412+ anchors.margins: -units.gu(2)
413+ enabled: spreadSelectArea.upperThirdContainsMouse
414+ onClicked: {
415+ appRepeater.closingIndex = index;
416+ ApplicationManager.stopApplication(model.appId)
417+ }
153418 }
154− ]
155419
156− WindowMoveResizeArea {
157− target: appDelegate
158− minWidth: appDelegate.minWidth
159− minHeight: appDelegate.minHeight
160− resizeHandleWidth: units.gu(0.5)
161− windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
420+ MouseArea {
421+ id: tileInfo
422+ objectName: "tileInfo"
423+ anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(5) }
424+ width: units.gu(30)
425+ height: titleInfoColumn.height
426+ visible: false
427+ hoverEnabled: true
428+
429+ onContainsMouseChanged: {
430+ if (containsMouse) {
431+ appRepeater.highlightedIndex = index
432+ }
433+ }
434+
435+ onClicked: {
436+ root.state = ""
437+ }
438+
439+ ColumnLayout {
440+ id: titleInfoColumn
441+ anchors { left: parent.left; top: parent.top; right: parent.right }
442+ spacing: units.gu(1)
443+
444+ UbuntuShape {
445+ Layout.preferredHeight: Math.min(units.gu(6), root.height * .05)
446+ Layout.preferredWidth: height * 8 / 7.6
447+ image: Image {
448+ anchors.fill: parent
449+ source: model.icon
450+ }
451+ }
452+ Label {
453+ Layout.fillWidth: true
454+ Layout.preferredHeight: units.gu(6)
455+ text: model.name
456+ wrapMode: Text.WordWrap
457+ maximumLineCount: 2
458+ }
459+ }
460+ }
461+ }
462+ }
463+ }
464+
465+ FloatingFlickable {
466+ id: spreadFlickable
467+ anchors.fill: parent
468+ contentWidth: Math.max(6, ApplicationManager.count) * Math.min(height / 4, width / 5)
469+ enabled: false
470+
471+ function snapTo(contentX) {
472+ snapAnimation.stop();
473+ snapAnimation.to = contentX
474+ snapAnimation.start();
475+ }
476+
477+ UbuntuNumberAnimation {
478+ id: snapAnimation
479+ target: spreadFlickable
480+ property: "contentX"
481+ }
482+ }
483+
484+ Item {
485+ id: workspaceSelector
486+ anchors {
487+ left: parent.left
488+ top: parent.top
489+ right: parent.right
490+ topMargin: units.gu(3.5) // TODO: should be root.panelHeight
491+ }
492+ height: root.height * 0.25
493+ visible: false
494+
495+ RowLayout {
496+ anchors.fill: parent
497+ spacing: units.gu(1)
498+ Item { Layout.fillWidth: true }
499+ Repeater {
500+ model: 1 // TODO: will be a workspacemodel in the future
501+ Item {
502+ Layout.fillHeight: true
503+ Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
504+ Image {
505+ source: root.background
506+ anchors {
507+ left: parent.left
508+ right: parent.right
509+ verticalCenter: parent.verticalCenter
510+ }
511+ height: parent.height * 0.75
512+
513+ // FIXME: This is temporary until we can have multiple Items per surface
514+ ShaderEffect {
515+ anchors.fill: parent
516+
517+ property var source: ShaderEffectSource {
518+ id: shaderEffectSource
519+ live: false
520+ sourceItem: appContainer
521+ Connections { target: root; onUpdateWorkspaces: shaderEffectSource.scheduleUpdate() }
522+ }
523+
524+ fragmentShader: "
525+ varying highp vec2 qt_TexCoord0;
526+ uniform sampler2D source;
527+ void main(void)
528+ {
529+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
530+ gl_FragColor = sourceColor;
531+ }"
532+ }
533+ }
534+
535+ // TODO: This is the bar for the currently selected workspace
536+ // Enable this once the workspace stuff is implemented
537+// Rectangle {
538+// anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
539+// height: units.dp(2)
540+// color: UbuntuColors.orange
541+// visible: index == 0 // TODO: should be active workspace index
542+// }
543+ }
162544
163− onPressed: appDelegate.focus = true;
164545 }
546+ // TODO: This is the "new workspace" button. Enable this once workspaces are implemented
547+// Item {
548+// Layout.fillHeight: true
549+// Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
550+// Rectangle {
551+// anchors {
552+// left: parent.left
553+// right: parent.right
554+// verticalCenter: parent.verticalCenter
555+// }
556+// height: parent.height * 0.75
557+// color: "#22ffffff"
558+
559+// Label {
560+// anchors.centerIn: parent
561+// font.pixelSize: parent.height / 2
562+// text: "+"
563+// }
564+// }
565+// }
566+ Item { Layout.fillWidth: true }
567+ }
568+ }
165569
166− DecoratedWindow {
167− id: decoratedWindow
168− objectName: "decoratedWindow_" + appId
169− anchors.fill: parent
170− application: ApplicationManager.get(index)
171− active: ApplicationManager.focusedApplicationId === model.appId
172− focus: true
173−
174− onClose: ApplicationManager.stopApplication(model.appId)
175− onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
176− onMinimize: appDelegate.state = "minimized"
570+ Label {
571+ id: currentSelectedLabel
572+ anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
573+ text: appRepeater.highlightedIndex >= 0 ? ApplicationManager.get(appRepeater.highlightedIndex).name : ""
574+ visible: false
575+ fontSize: "large"
576+ }
577+
578+ states: [
579+ State {
580+ name: "altTab"; when: root.altTabPressed
581+ PropertyChanges { target: workspaceSelector; visible: true }
582+ PropertyChanges { target: spreadFlickable; enabled: true }
583+ PropertyChanges { target: currentSelectedLabel; visible: true }
584+ PropertyChanges { target: spreadBackground; visible: true }
585+ PropertyChanges { target: appContainer; focus: true }
586+ }
587+ ]
588+ signal updateWorkspaces();
589+ property bool workspacesUpdated: false
590+ transitions: [
591+ Transition {
592+ from: "*"
593+ to: "altTab"
594+ SequentialAnimation {
595+ PropertyAction { target: appRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) }
596+ PauseAnimation { duration: 50 }
597+ PropertyAction { target: workspaceSelector; property: "visible" }
598+ ScriptAction { script: root.updateWorkspaces() }
599+ // FIXME: Updating of shaderEffectSource take a bit of time. This is temporary until we can paint multiple items per surface
600+ PauseAnimation { duration: 10 }
601+ PropertyAction { target: root; property: "workspacesUpdated"; value: true }
602+ PropertyAction { target: spreadFlickable; property: "visible" }
603+ PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
604+ PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
177605 }
606+ },
607+ Transition {
608+ from: "*"
609+ to: "*"
610+ PropertyAnimation { property: "opacity" }
611+ PropertyAction { target: root; property: "workspacesUpdated"; value: false }
612+ ScriptAction { script: { appContainer.focusSelected() } }
613+ PropertyAction { target: appRepeater; property: "highlightedIndex"; value: -1 }
178614 }
615+
616+ ]
617+
618+ MouseArea {
619+ anchors {
620+ top: parent.top
621+ right: parent.right
622+ bottom: parent.bottom
623+ }
624+ // TODO: Make this a push to edge thing like the launcher when we can,
625+ // for now, yes, we want 1 pixel, regardless of the scaling
626+ width: 1
627+ hoverEnabled: true
628+ onContainsMouseChanged: {
629+ if (containsMouse) {
630+ root.state = "altTab"
631+ }
632+ }
179633 }
180634}
qml/Stages/PhoneStage.qml+10
@@ -36,6 +36,8 @@ Rectangle {
3636 property bool altTabEnabled: true
3737 property real startScale: 1.1
3838 property real endScale: 0.7
39+ property bool keepDashRunning: true
40+ property bool suspended: false
3941 property int shellOrientationAngle: 0
4042 property int shellOrientation
4143 property int shellPrimaryOrientation
@@ -428,6 +430,14 @@ Rectangle {
428430 dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
429431 focusFirstApp: root.focusFirstApp
430432
433+ Binding {
434+ target: appDelegate.application
435+ property: "requestedState"
436+ value: (isDash && root.keepDashRunning) || (!root.suspended && appDelegate.focus)
437+ ? ApplicationInfoInterface.RequestedRunning
438+ : ApplicationInfoInterface.RequestedSuspended
439+ }
440+
431441 readonly property bool isDash: model.appId == "unity8-dash"
432442
433443 z: isDash && !spreadView.active ? -1 : behavioredIndex
qml/Stages/SpreadMaths.qmlnew file+164
@@ -0,0 +1,164 @@
1+/*
2+ * Copyright (C) 2015 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.2
18+import Ubuntu.Components 1.1
19+import Utils 0.1
20+
21+Item {
22+ id: root
23+ anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
24+
25+ // Information about the transformed item
26+ property int itemIndex: 0
27+ property real itemHeight: units.gu(10)
28+
29+ // Information about the environment
30+ property int totalItems: 0
31+ property Item flickable: null
32+ property int sceneHeight: units.gu(20)
33+
34+ // Spread properties
35+ property real spreadHeight: sceneHeight * 0.4
36+ property int spreadBottomOffset: sceneHeight * 0.18
37+ property int foldingAreaWidth: flickableWidth * 0.2
38+ property int maxVisibleItems: 7
39+ property int margins: flickableWidth * 0.05
40+ property real stackScale: 0.1
41+ property int leftEndFoldedAngle: 70
42+ property int rightEndFoldedAngle: 65
43+ property int unfoldedAngle: 30
44+ property int stackWidth: flickableWidth * 0.01
45+
46+
47+ // Internal
48+ readonly property int flickableWidth: flickable ? flickable.width : 0
49+ readonly property int flickableContentWidth: flickable ? flickable.contentWidth: 0
50+ readonly property real flickableProgress: flickable ? flickable.contentX / (flickable.contentWidth - flickableWidth) : 0
51+
52+ readonly property int contentWidth: flickableWidth - root.margins * 2
53+
54+ readonly property int distance: (flickableContentWidth - (margins * 2) - (foldingAreaWidth * 2)) / (totalItems - 2)
55+ readonly property int startPos: margins + foldingAreaWidth + (itemIndex - 1) * distance
56+ readonly property int linearX: startPos - flickableProgress * (flickableContentWidth - flickableWidth)
57+
58+ readonly property int leftFoldingAreaX: margins + foldingAreaWidth
59+ readonly property int rightFoldingAreaX: flickableWidth - foldingAreaWidth - margins
60+
61+ readonly property real leftFoldingAreaProgress: linearAnimation(leftFoldingAreaX, margins, 0, 1, linearX)
62+ readonly property real rightFoldingAreaProgress: linearAnimation(rightFoldingAreaX, flickableWidth - margins, 0, 1, linearX)
63+
64+ readonly property real limitedLeftProgress: Math.min(2, leftFoldingAreaProgress)
65+ readonly property real limitedRightProgress: Math.min(2, rightFoldingAreaProgress)
66+
67+ readonly property real middleSectionProgress: (linearX - margins - foldingAreaWidth) / (flickableWidth - (margins + foldingAreaWidth) * 2)
68+
69+ // Output
70+ readonly property int animatedX: {
71+ if (leftFoldingAreaProgress > 4) { // Stop it at the edge
72+ return margins;
73+ }
74+ if (leftFoldingAreaProgress > 2) { // move it slowly through the stack
75+ return linearAnimation(2, 4, margins + stackWidth, margins, leftFoldingAreaProgress)
76+ }
77+ if (leftFoldingAreaProgress > 1 && itemIndex == 0) {
78+ // The leftmost runs faster... make it stop before the stack and wait for others
79+ return margins + stackWidth;
80+ }
81+
82+ if (leftFoldingAreaProgress > 0) { // slow it down in a curve
83+ if (itemIndex == 0) { // except if it's the leftmost. that one goes straigt
84+ return linearAnimation(0, 1, leftFoldingAreaX, margins + stackWidth, leftFoldingAreaProgress)
85+ }
86+ return linearAnimation(0, 1, leftFoldingAreaX, margins + stackWidth, leftEasing.value)
87+ }
88+ // same for the right side stack... mostly... don't need to treat the rightmost special...
89+ if (rightFoldingAreaProgress > 4) {
90+ return flickableWidth - margins
91+ }
92+ if (rightFoldingAreaProgress > 2) {
93+ return linearAnimation(2, 4, flickableWidth - margins - stackWidth, flickableWidth - margins, rightFoldingAreaProgress)
94+ }
95+
96+ if (rightFoldingAreaProgress > 0) {
97+ return linearAnimation(0, 1, rightFoldingAreaX, flickableWidth - margins - stackWidth, rightEasing.value);
98+ }
99+
100+ return linearX
101+ }
102+
103+ readonly property int animatedY: sceneHeight - itemHeight - spreadBottomOffset
104+
105+ readonly property real animatedAngle: {
106+ if (limitedLeftProgress > 0) {
107+ // Leftmost is special...
108+ if (index == 0) {
109+ if (limitedLeftProgress < 1) {
110+ return unfoldedAngle;
111+ } else {
112+ return linearAnimation(1, 2, unfoldedAngle, leftEndFoldedAngle, limitedLeftProgress)
113+ }
114+ }
115+ return linearAnimation(0, 2, unfoldedAngle, leftEndFoldedAngle, limitedLeftProgress)
116+ } else if (limitedRightProgress > 0) {
117+ return linearAnimation(0, 1, unfoldedAngle, rightEndFoldedAngle, rightEasing.value)
118+ } else {
119+ return unfoldedAngle
120+ }
121+ }
122+
123+ readonly property real scale: limitedLeftProgress > 0 ?
124+ linearAnimation(0, 1, 1, 1 + stackScale, leftEasing.value)
125+ : limitedRightProgress > 0 ?
126+ linearAnimation(0, 1, 1, 1 + stackScale, rightEasing.value)
127+ : 0.95 + Math.abs(middleSectionProgress - 0.5) * 0.1
128+
129+ readonly property real closeIconOffset: (scale - 1) * (-root.spreadHeight / 2)
130+
131+ readonly property real tileInfoOpacity: leftFoldingAreaProgress > 0 ?
132+ linearAnimation(1, 1.5, 1, 0, leftFoldingAreaProgress)
133+ : rightFoldingAreaProgress > 0 ?
134+ linearAnimation(1, 1.5, 1, 0, rightFoldingAreaProgress)
135+ : 1
136+
137+ readonly property bool itemVisible: itemIndex == totalItems - 1 ? true : leftFoldingAreaProgress < 5 && rightFoldingAreaProgress < 5
138+ readonly property real shadowOpacity: itemIndex == totalItems -1 ?
139+ 1
140+ : leftFoldingAreaProgress > 3 ?
141+ linearAnimation(3, 3.5, 1, 0, leftFoldingAreaProgress)
142+ : rightFoldingAreaProgress > 3 ?
143+ linearAnimation(3, 3.5, 1, 0, rightFoldingAreaProgress)
144+ : 1
145+
146+
147+ // Helpers
148+ function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
149+ // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
150+ return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
151+ }
152+
153+ EasingCurve {
154+ id: leftEasing
155+ type: EasingCurve.OutSine
156+ progress: limitedLeftProgress / 2 // OutSine starts with twice the speed. slow it down.
157+ }
158+
159+ EasingCurve {
160+ id: rightEasing
161+ type: EasingCurve.OutSine
162+ progress: limitedRightProgress / 2 // OutSine starts with twice the speed. slow it down.
163+ }
164+}
qml/Stages/TabletStage.qml+14
@@ -36,6 +36,8 @@ Rectangle {
3636 property bool spreadEnabled: true // If false, animations and right edge will be disabled
3737
3838 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
39+ property bool keepDashRunning: true
40+ property bool suspended: false
3941 property int shellOrientationAngle: 0
4042 property int shellOrientation
4143 property int shellPrimaryOrientation
@@ -117,6 +119,7 @@ Rectangle {
117119
118120 QtObject {
119121 id: priv
122+ objectName: "stagesPriv"
120123
121124 property string focusedAppId: ApplicationManager.focusedApplicationId
122125 readonly property var focusedAppDelegate: {
@@ -626,6 +629,17 @@ Rectangle {
626629
627630 readonly property bool isDash: model.appId == "unity8-dash"
628631
632+ Binding {
633+ target: spreadTile.application
634+ property: "requestedState"
635+ value: (spreadTile.isDash && root.keepDashRunning)
636+ ||
637+ (!root.suspended && (model.appId == priv.mainStageAppId
638+ || model.appId == priv.sideStageAppId))
639+ ? ApplicationInfoInterface.RequestedRunning
640+ : ApplicationInfoInterface.RequestedSuspended
641+ }
642+
629643 // FIXME: A regular binding doesn't update any more after closing an app.
630644 // Using a Binding for now.
631645 Binding {
qml/Stages/graphics/window-close.svgnew file+64
@@ -0,0 +1,64 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3+
4+<svg
5+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6+ xmlns:cc="http://creativecommons.org/ns#"
7+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8+ xmlns:svg="http://www.w3.org/2000/svg"
9+ xmlns="http://www.w3.org/2000/svg"
10+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12+ width="14.81667mm"
13+ height="14.816668mm"
14+ viewBox="0 0 14.81667 14.816668"
15+ id="svg4171"
16+ version="1.1"
17+ inkscape:version="0.91+devel r"
18+ sodipodi:docname="window-close01.svg">
19+ <defs
20+ id="defs4173" />
21+ <sodipodi:namedview
22+ id="base"
23+ pagecolor="#ffffff"
24+ bordercolor="#666666"
25+ borderopacity="1.0"
26+ inkscape:pageopacity="0.0"
27+ inkscape:pageshadow="2"
28+ inkscape:zoom="11.957142"
29+ inkscape:cx="37.299889"
30+ inkscape:cy="25.219237"
31+ inkscape:document-units="mm"
32+ inkscape:current-layer="layer1"
33+ showgrid="false"
34+ fit-margin-top="0"
35+ fit-margin-left="0"
36+ fit-margin-right="0"
37+ fit-margin-bottom="0" />
38+ <metadata
39+ id="metadata4176">
40+ <rdf:RDF>
41+ <cc:Work
42+ rdf:about="">
43+ <dc:format>image/svg+xml</dc:format>
44+ <dc:type
45+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
46+ <dc:title></dc:title>
47+ </cc:Work>
48+ </rdf:RDF>
49+ </metadata>
50+ <g
51+ inkscape:label="Layer 1"
52+ inkscape:groupmode="layer"
53+ id="layer1"
54+ transform="translate(-134.71071,-3.8416665)">
55+ <path
56+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:16.93333244;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
57+ d="M 142.11905,3.8416665 A 7.4083333,7.4083333 0 0 0 134.71071,11.25 7.4083333,7.4083333 0 0 0 142.11905,18.658334 7.4083333,7.4083333 0 0 0 149.52738,11.25 7.4083333,7.4083333 0 0 0 142.11905,3.8416665 Z"
58+ id="path4141" />
59+ <path
60+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:16.93333244;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
61+ d="m 139.31818,7.7008631 2.80087,2.8008629 2.80086,-2.8008629 0.74827,0.7482734 -2.80086,2.8008635 2.80086,2.800864 -0.74827,0.748273 -2.80086,-2.800864 -2.80087,2.800864 -0.74827,-0.748273 2.80086,-2.800864 -2.80086,-2.8008635 0.74827,-0.7482734 z"
62+ id="path4170" />
63+ </g>
64+</svg>
qml/Tutorial/Tutorial.qml−7
@@ -23,7 +23,6 @@ Item {
2323 property alias active: loader.active
2424 property bool paused
2525 property real edgeSize
26− property bool useEdgeDragArea
2726
2827 property Item launcher
2928 property Item panel
@@ -59,12 +58,6 @@ Item {
5958 value: root.edgeSize
6059 }
6160
62− Binding {
63− target: loader.item
64− property: "useEdgeDragArea"
65− value: root.useEdgeDragArea
66− }
67−
6861 Binding {
6962 target: loader.item
7063 property: "launcher"
qml/Tutorial/TutorialContent.qml+2−9
@@ -32,7 +32,6 @@ Item {
3232
3333 property bool paused: false
3434 property real edgeSize
35− property bool useEdgeDragArea
3635
3736 signal finished()
3837
@@ -78,17 +77,11 @@ Item {
7877 anchors.fill: parent
7978 textXOffset: root.launcher.panelWidth
8079 paused: !shown || root.paused
81− text: root.useEdgeDragArea ? i18n.tr("Tap here to continue.") : i18n.tr("Click here to finish.")
80+ text: i18n.tr("Tap here to continue.")
8281
8382 onFinished: {
8483 root.launcher.hide();
85− if (root.useEdgeDragArea) {
86− tutorialRight.show();
87− } else {
88− // Last two screens use EdgeDragArea (right edge spread and
89− // bottom edge). So just end tutorial here.
90− root.finish();
91− }
84+ tutorialRight.show();
9285 }
9386 }
9487
tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+47
@@ -67,6 +67,7 @@ private Q_SLOTS:
6767 void rejectingTouchfterItsEnd();
6868 void removeOldUndecidedCandidates();
6969 void interimOwnerWontGetUnownedTouchEvents();
70+ void candidateVanishes();
7071
7172private:
7273 TouchRegistry *touchRegistry;
@@ -817,6 +818,52 @@ void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
817818 QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
818819}
819820
821+/*
822+ Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473492
823+
824+ Bug caused by candidates getting deleted before ownership resolution
825+ */
826+void tst_TouchRegistry::candidateVanishes()
827+{
828+ DummyCandidate mainCandidate;
829+ DummyCandidate *missingCandidate = new DummyCandidate;
830+
831+ {
832+ QList<QTouchEvent::TouchPoint> touchPoints;
833+ touchPoints.append(QTouchEvent::TouchPoint(0));
834+ touchPoints[0].setState(Qt::TouchPointPressed);
835+ QTouchEvent touchEvent(QEvent::TouchBegin,
836+ 0 /* device */,
837+ Qt::NoModifier,
838+ Qt::TouchPointPressed,
839+ touchPoints);
840+ touchRegistry->update(&touchEvent);
841+ }
842+
843+ touchRegistry->addCandidateOwnerForTouch(0, &mainCandidate);
844+ touchRegistry->addCandidateOwnerForTouch(0, missingCandidate);
845+
846+ {
847+ QList<QTouchEvent::TouchPoint> touchPoints;
848+ touchPoints.append(QTouchEvent::TouchPoint(0));
849+ touchPoints[0].setState(Qt::TouchPointMoved);
850+ QTouchEvent touchEvent(QEvent::TouchUpdate,
851+ 0 /* device */,
852+ Qt::NoModifier,
853+ Qt::TouchPointMoved,
854+ touchPoints);
855+ touchRegistry->update(&touchEvent);
856+ }
857+
858+ // This candidate suddenly disappears without having removed its candidacy.
859+ delete missingCandidate;
860+ missingCandidate = nullptr;
861+
862+ touchRegistry->requestTouchOwnership(0, &mainCandidate);
863+
864+ QVERIFY(mainCandidate.ownedTouches.contains(0));
865+}
866+
820867////////////// TouchMemento //////////
821868
822869TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
tests/mocks/Unity/Application/ApplicationInfo.cpp+40−6
@@ -30,7 +30,7 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
3030 : ApplicationInfoInterface(appId, parent)
3131 , m_appId(appId)
3232 , m_stage(MainStage)
33− , m_state(Starting)
33+ , m_state(Stopped)
3434 , m_focused(false)
3535 , m_fullscreen(false)
3636 , m_session(0)
@@ -39,6 +39,7 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
3939 Qt::InvertedPortraitOrientation |
4040 Qt::InvertedLandscapeOrientation)
4141 , m_rotatesWindowContents(false)
42+ , m_requestedState(RequestedRunning)
4243 , m_manualSurfaceCreation(false)
4344{
4445}
@@ -46,7 +47,7 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
4647ApplicationInfo::ApplicationInfo(QObject *parent)
4748 : ApplicationInfoInterface(QString(), parent)
4849 , m_stage(MainStage)
49− , m_state(Starting)
50+ , m_state(Stopped)
5051 , m_focused(false)
5152 , m_fullscreen(false)
5253 , m_session(0)
@@ -55,6 +56,7 @@ ApplicationInfo::ApplicationInfo(QObject *parent)
5556 Qt::InvertedPortraitOrientation |
5657 Qt::InvertedLandscapeOrientation)
5758 , m_rotatesWindowContents(false)
59+ , m_requestedState(RequestedRunning)
5860 , m_manualSurfaceCreation(false)
5961{
6062}
@@ -74,7 +76,6 @@ void ApplicationInfo::createSession()
7476
7577void ApplicationInfo::setSession(Session* session)
7678{
77− qDebug() << "Application::setSession - appId=" << appId() << "session=" << session;
7879 if (m_session == session)
7980 return;
8081
@@ -91,6 +92,8 @@ void ApplicationInfo::setSession(Session* session)
9192 m_session->setApplication(this);
9293 m_session->setParent(this);
9394 SessionManager::singleton()->registerSession(m_session);
95+ connect(m_session, &Session::surfaceChanged,
96+ this, &ApplicationInfo::onSessionSurfaceChanged);
9497
9598 if (!m_manualSurfaceCreation) {
9699 QTimer::singleShot(500, m_session, SLOT(createSurface()));
@@ -161,11 +164,12 @@ void ApplicationInfo::setState(State value)
161164 m_state = value;
162165 Q_EMIT stateChanged(value);
163166
164− if (!m_manualSurfaceCreation && m_state == ApplicationInfo::Running) {
167+ if (!m_manualSurfaceCreation && !m_session && m_state == ApplicationInfo::Starting) {
165168 QTimer::singleShot(500, this, SLOT(createSession()));
166169 } else if (m_state == ApplicationInfo::Stopped) {
167− delete m_session;
168− m_session = nullptr;
170+ Session *session = m_session;
171+ setSession(nullptr);
172+ delete session;
169173 }
170174 }
171175}
@@ -213,3 +217,33 @@ void ApplicationInfo::setRotatesWindowContents(bool value)
213217{
214218 m_rotatesWindowContents = value;
215219}
220+
221+ApplicationInfo::RequestedState ApplicationInfo::requestedState() const
222+{
223+ return m_requestedState;
224+}
225+
226+void ApplicationInfo::setRequestedState(RequestedState value)
227+{
228+ if (m_requestedState != value) {
229+ m_requestedState = value;
230+ Q_EMIT requestedStateChanged(m_requestedState);
231+
232+ if (m_requestedState == RequestedRunning && m_state == Suspended) {
233+ setState(Running);
234+ } else if (m_requestedState == RequestedSuspended && m_state == Running) {
235+ setState(Suspended);
236+ }
237+ }
238+}
239+
240+void ApplicationInfo::onSessionSurfaceChanged(MirSurfaceItem* surface)
241+{
242+ if (surface != nullptr && m_state == Starting) {
243+ if (m_requestedState == RequestedRunning) {
244+ setState(Running);
245+ } else {
246+ setState(Suspended);
247+ }
248+ }
249+}
tests/mocks/Unity/Application/ApplicationInfo.h+7
@@ -46,6 +46,9 @@ public:
4646 ApplicationInfo(const QString &appId, QObject *parent = nullptr);
4747 ~ApplicationInfo();
4848
49+ RequestedState requestedState() const override;
50+ void setRequestedState(RequestedState) override;
51+
4952 void setIconId(const QString &iconId);
5053 void setScreenshotId(const QString &screenshotId);
5154
@@ -101,6 +104,9 @@ Q_SIGNALS:
101104public Q_SLOTS:
102105 Q_INVOKABLE void createSession();
103106
107+private Q_SLOTS:
108+ void onSessionSurfaceChanged(MirSurfaceItem*);
109+
104110private:
105111 void setIcon(const QUrl &value);
106112
@@ -116,6 +122,7 @@ private:
116122 Session* m_session;
117123 Qt::ScreenOrientations m_supportedOrientations;
118124 bool m_rotatesWindowContents;
125+ RequestedState m_requestedState;
119126
120127 bool m_manualSurfaceCreation;
121128};
tests/mocks/Unity/Application/ApplicationManager.cpp+2−49
@@ -49,8 +49,6 @@ ApplicationManager *ApplicationManager::singleton()
4949
5050ApplicationManager::ApplicationManager(QObject *parent)
5151 : ApplicationManagerInterface(parent)
52− , m_suspended(false)
53− , m_forceDashActive(false)
5452{
5553 m_roleNames.insert(RoleSession, "session");
5654 m_roleNames.insert(RoleFullscreen, "fullscreen");
@@ -217,7 +215,7 @@ ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
217215 && application->stage() == ApplicationInfo::SideStage) {
218216 application->setStage(ApplicationInfo::MainStage);
219217 }
220− application->setState(ApplicationInfo::Running);
218+ application->setState(ApplicationInfo::Starting);
221219
222220 return application;
223221}
@@ -262,50 +260,6 @@ QString ApplicationManager::focusedApplicationId() const {
262260 return QString();
263261}
264262
265−bool ApplicationManager::suspended() const
266−{
267− return m_suspended;
268−}
269−
270−void ApplicationManager::setSuspended(bool suspended)
271−{
272− ApplicationInfo *focusedApp = findApplication(focusedApplicationId());
273− if (focusedApp) {
274− if (suspended) {
275− focusedApp->setState(ApplicationInfo::Suspended);
276− } else {
277− focusedApp->setState(ApplicationInfo::Running);
278− }
279− }
280− m_suspended = suspended;
281− Q_EMIT suspendedChanged();
282−}
283−
284−bool ApplicationManager::forceDashActive() const
285−{
286− return m_forceDashActive;
287−}
288−
289−void ApplicationManager::setForceDashActive(bool forceDashActive)
290−{
291− if (m_forceDashActive == forceDashActive) {
292− return;
293− }
294−
295− ApplicationInfo *dash = findApplication("unity8-dash");
296− if (dash) {
297− if (forceDashActive) {
298− dash->setState(ApplicationInfo::Running);
299− } else {
300− if (!dash->focused()) {
301− dash->setState(ApplicationInfo::Suspended);
302− }
303− }
304− }
305− m_forceDashActive = forceDashActive;
306− Q_EMIT forceDashActiveChanged();
307−}
308−
309263bool ApplicationManager::focusApplication(const QString &appId)
310264{
311265 ApplicationInfo *application = findApplication(appId);
@@ -316,13 +270,11 @@ bool ApplicationManager::focusApplication(const QString &appId)
316270 for (ApplicationInfo *app : m_runningApplications) {
317271 if (app->focused()) {
318272 app->setFocused(false);
319− app->setState(ApplicationInfo::Suspended);
320273 }
321274 }
322275
323276 // focus this app
324277 application->setFocused(true);
325− application->setState(ApplicationInfo::Running);
326278
327279 // move app to top of stack
328280 move(m_runningApplications.indexOf(application), 0);
@@ -394,6 +346,7 @@ void ApplicationManager::buildListOfAvailableApplications()
394346 application->setScreenshotId("gallery");
395347 application->setIconId("gallery");
396348 application->setFullscreen(true);
349+ application->setStage(ApplicationInfo::MainStage);
397350 m_availableApplications.append(application);
398351
399352 application = new ApplicationInfo(this);
tests/mocks/Unity/Application/ApplicationManager.h−7
@@ -69,11 +69,6 @@ class ApplicationManager : public ApplicationManagerInterface {
6969 Q_INVOKABLE bool stopApplication(const QString &appId) override;
7070
7171 QString focusedApplicationId() const override;
72− bool suspended() const override;
73− void setSuspended(bool suspended) override;
74−
75− bool forceDashActive() const override;
76− void setForceDashActive(bool forceDashActive) override;
7772
7873 // Only for testing
7974 QStringList availableApplications();
@@ -96,8 +91,6 @@ class ApplicationManager : public ApplicationManagerInterface {
9691 void remove(ApplicationInfo* application);
9792 void buildListOfAvailableApplications();
9893 void onWindowCreated();
99− bool m_suspended;
100− bool m_forceDashActive;
10194 QList<ApplicationInfo*> m_runningApplications;
10295 QList<ApplicationInfo*> m_availableApplications;
10396 QTimer m_windowCreatedTimer;
tests/mocks/Unity/CMakeLists.txt+1−1
@@ -7,7 +7,7 @@ add_subdirectory(Notifications)
77add_subdirectory(DashCommunicator)
88
99pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
10−pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=6)
10+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=7)
1111
1212include_directories(
1313 ${CMAKE_CURRENT_BINARY_DIR}
tests/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
22
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+22
@@ -37,9 +37,13 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
3737 m_count(0),
3838 m_countVisible(false),
3939 m_focused(false),
40+ m_alerting(false),
4041 m_quickList(new MockQuickListModel(this))
4142{
43+}
4244
45+MockLauncherItem::~MockLauncherItem()
46+{
4347}
4448
4549QString MockLauncherItem::appId() const
@@ -129,6 +133,9 @@ void MockLauncherItem::setCount(int count)
129133 {
130134 m_count = count;
131135 Q_EMIT countChanged(count);
136+ if (m_countVisible) {
137+ setAlerting(true);
138+ }
132139 }
133140}
134141
@@ -142,6 +149,9 @@ void MockLauncherItem::setCountVisible(bool countVisible)
142149 if (m_countVisible != countVisible) {
143150 m_countVisible = countVisible;
144151 Q_EMIT countVisibleChanged(countVisible);
152+ if (countVisible) {
153+ setAlerting(true);
154+ }
145155 }
146156}
147157
@@ -159,6 +169,18 @@ void MockLauncherItem::setFocused(bool focused)
159169 }
160170}
161171
172+bool MockLauncherItem::alerting() const
173+{
174+ return m_alerting;
175+}
176+
177+void MockLauncherItem::setAlerting(bool alerting)
178+{
179+ if (m_alerting != alerting) {
180+ m_alerting = alerting;
181+ Q_EMIT alertingChanged(alerting);
182+ }
183+}
162184
163185unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
164186{
tests/mocks/Unity/Launcher/MockLauncherItem.h+4
@@ -31,6 +31,7 @@ class MockLauncherItem: public LauncherItemInterface
3131 Q_OBJECT
3232public:
3333 MockLauncherItem(const QString &appId, const QString& desktopFile, const QString& name, const QString& icon, QObject* parent = 0);
34+ ~MockLauncherItem();
3435
3536 QString appId() const override;
3637 QString desktopFile() const;
@@ -44,6 +45,7 @@ public:
4445 int count() const override;
4546 bool countVisible() const override;
4647 bool focused() const override;
48+ bool alerting() const override;
4749
4850 unity::shell::launcher::QuickListModelInterface *quickList() const override;
4951
@@ -55,6 +57,7 @@ private:
5557 void setCount(int count);
5658 void setCountVisible(bool countVisible);
5759 void setFocused(bool focused);
60+ void setAlerting(bool alerting);
5861
5962 QString m_appId;
6063 QString m_desktopFile;
@@ -67,6 +70,7 @@ private:
6770 int m_count;
6871 bool m_countVisible;
6972 bool m_focused;
73+ bool m_alerting;
7074 MockQuickListModel *m_quickList;
7175
7276 friend class MockLauncherModel;
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+54−1
@@ -34,6 +34,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
3434 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
3535 item->setProgress(50);
3636 item->setCountVisible(true);
37+ item->setAlerting(false);
3738 m_list.append(item);
3839 item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
3940 m_list.append(item);
@@ -43,25 +44,31 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
4344 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
4445 item->setCount(1);
4546 item->setCountVisible(true);
47+ item->setRunning(true);
48+ item->setAlerting(false);
4649 m_list.append(item);
4750 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
4851 item->setCount(12);
4952 item->setCountVisible(true);
53+ item->setAlerting(false);
5054 item->setPinned(true);
5155 m_list.append(item);
5256 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
5357 item->setCount(123);
5458 item->setCountVisible(true);
59+ item->setAlerting(false);
5560 m_list.append(item);
5661 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
5762 item->setCount(1234567890);
5863 item->setCountVisible(true);
64+ item->setAlerting(false);
5965 item->setPinned(true);
6066 m_list.append(item);
6167 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
6268 item->setProgress(50);
6369 item->setCount(5);
6470 item->setCountVisible(true);
71+ item->setAlerting(false);
6572 item->setFocused(true);
6673 item->setPinned(true);
6774 m_list.append(item);
@@ -105,6 +112,8 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
105112 return item->countVisible();
106113 case RoleFocused:
107114 return item->focused();
115+ case RoleAlerting:
116+ return item->alerting();
108117 }
109118
110119 return QVariant();
@@ -119,6 +128,18 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
119128 return m_list.at(index);
120129}
121130
131+void MockLauncherModel::setAlerting(const QString &appId, bool alerting) {
132+ int index = findApp(appId);
133+ if (index >= 0) {
134+ QModelIndex modelIndex = this->index(index);
135+ MockLauncherItem *item = m_list.at(index);
136+ if (!item->focused()) {
137+ item->setAlerting(alerting);
138+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
139+ }
140+ }
141+}
142+
122143void MockLauncherModel::move(int oldIndex, int newIndex)
123144{
124145 // Make sure its not moved outside the lists
@@ -174,7 +195,9 @@ void MockLauncherModel::requestRemove(const QString &appId)
174195 int index = findApp(appId);
175196 if (index >= 0) {
176197 beginRemoveRows(QModelIndex(), index, 0);
177− m_list.takeAt(index)->deleteLater();
198+ MockLauncherItem * item = m_list.takeAt(index);
199+ item->setRunning(false);
200+ item->deleteLater();
178201 endRemoveRows();
179202 }
180203}
@@ -195,6 +218,16 @@ int MockLauncherModel::findApp(const QString &appId)
195218 return -1;
196219}
197220
221+void MockLauncherModel::setProgress(const QString &appId, int progress)
222+{
223+ int index = findApp(appId);
224+ if (index >= 0) {
225+ m_list.at(index)->setProgress(progress);
226+ QModelIndex modelIndex = this->index(index);
227+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleProgress);
228+ }
229+}
230+
198231void MockLauncherModel::setUser(const QString &username)
199232{
200233 Q_UNUSED(username)
@@ -226,6 +259,26 @@ void MockLauncherModel::emitHint()
226259 Q_EMIT hint();
227260}
228261
262+void MockLauncherModel::setCount(const QString &appId, int count)
263+{
264+ int index = findApp(appId);
265+ if (index >= 0) {
266+ m_list.at(index)->setCount(count);
267+ QModelIndex modelIndex = this->index(index);
268+ Q_EMIT dataChanged(modelIndex, modelIndex);
269+ }
270+}
271+
272+void MockLauncherModel::setCountVisible(const QString &appId, bool countVisible)
273+{
274+ int index = findApp(appId);
275+ if (index >= 0) {
276+ m_list.at(index)->setCountVisible(countVisible);
277+ QModelIndex modelIndex = this->index(index);
278+ Q_EMIT dataChanged(modelIndex, modelIndex);
279+ }
280+}
281+
229282unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
230283{
231284 return nullptr;
tests/mocks/Unity/Launcher/MockLauncherModel.h+4
@@ -38,6 +38,7 @@ public:
3838
3939 QVariant data(const QModelIndex& index, int role) const override;
4040
41+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
4142 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const override;
4243 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
4344 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
@@ -54,6 +55,9 @@ public:
5455
5556 // For testing
5657 Q_INVOKABLE void emitHint();
58+ Q_INVOKABLE void setProgress(const QString &appId, int progress);
59+ Q_INVOKABLE void setCount(const QString &appId, int count);
60+ Q_INVOKABLE void setCountVisible(const QString &appId, bool countVisible);
5761
5862Q_SIGNALS:
5963 void quickListTriggered(const QString &appId, int index);
tests/mocks/Unity/fake_scope.cpp+5−3
@@ -181,18 +181,20 @@ void Scope::setNoResultsHint(const QString& str)
181181 }
182182}
183183
184−void Scope::activate(QVariant const& result)
184+void Scope::activate(QVariant const& result, QString const& categoryId)
185185{
186− qDebug() << "Called activate on scope" << m_id << "with result" << result;
186+ qDebug() << "Called activate on scope" << m_id << "with result" << result << "and category" << categoryId;
187187 if (result.toString() == "Result.2.2") {
188188 Scopes *scopes = dynamic_cast<Scopes*>(parent());
189189 m_openScope = scopes->getScopeFromAll("MockScope9");
190190 scopes->addTempScope(m_openScope);
191191 Q_EMIT openScope(m_openScope);
192+ } else {
193+ Q_EMIT previewRequested(result);
192194 }
193195}
194196
195−PreviewStack* Scope::preview(QVariant const& result)
197+PreviewStack* Scope::preview(QVariant const& result, QString const& /*categoryId*/)
196198{
197199 Q_UNUSED(result);
198200
tests/mocks/Unity/fake_scope.h+2−2
@@ -60,8 +60,8 @@ public:
6060 Q_INVOKABLE void setName(const QString &name); // This is not invokable in the Interface, here for testing benefits
6161 Q_INVOKABLE void setSearchInProgress(const bool inProg); // This is not invokable in the Interface, here for testing benefits
6262
63− Q_INVOKABLE void activate(QVariant const& result) override;
64− Q_INVOKABLE PreviewStack* preview(QVariant const& result) override;
63+ Q_INVOKABLE void activate(QVariant const& result, QString const& categoryId) override;
64+ Q_INVOKABLE PreviewStack* preview(QVariant const& result, QString const& categoryId) override;
6565 Q_INVOKABLE void cancelActivation() override;
6666 Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
6767
tests/mocks/Unity/fake_scopesoverview.cpp+1−1
@@ -41,7 +41,7 @@ void ScopesOverview::setSearchQuery(const QString& search_query)
4141 Q_EMIT categoriesChanged();
4242}
4343
44−Q_INVOKABLE void ScopesOverview::activate(QVariant const& result)
44+Q_INVOKABLE void ScopesOverview::activate(QVariant const& result, QString const& /*categoryId*/)
4545{
4646 Scopes *scopes = dynamic_cast<Scopes*>(parent());
4747 if (scopes->getScope(result.toString())) {
tests/mocks/Unity/fake_scopesoverview.h+1−1
@@ -32,7 +32,7 @@ public:
3232 ScopesOverview(Scopes* parent = 0);
3333
3434 void setSearchQuery(const QString& search_query) override;
35− Q_INVOKABLE void activate(QVariant const& result) override;
35+ Q_INVOKABLE void activate(QVariant const& result, QString const& categoryId) override;
3636
3737 // This is implementation detail
3838 void setFavorite(Scope *scope, bool favorite);
tests/mocks/Utils/CMakeLists.txt+3−1
@@ -4,12 +4,12 @@ include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
55 ${CMAKE_CURRENT_BINARY_DIR}
66 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
78 ${GIO_INCLUDE_DIRS}
89 ${CMAKE_SOURCE_DIR}/plugins/Utils
910)
1011
1112set(QMLPLUGIN_SRC
12− ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
1313 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
1414 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
1515 ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
@@ -18,9 +18,11 @@ set(QMLPLUGIN_SRC
1818 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
1919 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
2020 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
2122 constants.cpp
2223 plugin.cpp
2324 windowstatestorage.cpp
25+ plugin.cpp
2426 )
2527
2628add_library(FakeUtils-qml SHARED
tests/mocks/Utils/plugin.cpp+9−1
@@ -23,6 +23,14 @@
2323
2424// local
2525#include "plugin.h"
26+#include "inputwatcher.h"
27+#include "qlimitproxymodelqml.h"
28+#include "unitysortfilterproxymodelqml.h"
29+#include "relativetimeformatter.h"
30+#include "timeformatter.h"
31+#include "unitymenumodelpaths.h"
32+#include "windowkeysfilter.h"
33+#include "easingcurve.h"
2634#include "windowstatestorage.h"
2735#include "constants.h"
2836
@@ -53,7 +61,7 @@ static QObject *createConstants(QQmlEngine *engine, QJSEngine *scriptEngine)
5361
5462void FakeUtilsPlugin::registerTypes(const char *uri)
5563{
56− Q_ASSERT(uri == QLatin1String("Utils"));;
64+ Q_ASSERT(uri == QLatin1String("Utils"));
5765 qmlRegisterType<QAbstractItemModel>();
5866 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
5967 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
tests/plugins/CMakeLists.txt+1
@@ -2,6 +2,7 @@ add_subdirectory(AccountsService)
22add_subdirectory(Greeter)
33add_subdirectory(LightDM)
44add_subdirectory(Dash)
5+add_subdirectory(ScreenGrabber)
56add_subdirectory(Ubuntu)
67add_subdirectory(Unity)
78add_subdirectory(Utils)
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−2
@@ -1,5 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
32
43include_directories(
54 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+3
@@ -43,6 +43,7 @@ private Q_SLOTS:
4343 item.insert("name", "Item 1");
4444 item.insert("icon", "fake.svg");
4545 item.insert("count", 0);
46+ item.insert("progress", 0);
4647 item.insert("countVisible", false);
4748 item.insert("pinned", true);
4849 list.append(item);
@@ -67,6 +68,7 @@ private Q_SLOTS:
6768 inSync &= model->get(i)->name() == list.at(i).value("name").toString();
6869 inSync &= model->get(i)->icon() == list.at(i).value("icon").toString();
6970 inSync &= model->get(i)->count() == list.at(i).value("count").toInt();
71+ inSync &= model->get(i)->progress() == list.at(i).value("progress").toInt();
7072 inSync &= model->get(i)->countVisible() == list.at(i).value("countVisible").toBool();
7173 }
7274 return inSync;
@@ -111,6 +113,7 @@ private Q_SLOTS:
111113 newEntry.insert("name", "New app");
112114 newEntry.insert("icon", "some-icon.svg");
113115 newEntry.insert("count", 0);
116+ newEntry.insert("progress", 42);
114117 newEntry.insert("countVisible", false);
115118 newEntry.insert("pinned", true);
116119 newList.append(newEntry);
tests/plugins/ScreenGrabber/CMakeLists.txtnew file+14
@@ -0,0 +1,14 @@
1+include_directories(
2+ ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber
3+ ${CMAKE_CURRENT_BINARY_DIR}
4+ )
5+
6+add_definitions(
7+ -DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
8+ )
9+add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
10+
11+add_executable(ScreenGrabberTestExec ScreenGrabberTest.cpp ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber/screengrabber.cpp)
12+qt5_use_modules(ScreenGrabberTestExec Test Quick)
13+target_link_libraries(ScreenGrabberTestExec ${Qt5Quick_LIBRARIES} ScreenGrabber-qml)
14+add_unity8_uitest(ScreenGrabber ScreenGrabberTestExec DEPENDS ScreenGrabber-qml)
tests/plugins/ScreenGrabber/ScreenGrabberTest.cppnew file+65
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright (C) 2015 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 "screengrabber.h"
18+
19+#include <QtTest>
20+#include <QtQuick/QQuickView>
21+#include <QtQuick/QQuickItem>
22+#include <QQmlEngine>
23+
24+class ScreenGrabberTest: public QObject
25+{
26+ Q_OBJECT
27+
28+private Q_SLOTS:
29+
30+ void initTestCase()
31+ {
32+ m_view = new QQuickView();
33+ m_view->engine()->addImportPath(BUILT_PLUGINS_DIR);
34+ m_view->setSource(QUrl::fromLocalFile(CURRENT_SOURCE_DIR "/grabber.qml"));
35+ m_grabber = dynamic_cast<ScreenGrabber*>(m_view->rootObject()->property("grabber").value<QObject*>());
36+ QVERIFY(m_grabber);
37+ m_view->show();
38+ QTest::qWaitForWindowExposed(m_view);
39+ }
40+
41+ void cleanupTestCase()
42+ {
43+ delete m_view;
44+ m_view = nullptr;
45+ QTRY_COMPARE(QThreadPool::globalInstance()->activeThreadCount(), 0);
46+ }
47+
48+ void testGrabScreenshot()
49+ {
50+ QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
51+ m_grabber->captureAndSave();
52+ QTRY_VERIFY(grabberSpy.count() == 1);
53+ const QVariantList args = grabberSpy.takeFirst();
54+ QVERIFY(args.count() == 1); // verify we got a non-empty filename where the screenshot has been saved
55+ QVERIFY(!args.first().toString().isEmpty());
56+ }
57+
58+private:
59+ QQuickView *m_view;
60+ ScreenGrabber *m_grabber = nullptr;
61+};
62+
63+QTEST_MAIN(ScreenGrabberTest)
64+
65+#include "ScreenGrabberTest.moc"
tests/plugins/ScreenGrabber/grabber.qmlnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright 2015 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.4
18+import ScreenGrabber 0.1
19+
20+Rectangle {
21+ property var grabber: screenGrabber
22+
23+ width: 100
24+ height: 100
25+ color: "green"
26+
27+ ScreenGrabber {
28+ id: screenGrabber
29+ objectName: "screenGrabber"
30+ }
31+}
tests/plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
11pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
33
44include_directories(
55 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/Unity/Launcher/launchermodeltest.cpp+83−11
@@ -39,6 +39,9 @@ class MockApp: public unity::shell::application::ApplicationInfoInterface
3939 Q_OBJECT
4040public:
4141 MockApp(const QString &appId, QObject *parent = 0): ApplicationInfoInterface(appId, parent), m_appId(appId), m_focused(false) { }
42+
43+ RequestedState requestedState() const override { return RequestedRunning; }
44+ void setRequestedState(RequestedState) override {}
4245 QString appId() const override { return m_appId; }
4346 QString name() const override { return "mock"; }
4447 QString comment() const override { return "this is a mock"; }
@@ -68,6 +71,7 @@ class MockAppManager: public unity::shell::application::ApplicationManagerInterf
6871 Q_OBJECT
6972public:
7073 MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
74+ ~MockAppManager() {}
7175 int rowCount(const QModelIndex &) const override { return m_list.count(); }
7276 QVariant data(const QModelIndex &, int ) const override { return QVariant(); }
7377 QString focusedApplicationId() const override {
@@ -116,10 +120,6 @@ public:
116120 endRemoveRows();
117121 }
118122 bool requestFocusApplication(const QString &appId) override { Q_UNUSED(appId); return true; }
119− bool suspended() const override { return false; }
120− void setSuspended(bool) override {}
121− bool forceDashActive() const override { return false; }
122− void setForceDashActive(bool) override {}
123123
124124private:
125125 QList<MockApp*> m_list;
@@ -298,6 +298,22 @@ private Q_SLOTS:
298298 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
299299 }
300300
301+ void testApplicationRunning() {
302+ launcherModel->pin("abs-icon");
303+ launcherModel->pin("no-icon");
304+
305+ QCOMPARE(launcherModel->get(0)->running(), true);
306+ QCOMPARE(launcherModel->get(1)->running(), true);
307+
308+ appManager->stopApplication("abs-icon");
309+ QCOMPARE(launcherModel->get(0)->running(), false);
310+ QCOMPARE(launcherModel->get(1)->running(), true);
311+
312+ appManager->stopApplication("no-icon");
313+ QCOMPARE(launcherModel->get(0)->running(), false);
314+ QCOMPARE(launcherModel->get(1)->running(), false);
315+ }
316+
301317 void testApplicationFocused() {
302318 // all apps unfocused at beginning...
303319 QCOMPARE(launcherModel->get(0)->focused(), false);
@@ -325,6 +341,46 @@ private Q_SLOTS:
325341 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
326342 }
327343
344+ void testQuitMenuItem() {
345+ // we have 2 apps running, both should have the Quit action in its quick list
346+ QCOMPARE(launcherModel->rowCount(), 2);
347+
348+ // stop the second one keeping it pinned so that it doesn't go away
349+ launcherModel->pin("no-icon");
350+ appManager->stopApplication("no-icon");
351+
352+ // find the first Quit item, should be there
353+ QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
354+ int quitActionIndex = -1;
355+ for (int i = 0; i < model->rowCount(); ++i) {
356+ if (model->get(i).actionId() == "stop_item") {
357+ quitActionIndex = i;
358+ break;
359+ }
360+ }
361+ QVERIFY(quitActionIndex >= 0);
362+
363+ // find the second Quit item, should NOT be there, the app is stopped
364+ QuickListModel *model2 = qobject_cast<QuickListModel*>(launcherModel->get(1)->quickList());
365+ int quitActionIndex2 = -1;
366+ for (int i = 0; i < model2->rowCount(); ++i) {
367+ if (model2->get(i).actionId() == "stop_item") {
368+ quitActionIndex2 = i;
369+ break;
370+ }
371+ }
372+ QVERIFY(quitActionIndex2 == -1);
373+
374+ // trigger the first quit item quicklist action
375+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), quitActionIndex);
376+ // first app should be gone...
377+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
378+ // ... the second app (now at index 0) should still be there, pinned and stopped
379+ QCOMPARE(launcherModel->get(0)->appId(), QStringLiteral("no-icon"));
380+ QCOMPARE(launcherModel->get(0)->pinned(), true);
381+ QCOMPARE(launcherModel->get(0)->running(), false);
382+ }
383+
328384 void testGetUrlForAppId() {
329385 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
330386 QCOMPARE(launcherModel->getUrlForAppId(""), QString());
@@ -368,11 +424,18 @@ private Q_SLOTS:
368424 }
369425
370426 void testCountEmblems() {
427+ QSignalSpy spy(launcherModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
428+
371429 // Call GetAll on abs-icon
372430 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
373431 QDBusReply<QVariantMap> reply = interface.call("GetAll");
374432 QVariantMap map = reply.value();
375433
434+ // Check that the alerting-status is still false, and the item on the upper side of the API
435+ int index = launcherModel->findApplication("abs-icon");
436+ QCOMPARE(index >= 0, true);
437+ QVERIFY(launcherModel->get(index)->alerting() == false);
438+
376439 // Make sure GetAll returns a map with count and countVisible props
377440 QCOMPARE(map.contains("count"), true);
378441 QCOMPARE(map.contains("countVisible"), true);
@@ -393,13 +456,8 @@ private Q_SLOTS:
393456 QCOMPARE(map.value("count").toInt(), 55);
394457 QCOMPARE(map.value("countVisible").toBool(), true);
395458
396− // Now the item on the upper side of the API
397− int index = launcherModel->findApplication("abs-icon");
398− QCOMPARE(index >= 0, true);
399−
400− // And make sure values have changed there as well
401− QCOMPARE(launcherModel->get(index)->countVisible(), true);
402− QCOMPARE(launcherModel->get(index)->count(), 55);
459+ // Finally check, that the change to "count" implicitly also set the alerting-state to true
460+ QVERIFY(launcherModel->get(index)->alerting() == true);
403461 }
404462
405463 void testCountEmblemAddsRemovesItem_data() {
@@ -459,6 +517,20 @@ private Q_SLOTS:
459517 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
460518 }
461519
520+ void testAlert() {
521+ // Check that the alerting-status is still false
522+ int index = launcherModel->findApplication("abs-icon");
523+ QCOMPARE(index >= 0, true);
524+ QVERIFY(launcherModel->get(index)->alerting() == false);
525+
526+ // Call Alert() on "abs-icon"
527+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "com.canonical.Unity.Launcher.Item");
528+ interface.call("Alert");
529+
530+ // Check that the alerting-status is now true
531+ QVERIFY(launcherModel->get(index)->alerting() == true);
532+ }
533+
462534 void testRefreshAfterDeletedDesktopFiles_data() {
463535 QTest::addColumn<bool>("deleted");
464536 QTest::newRow("have .desktop files") << false;
tests/plugins/Unity/Session/sessionbackendtest.cpp+54−8
@@ -24,6 +24,11 @@
2424#include <QDBusInterface>
2525#include <QDBusReply>
2626#include <QDBusVariant>
27+#include <QDebug>
28+#include <QDBusObjectPath>
29+
30+#include <unistd.h>
31+#include <sys/types.h>
2732
2833#include "dbusunitysessionservice.h"
2934
@@ -52,9 +57,10 @@ private Q_SLOTS:
5257 QTest::addColumn<QString>("method");
5358 QTest::addColumn<QString>("signal");
5459
55− QTest::newRow("Logout") << "RequestLogout" << "logoutRequested(bool)";
56− QTest::newRow("Reboot") << "RequestReboot" << "rebootRequested(bool)";
57− QTest::newRow("Shutdown") << "RequestShutdown" << "shutdownRequested(bool)";
60+ QTest::newRow("Logout") << "RequestLogout" << "LogoutRequested(bool)";
61+ QTest::newRow("Reboot") << "RequestReboot" << "RebootRequested(bool)";
62+ QTest::newRow("Shutdown") << "RequestShutdown" << "ShutdownRequested(bool)";
63+ QTest::newRow("PromptLock") << "PromptLock" << "LockRequested()";
5864 }
5965
6066 void testUnitySessionLogoutRequested() {
@@ -65,7 +71,7 @@ private Q_SLOTS:
6571 QCoreApplication::processEvents(); // to let the service register on DBus
6672
6773 // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
68− QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
74+ QSignalSpy spy(&dbusUnitySessionService, qPrintable(signal.prepend(QSIGNAL_CODE)));
6975
7076 QDBusReply<void> reply = dbusUnitySession->call(method);
7177 QCOMPARE(reply.isValid(), true);
@@ -77,9 +83,9 @@ private Q_SLOTS:
7783 QTest::addColumn<uint>("method");
7884 QTest::addColumn<QString>("signal");
7985
80− QTest::newRow("Logout") << (uint)Action::LOGOUT << "logoutRequested(bool)";
81− QTest::newRow("Shutdown") << (unsigned)Action::SHUTDOWN << "shutdownRequested(bool)";
82− QTest::newRow("Reboot") << (unsigned)Action::REBOOT << "rebootRequested(bool)";
86+ QTest::newRow("Logout") << (uint)Action::LOGOUT << "LogoutRequested(bool)";
87+ QTest::newRow("Shutdown") << (uint)Action::SHUTDOWN << "ShutdownRequested(bool)";
88+ QTest::newRow("Reboot") << (uint)Action::REBOOT << "RebootRequested(bool)";
8389 }
8490
8591 void testGnomeSessionWrapper() {
@@ -92,7 +98,7 @@ private Q_SLOTS:
9298 // Spy on the given signal on the /com/canonical/Unity/Session object
9399 // as proof we are actually calling the actual method.
94100 // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
95− QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
101+ QSignalSpy spy(&dbusUnitySessionService, qPrintable(signal.prepend(QSIGNAL_CODE)));
96102
97103 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
98104 QCoreApplication::processEvents(); // to let the service register on DBus
@@ -116,6 +122,46 @@ private Q_SLOTS:
116122 QCOMPARE(spy.count(), 1);
117123 }
118124
125+ void testUserName() {
126+ DBusUnitySessionService dbusUnitySessionService;
127+ QCoreApplication::processEvents(); // to let the service register on DBus
128+
129+ QProcess * proc = new QProcess(this);
130+ proc->start("id -un", QProcess::ReadOnly);
131+ proc->waitForFinished();
132+ const QByteArray out = proc->readAll().trimmed();
133+
134+ QCOMPARE(dbusUnitySessionService.UserName(), QString::fromUtf8(out));
135+ }
136+
137+ void testRealName() {
138+ DBusUnitySessionService dbusUnitySessionService;
139+ QCoreApplication::processEvents(); // to let the service register on DBus
140+
141+ QDBusInterface accIface("org.freedesktop.Accounts", "/org/freedesktop/Accounts", "org.freedesktop.Accounts", QDBusConnection::systemBus());
142+ if (accIface.isValid()) {
143+ QDBusReply<QDBusObjectPath> userPath = accIface.asyncCall("FindUserById", static_cast<qint64>(geteuid()));
144+ if (userPath.isValid()) {
145+ QDBusInterface userAccIface("org.freedesktop.Accounts", userPath.value().path(), "org.freedesktop.Accounts.User", QDBusConnection::systemBus());
146+ QCOMPARE(dbusUnitySessionService.RealName(), userAccIface.property("RealName").toString());
147+ }
148+ }
149+ }
150+
151+ void testLogin1Capabilities() {
152+ DBusUnitySessionService dbusUnitySessionService;
153+ QDBusInterface login1face("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", QDBusConnection::systemBus());
154+ QCoreApplication::processEvents(); // to let the services register on DBus
155+
156+ if (login1face.isValid()) {
157+ QCOMPARE(dbusUnitySessionService.CanHibernate(), (login1face.call("CanHibernate").arguments().first().toString() != "no"));
158+ QCOMPARE(dbusUnitySessionService.CanSuspend(), (login1face.call("CanSuspend").arguments().first().toString() != "no"));
159+ QCOMPARE(dbusUnitySessionService.CanReboot(), (login1face.call("CanReboot").arguments().first().toString() != "no"));
160+ QCOMPARE(dbusUnitySessionService.CanShutdown(), (login1face.call("CanPowerOff").arguments().first().toString() != "no"));
161+ QCOMPARE(dbusUnitySessionService.CanHybridSleep(), (login1face.call("CanHybridSleep").arguments().first().toString() != "no"));
162+ }
163+ }
164+
119165private:
120166 QDBusInterface *dbusUnitySession;
121167};
tests/qmltests/Components/tst_PhysicalKeysMapper.qml+5
@@ -89,6 +89,11 @@ TestCase {
8989 compare(powerSpy.count, 1);
9090 }
9191
92+ function test_screenshotPrtScr() {
93+ physicalKeysMapper.onKeyPressed({ key: Qt.Key_Print })
94+ tryCompare(screenshotSpy, "count", 1) // verify the screenshotTriggered signal has been called, exactly once
95+ }
96+
9297 function test_screenshotButtons_data() {
9398 return [
9499 { tag: "UpFirst", first: Qt.Key_VolumeUp, second: Qt.Key_VolumeDown },
tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+146−6
@@ -34,10 +34,86 @@ Rectangle {
3434 "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
3535 }
3636
37+ property var actionsData: {
38+ "actions": [ {"label": "Some Label", "id": "someid"} ]
39+ }
40+
41+ property var audioData: {
42+ "tracks": [ { title: "Some track name", length: "30", source: "/not/existing/path/testsound1" } ]
43+ }
44+
45+ property var commentData: {
46+ "author": "Claire Thompson",
47+ "comment": "C.",
48+ "source": "../../graphics/avatars/amanda@12.png"
49+ }
50+
51+ property var commentInputData: {
52+ "submit-label": "TestSubmitLabel"
53+ }
54+
55+ property var galleryData: {
56+ "sources": [
57+ "../../graphics/phone_background.jpg",
58+ "../../graphics/tablet_background.jpg",
59+ "../../graphics/clock@18.png",
60+ "../../graphics/borked"
61+ ]
62+ }
63+
64+ property var headerData: {
65+ "title": "THE TITLE",
66+ "subtitle": "Something catchy"
67+ }
68+
69+ property var iconActionsData: {
70+ "actions": [ {"label": "10", "id": "s", "icon": Qt.resolvedUrl("../artwork/avatar@12.png") } ]
71+ }
72+
73+ property var imageData: {
74+ "source": "../../graphics/phone_background.jpg",
75+ "zoomable": false
76+ }
77+
78+ property var paymentsData: {
79+ "source": { "price" : 0.99, "currency": "USD", "store_item_id": "com.example.package" }
80+ }
81+
82+ property var progressData: {
83+ "type": "progress",
84+ "source": { "dbus-name" : "somename", "dbus-object": "somestring" }
85+ }
86+
87+ property var ratingInputData: {
88+ "visible": "both",
89+ "required": "both"
90+ }
91+
92+ property var ratingEditData: {
93+ "visible": "both",
94+ "required": "both",
95+ author: "Some dude",
96+ rating: 4.5,
97+ review: "Very cool app"
98+ }
99+
100+ property var reviewsData: {
101+ "reviews": [ { author: "Some dude", rating: 4.5, review: "Very cool app" } ]
102+ }
103+
104+ property var videoData: {
105+ "source": "",
106+ "screenshot": "../../../tests/qmltests/Components/tst_LazyImage/square.png"
107+ }
108+
37109 ListModel {
38110 id: widgetsModel
39111 }
40112
113+ ListModel {
114+ id: allWidgetsModel
115+ }
116+
41117 property var widgetData: {
42118 "title": "Title here",
43119 "collapsed-widgets": 2,
@@ -58,11 +134,35 @@ Rectangle {
58134 "expanded": false
59135 }
60136
137+ property var allWidgetsData: {
138+ "title": "Title here",
139+ "collapsed-widgets": 0,
140+ "widgets": allWidgetsModel
141+ }
142+
61143 Component.onCompleted: {
62144 widgetsModel.append({"type": "text", "widgetId": "text1", "properties": { "text": longText }});
63− widgetsModel.append({"type": "text", "widgetId": "table1", "properties": { "text": tableData }});
145+ widgetsModel.append({"type": "table", "widgetId": "table1", "properties": tableData });
64146 widgetsModel.append({"type": "text", "widgetId": "text3", "properties": { "text": shortText }});
65− widgetsModel.append({"type": "text", "widgetId": "text4", "properties": { "text": longText }});
147+ widgetsModel.append({"type": "text", "widgetId": "text4", "properties": { "text": longText2 }});
148+
149+ allWidgetsModel.append({"type": "actions", "widgetId": "actions1", "properties": actionsData });
150+ allWidgetsModel.append({"type": "audio", "widgetId": "audio1", "properties": audioData });
151+ allWidgetsModel.append({"type": "comment", "widgetId": "comment1", "properties": commentData });
152+ allWidgetsModel.append({"type": "comment-input", "widgetId": "comment-input1", "properties": commentInputData });
153+ // "expandable" For now we're not testing inception of expandables
154+ allWidgetsModel.append({"type": "gallery", "widgetId": "gallery1", "properties": galleryData });
155+ allWidgetsModel.append({"type": "header", "widgetId": "header1", "properties": headerData });
156+ allWidgetsModel.append({"type": "icon-actions", "widgetId": "icon-actions1", "properties": iconActionsData } );
157+ allWidgetsModel.append({"type": "image", "widgetId": "image1", "properties": imageData });
158+ allWidgetsModel.append({"type": "payments", "widgetId": "payments1", "properties": paymentsData });
159+ allWidgetsModel.append({"type": "progress", "widgetId": "progress1", "properties": progressData });
160+ allWidgetsModel.append({"type": "rating-input", "widgetId": "rating-input1", "properties": ratingInputData });
161+ allWidgetsModel.append({"type": "rating-edit", "widgetId": "rating-edit1", "properties": ratingEditData });
162+ allWidgetsModel.append({"type": "reviews", "widgetId": "reviews1", "properties": reviewsData });
163+ allWidgetsModel.append({"type": "table", "widgetId": "table1", "properties": tableData });
164+ allWidgetsModel.append({"type": "text", "widgetId": "text1", "properties": { "text": longText }});
165+ allWidgetsModel.append({"type": "video", "widgetId": "video1", "properties": videoData });
66166 }
67167
68168 PreviewWidgetFactory {
@@ -72,6 +172,12 @@ Rectangle {
72172 widgetData: root.widgetData
73173 }
74174
175+ PreviewWidgetFactory {
176+ id: previewWidgetFactory
177+ anchors { left: parent.left; right: parent.right; top: previewExpandable.bottom }
178+ opacity: 0
179+ }
180+
75181 UT.UnityTestCase {
76182 name: "PreviewExpandableTest"
77183 when: windowShown
@@ -92,13 +198,10 @@ Rectangle {
92198 }
93199
94200 function init() {
201+ previewExpandable.widgetData = widgetData;
95202 checkInitialState();
96203 }
97204
98− function test_collapsed_by_default() {
99− // Nothing init does this already
100− }
101−
102205 function test_expand_collapse() {
103206 var expandButton = findChild(previewExpandable, "expandButton")
104207 mouseClick(expandButton);
@@ -151,5 +254,42 @@ Rectangle {
151254 compare (repeater.itemAt(2).expanded, false);
152255 compare (repeater.itemAt(3).expanded, false);
153256 }
257+
258+ function test_all_widgets_height() {
259+ previewExpandable.widgetData = allWidgetsData;
260+
261+ var repeater = findChild(previewExpandable, "repeater")
262+ for (var i = 0; i < repeater.count; ++i) {
263+ tryCompare(repeater.itemAt(i), "height", 0);
264+ }
265+
266+ var expandButton = findChild(previewExpandable, "expandButton")
267+ mouseClick(expandButton);
268+
269+ var repeater = findChild(previewExpandable, "repeater")
270+ for (var i = 0; i < repeater.count; ++i) {
271+ previewWidgetFactory.active = false;
272+ wait(0); // spin the event loop otherwise we get warnings because the previous item from the
273+ // widget factory has still not been deleted and we change the widgetData
274+ previewWidgetFactory.widgetData = allWidgetsModel.get(i).properties;
275+ previewWidgetFactory.widgetType = allWidgetsModel.get(i).type;
276+ previewWidgetFactory.active = true;
277+
278+ // Wait for the height ot settle by waiting twice the time of the
279+ // longest of the height behaviour animations
280+ wait(UbuntuAnimation.SnapDuration * 2);
281+
282+ // Check the item inside the expandable has the same height
283+ // as the one straight from the factory
284+ verify(repeater.itemAt(i).height > 0);
285+ tryCompare(repeater.itemAt(i), "height", previewWidgetFactory.height);
286+ }
287+
288+ mouseClick(expandButton);
289+ compare(previewExpandable.expanded, false);
290+ for (var i = 0; i < repeater.count; ++i) {
291+ tryCompare(repeater.itemAt(i), "height", 0);
292+ }
293+ }
154294 }
155295}
tests/qmltests/Dash/tst_PageHeader.qml+16−12
@@ -33,13 +33,18 @@ Item {
3333
3434 property alias searchEnabled : pageHeader.searchEntryEnabled
3535 property alias searchQuery : pageHeader.searchQuery
36+ property var headerContainer: findChild(pageHeader, "headerContainer")
3637
3738 function doTypeString(text) {
38− var headerContainer = findChild(pageHeader, "headerContainer");
3939 tryCompare(headerContainer, "contentY", 0);
4040 typeString(text);
4141 }
4242
43+ function doResetSearch() {
44+ pageHeader.resetSearch();
45+ tryCompare(headerContainer, "contentY", headerContainer.height);
46+ }
47+
4348 function init() {
4449 searchEnabled = true;
4550
@@ -49,14 +54,13 @@ Item {
4954 pageHeader.searchHistory.clear();
5055
5156 // Check initial state
52− var headerContainer = findChild(pageHeader, "headerContainer");
5357 tryCompareFunction(function() { return headerContainer.popover === null; }, true);
5458 compare(pageHeader.searchHistory.count, 0);
5559 }
5660
5761 function test_search_disabled() {
5862 searchEnabled = false
59− pageHeader.resetSearch()
63+ doResetSearch();
6064
6165 pageHeader.triggerSearch()
6266 keyClick(Qt.Key_S)
@@ -66,7 +70,7 @@ Item {
6670
6771 function test_search_enable() {
6872 searchEnabled = true
69− pageHeader.resetSearch()
73+ doResetSearch();
7074
7175 pageHeader.triggerSearch()
7276 doTypeString("test")
@@ -84,42 +88,42 @@ Item {
8488
8589 function test_reset_search() {
8690 searchEnabled = true
87− pageHeader.resetSearch()
91+ doResetSearch();
8892
8993 pageHeader.triggerSearch()
9094 keyClick(Qt.Key_S)
9195
9296 compare(searchQuery, "s", "Could not type in TextField.")
9397
94− pageHeader.resetSearch()
98+ doResetSearch();
9599 compare(searchQuery, "", "Reset search did not reset searchQuery correctly.")
96100 }
97101
98102 function test_history() {
99103 pageHeader.triggerSearch()
100104 doTypeString("humppa1")
101− pageHeader.resetSearch()
105+ doResetSearch();
102106
103107 tryCompare(pageHeader.searchHistory, "count", 1)
104108 compare(pageHeader.searchHistory.get(0).query, "humppa1")
105109
106110 pageHeader.triggerSearch()
107111 doTypeString("humppa2")
108− pageHeader.resetSearch()
112+ doResetSearch();
109113
110114 compare(pageHeader.searchHistory.count, 2)
111115 compare(pageHeader.searchHistory.get(0).query, "humppa2")
112116
113117 pageHeader.triggerSearch()
114118 doTypeString("humppa3")
115− pageHeader.resetSearch()
119+ doResetSearch();
116120
117121 compare(pageHeader.searchHistory.count, 3)
118122 compare(pageHeader.searchHistory.get(0).query, "humppa3")
119123
120124 pageHeader.triggerSearch()
121125 doTypeString("humppa4")
122− pageHeader.resetSearch()
126+ doResetSearch();
123127
124128 compare(pageHeader.searchHistory.count, 3)
125129 compare(pageHeader.searchHistory.get(0).query, "humppa4")
@@ -139,7 +143,7 @@ Item {
139143 }
140144
141145 function cleanup() {
142− pageHeader.resetSearch();
146+ doResetSearch();
143147 }
144148
145149 function test_popover() {
@@ -223,7 +227,7 @@ Item {
223227 tryCompare(headerContainer, "showSearch", !data.hideSearch);
224228 tryCompareFunction(function() { return headerContainer.popover === null; }, true);
225229
226− pageHeader.resetSearch();
230+ doResetSearch();
227231 }
228232
229233 function test_search_change_shows_search() {
tests/qmltests/Launcher/tst_Launcher.qml+246−12
@@ -93,6 +93,57 @@ Item {
9393 onClicked: launcherLoader.item.inverted = !launcherLoader.item.inverted
9494 Layout.fillWidth: true
9595 }
96+
97+ Row {
98+ spacing: units.gu(1)
99+
100+ Button {
101+ text: "35% bar"
102+ onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntryBar.displayText)).appId, 35)
103+ Layout.fillWidth: true
104+ }
105+
106+ TextArea {
107+ id: appIdEntryBar
108+ anchors.verticalCenter: parent.verticalCenter
109+ width: units.gu(4)
110+ height: units.gu(4)
111+ autoSize: true
112+ text: "2"
113+ maximumLineCount: 1
114+ }
115+
116+ Button {
117+ text: "no bar"
118+ onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntryBar.displayText)).appId, -1)
119+ Layout.fillWidth: true
120+ }
121+ }
122+
123+ Row {
124+ spacing: units.gu(1)
125+
126+ Button {
127+ text: "set alert"
128+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntryAlert.displayText)).appId, true)
129+ }
130+
131+ TextArea {
132+ id: appIdEntryAlert
133+ anchors.verticalCenter: parent.verticalCenter
134+ width: units.gu(5)
135+ height: units.gu(4)
136+ autoSize: true
137+ text: "2"
138+ maximumLineCount: 1
139+ Layout.fillWidth: true
140+ }
141+
142+ Button {
143+ text: "unset alert"
144+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntryAlert.displayText)).appId, false)
145+ }
146+ }
96147 }
97148
98149 SignalSpy {
@@ -146,7 +197,7 @@ Item {
146197 startX+units.gu(8), startY);
147198
148199 var panel = findChild(launcher, "launcherPanel");
149− verify(panel != undefined);
200+ verify(!!panel);
150201
151202 // wait until it gets fully extended
152203 tryCompare(panel, "x", 0);
@@ -157,7 +208,7 @@ Item {
157208 mouseMove(root, 1, root.height / 2);
158209
159210 var panel = findChild(launcher, "launcherPanel");
160− verify(panel != undefined);
211+ verify(!!panel);
161212
162213 // wait until it gets fully extended
163214 tryCompare(panel, "x", 0);
@@ -169,6 +220,16 @@ Item {
169220 tryCompare(panel, "x", -panel.width, 1000);
170221 }
171222
223+ function waitForWiggleToStart(appIcon) {
224+ verify(appIcon != undefined)
225+ tryCompare(appIcon, "wiggling", true, 1000, "wiggle-anim should not be in stopped state")
226+ }
227+
228+ function waitForWiggleToStop(appIcon) {
229+ verify(appIcon != undefined)
230+ tryCompare(appIcon, "wiggling", false, 1000, "wiggle-anim should not be in running state")
231+ }
232+
172233 function positionLauncherListAtBeginning() {
173234 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
174235 listView.contentY = -listView.topMargin;
@@ -187,7 +248,7 @@ Item {
187248 waitUntilLauncherDisappears();
188249
189250 var panel = findChild(launcher, "launcherPanel")
190− verify(panel != undefined)
251+ verify(!!panel)
191252
192253 // it starts out hidden just left of the left launcher edge
193254 compare(panel.x, -panel.width)
@@ -225,7 +286,7 @@ Item {
225286
226287 var appIcon = findChild(launcher, "launcherDelegate0");
227288
228− verify(appIcon != undefined);
289+ verify(!!appIcon);
229290
230291 if (data.mouse) {
231292 mouseClick(appIcon);
@@ -248,7 +309,7 @@ Item {
248309 dragLauncherIntoView()
249310
250311 var dashIcon = findChild(launcher, "dashItem")
251− verify(dashIcon != undefined)
312+ verify(!!dashIcon)
252313
253314 mouseClick(dashIcon)
254315
@@ -321,6 +382,15 @@ Item {
321382 }
322383 }
323384
385+ function test_runningHighlight() {
386+ dragLauncherIntoView();
387+ var launcherListView = findChild(launcher, "launcherListView");
388+ for (var i = 0; i < launcherListView.count; ++i) {
389+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
390+ compare(findChild(delegate, "runningHighlight").visible, LauncherModel.get(i).running)
391+ }
392+ }
393+
324394 function test_focusedHighlight() {
325395 dragLauncherIntoView();
326396 var launcherListView = findChild(launcher, "launcherListView");
@@ -459,7 +529,14 @@ Item {
459529 waitUntilLauncherDisappears();
460530 }
461531
462− function test_dragndrop_cancel() {
532+ function test_dragndrop_cancel_data() {
533+ return [
534+ {tag: "by mouse", mouse: true},
535+ {tag: "by touch", mouse: false}
536+ ]
537+ }
538+
539+ function test_dragndrop_cancel(data) {
463540 dragLauncherIntoView();
464541 var draggedItem = findChild(launcher, "launcherDelegate4")
465542 var item0 = findChild(launcher, "launcherDelegate0")
@@ -468,22 +545,37 @@ Item {
468545 // Doing longpress
469546 var currentMouseX = draggedItem.width / 2
470547 var currentMouseY = draggedItem.height / 2
471− mousePress(draggedItem, currentMouseX, currentMouseY)
548+
549+ if(data.mouse) {
550+ mousePress(draggedItem, currentMouseX, currentMouseY)
551+ } else {
552+ touchPress(draggedItem, currentMouseX, currentMouseY)
553+ }
554+
472555 // DraggedItem needs to hide and fakeDragItem become visible
473556 tryCompare(draggedItem, "itemOpacity", 0)
474557 tryCompare(fakeDragItem, "visible", true)
475558
476559 // Dragging
477560 currentMouseX -= units.gu(20)
478− mouseMove(draggedItem, currentMouseX, currentMouseY)
561+
562+ if(data.mouse) {
563+ mouseMove(draggedItem, currentMouseX, currentMouseY)
564+ } else {
565+ touchMove(draggedItem, currentMouseX, currentMouseY)
566+ }
479567
480568 // Make sure we're in the dragging state
481569 var dndArea = findChild(launcher, "dndArea");
482570 tryCompare(draggedItem, "dragging", true)
483571 tryCompare(dndArea, "draggedIndex", 4)
484572
485− // Tap somewhere in the middle of the screen to close/hide the launcher
486− tap(root)
573+ // Click/Tap somewhere in the middle of the screen to close/hide the launcher
574+ if(data.mouse) {
575+ mouseClick(root)
576+ } else {
577+ tap(root)
578+ }
487579
488580 // Make sure the dnd operation has been stopped
489581 tryCompare(draggedItem, "dragging", false)
@@ -538,7 +630,7 @@ Item {
538630
539631 // Doing longpress
540632 mousePress(draggedItem);
541− tryCompare(quickListShape, "opacity", 0.96);
633+ tryCompare(quickListShape, "opacity", 0.8);
542634 mouseRelease(draggedItem);
543635
544636 verify(quickList.y >= units.gu(1));
@@ -623,9 +715,31 @@ Item {
623715 tryCompare(quickList, "state", "");
624716 }
625717
718+ function test_quickListMenuOnRMB() {
719+ dragLauncherIntoView();
720+ var clickedItem = findChild(launcher, "launcherDelegate5")
721+ var quickList = findChild(launcher, "quickList")
722+ var quickListShape = findChild(launcher, "quickListShape")
723+ var dndArea = findChild(launcher, "dndArea");
724+
725+ // Initial state
726+ tryCompare(quickListShape, "visible", false)
727+
728+ // Doing RMB click
729+ mouseClick(clickedItem, clickedItem.width / 2, clickedItem.height / 2, Qt.RightButton)
730+ tryCompare(quickListShape, "visible", true)
731+ verify(quickList, "state", "open")
732+ verify(dndArea, "dragging", false)
733+
734+ // Click somewhere in the empty space to dismiss the quicklist
735+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
736+ tryCompare(quickListShape, "visible", false);
737+ verify(quickList, "state", "")
738+ }
739+
626740 function test_revealByHover() {
627741 var panel = findChild(launcher, "launcherPanel");
628− verify(panel != undefined);
742+ verify(!!panel);
629743
630744 revealByHover();
631745 tryCompare(launcher, "state", "visibleTemporary");
@@ -636,5 +750,125 @@ Item {
636750 tryCompare(launcher, "state", "", 1000, "Launcher didn't hide after moving mouse away from it");
637751 waitUntilLauncherDisappears();
638752 }
753+
754+ function test_progressChangeViaModel() {
755+ dragLauncherIntoView();
756+ var item = findChild(launcher, "launcherDelegate0")
757+ verify(item != undefined)
758+ LauncherModel.setProgress(LauncherModel.get(0).appId, -1)
759+ compare(findChild(item, "progressOverlay").visible, false)
760+ LauncherModel.setProgress(LauncherModel.get(0).appId, 20)
761+ compare(findChild(item, "progressOverlay").visible, true)
762+ LauncherModel.setProgress(LauncherModel.get(0).appId, 0)
763+ }
764+
765+ function test_alertPeekingIcon() {
766+ var listView = findChild(launcher, "launcherListView")
767+ verify(listView != undefined)
768+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
769+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
770+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
771+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
772+ }
773+
774+ function test_alertHidingIcon() {
775+ var listView = findChild(launcher, "launcherListView")
776+ verify(listView != undefined)
777+ var appIcon6 = findChild(launcher, "launcherDelegate6")
778+ verify(appIcon6 != undefined)
779+ LauncherModel.setAlerting(LauncherModel.get(6).appId, true)
780+ waitForWiggleToStart(appIcon6)
781+ LauncherModel.setAlerting(LauncherModel.get(6).appId, false)
782+ waitForWiggleToStop(appIcon6)
783+ tryCompare(appIcon6, "x", 0, 1000, "x-value of appId #6 should not be non-zero")
784+ waitForRendering(listView)
785+ }
786+
787+ function test_alertIgnoreFocusedApp() {
788+ LauncherModel.setAlerting(LauncherModel.get(0).appId, true)
789+ compare(LauncherModel.get(0).alerting, false, "Focused app should not have the alert-state set")
790+ }
791+
792+ function test_alertOnlyOnePeekingIcon() {
793+ var listView = findChild(launcher, "launcherListView")
794+ verify(listView != undefined)
795+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
796+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
797+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
798+ tryCompare(listView, "peekingIndex", 3, 1000, "Wrong appId set as peeking-index")
799+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
800+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
801+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
802+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
803+ waitForRendering(listView)
804+ }
805+
806+ function test_alertMultipleApps() {
807+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
808+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
809+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
810+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
811+ compare(LauncherModel.get(1).alerting, true, "Alert-state of appId #1 should not be false")
812+ compare(LauncherModel.get(3).alerting, true, "Alert-state of appId #3 should not be false")
813+ compare(LauncherModel.get(5).alerting, true, "Alert-state of appId #5 should not be false")
814+ compare(LauncherModel.get(7).alerting, true, "Alert-state of appId #7 should not be false")
815+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
816+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
817+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
818+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
819+ compare(LauncherModel.get(1).alerting, false, "Alert-state of appId #1 should not be true")
820+ compare(LauncherModel.get(3).alerting, false, "Alert-state of appId #1 should not be true")
821+ compare(LauncherModel.get(5).alerting, false, "Alert-state of appId #1 should not be true")
822+ compare(LauncherModel.get(7).alerting, false, "Alert-state of appId #1 should not be true")
823+ }
824+
825+ function test_alertMoveIconIntoView() {
826+ dragLauncherIntoView();
827+ var appIcon1 = findChild(launcher, "launcherDelegate1");
828+ var appIcon7 = findChild(launcher, "launcherDelegate7");
829+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
830+ tryCompare(appIcon1, "angle", 0, 1000, "angle of appId #1 should not be non-zero")
831+ waitForWiggleToStart(appIcon1)
832+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
833+ tryCompare(appIcon7, "angle", 0, 1000, "angle of appId #7 should not be non-zero")
834+ waitForWiggleToStart(appIcon7)
835+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
836+ waitForWiggleToStop(appIcon1)
837+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
838+ waitForWiggleToStop(appIcon7)
839+ }
840+
841+ function test_alertWigglePeekDrag() {
842+ var appIcon5 = findChild(launcher, "launcherDelegate5");
843+ var listView = findChild(launcher, "launcherListView")
844+ verify(listView != undefined)
845+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
846+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
847+ waitForWiggleToStart(appIcon5)
848+ tryCompare(appIcon5, "wiggling", true, 1000, "appId #6 should not be still")
849+ dragLauncherIntoView();
850+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
851+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
852+ waitForWiggleToStop(appIcon5)
853+ tryCompare(appIcon5, "wiggling", false, 1000, "appId #1 should not be wiggling")
854+ }
855+
856+ function test_alertViaCountAndCountVisible() {
857+ dragLauncherIntoView();
858+ var appIcon1 = findChild(launcher, "launcherDelegate1")
859+ var oldCount = LauncherModel.get(1).count
860+ LauncherModel.setCount(LauncherModel.get(1).appId, 42)
861+ tryCompare(appIcon1, "wiggling", false, 1000, "appId #1 should be still")
862+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 1)
863+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
864+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
865+ waitForWiggleToStop(appIcon1)
866+ LauncherModel.setCount(LauncherModel.get(1).appId, 4711)
867+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
868+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
869+ waitForWiggleToStop(appIcon1)
870+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 0)
871+ LauncherModel.setCount(LauncherModel.get(1).appId, oldCount)
872+ }
639873 }
640874}
tests/qmltests/Panel/tst_IndicatorItem.qml+12−12
@@ -181,25 +181,25 @@ Rectangle {
181181 }
182182
183183 function test_select() {
184− tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
184+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ffffff");
185185 tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
186− tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
187− tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
188− tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
186+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ffffff");
187+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ffffff");
188+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ffffff");
189189
190190 indicatorItem.expanded = true;
191− tryCompare(findChild(indicatorItem, "icon0"), "color", "#4c4c4c");
191+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#888888");
192192 tryCompare(findChild(indicatorItem, "icon0"), "opacity", 0.6);
193− tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#4c4c4c");
194− tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#4c4c4c");
195− tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#4c4c4c");
193+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#888888");
194+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#888888");
195+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#888888");
196196
197197 indicatorItem.selected = true;
198− tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
198+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ffffff");
199199 tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
200− tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
201− tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
202− tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
200+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ffffff");
201+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ffffff");
202+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ffffff");
203203 }
204204 }
205205}
tests/qmltests/Panel/tst_Panel.qml+18
@@ -19,6 +19,7 @@ import QtQuick.Layouts 1.1
1919import QtTest 1.0
2020import Unity.Test 0.1
2121import Ubuntu.Components 0.1
22+import Unity.Application 0.1
2223import Unity.Indicators 0.1 as Indicators
2324import Ubuntu.Telephony 0.1 as Telephony
2425import "../../../qml/Panel"
@@ -421,6 +422,23 @@ IndicatorTest {
421422 verify(panel.indicators.fullyClosed);
422423 }
423424
425+ function test_tapToReturnCallDoesntExpandIndicators() {
426+ compare(panel.indicators.shown, false);
427+ verify(panel.indicators.fullyClosed);
428+
429+ callManager.foregroundCall = phoneCall;
430+
431+ ApplicationManager.focusApplication("unity8-dash");
432+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
433+
434+ mouseClick(panel.indicators,
435+ panel.indicators.width / 2,
436+ panel.indicators.minimizedPanelHeight / 2);
437+
438+ compare(panel.indicators.shown, false);
439+ verify(panel.indicators.fullyClosed);
440+ }
441+
424442 function test_openAndClosePanelWithMouseClicks() {
425443 compare(panel.indicators.shown, false);
426444 verify(panel.indicators.fullyClosed);
tests/qmltests/Stages/ApplicationCheckBox.qml+21
@@ -86,8 +86,29 @@ RowLayout {
8686 }
8787 }
8888 Label {
89+ id: appIdLabel
8990 text: root.appId
9091 color: "white"
9192 anchors.verticalCenter: parent.verticalCenter
9293 }
94+ Rectangle {
95+ color: {
96+ if (d.application) {
97+ if (d.application.state === ApplicationInfoInterface.Starting) {
98+ return "yellow";
99+ } else if (d.application.state === ApplicationInfoInterface.Running) {
100+ return "green";
101+ } else if (d.application.state === ApplicationInfoInterface.Suspended) {
102+ return "blue";
103+ } else {
104+ return "darkred";
105+ }
106+ } else {
107+ return "darkred";
108+ }
109+ }
110+ width: height
111+ height: appIdLabel.height * 0.7
112+ anchors.verticalCenter: parent.verticalCenter
113+ }
93114}
tests/qmltests/Stages/tst_DesktopStage.qml+2−2
@@ -24,9 +24,8 @@ import Utils 0.1
2424
2525import "../../../qml/Stages"
2626
27−Rectangle {
27+Item {
2828 id: root
29− color: "darkblue"
3029 width: desktopStageLoader.width + controls.width
3130 height: desktopStageLoader.height
3231
@@ -57,6 +56,7 @@ Rectangle {
5756 property bool itemDestroyed: false
5857 sourceComponent: Component {
5958 DesktopStage {
59+ color: "darkblue"
6060 anchors.fill: parent
6161 Component.onDestruction: {
6262 desktopStageLoader.itemDestroyed = true;
tests/qmltests/Stages/tst_PhoneStage.qml+76−3
@@ -295,22 +295,23 @@ Item {
295295 tryCompare(ApplicationManager, "count", oldCount - 1)
296296 }
297297 phoneStage.shellOrientationAngle = 0;
298+ phoneStage.select(ApplicationManager.get(0).appId);
298299 }
299300
300301 function test_focusNewTopMostAppAfterFocusedOneClosesItself() {
301302 addApps(2);
302303
303304 var secondApp = ApplicationManager.get(0);
304− tryCompare(secondApp, "state", ApplicationInfoInterface.Running);
305+ tryCompare(secondApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
305306 tryCompare(secondApp, "focused", true);
306307
307308 var firstApp = ApplicationManager.get(1);
308− tryCompare(firstApp, "state", ApplicationInfoInterface.Suspended);
309+ tryCompare(firstApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
309310 tryCompare(firstApp, "focused", false);
310311
311312 ApplicationManager.stopApplication(secondApp.appId);
312313
313− tryCompare(firstApp, "state", ApplicationInfoInterface.Running);
314+ tryCompare(firstApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
314315 tryCompare(firstApp, "focused", true);
315316 }
316317
@@ -388,5 +389,77 @@ Item {
388389
389390 tryCompare(focusedDelegate, "x", 0);
390391 }
392+
393+ function test_focusedAppIsTheOnlyRunningApp() {
394+ addApps(2);
395+
396+ var delegateA = findChild(phoneStage, "appDelegate0");
397+ verify(delegateA);
398+ var delegateB = findChild(phoneStage, "appDelegate1");
399+ verify(delegateB);
400+
401+ // A is focused and running, B is unfocused and suspended
402+ compare(delegateA.focus, true);
403+ compare(delegateA.application.requestedState, ApplicationInfoInterface.RequestedRunning);
404+ compare(delegateB.focus, false);
405+ compare(delegateB.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
406+
407+ // Switch foreground/focused appp from A to B
408+ goToSpread();
409+ phoneStage.select(delegateB.application.appId);
410+
411+ // Now it's the other way round
412+ // A is unfocused and suspended, B is focused and running
413+ tryCompare(delegateA, "focus", false);
414+ tryCompare(delegateA.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
415+ tryCompare(delegateB, "focus", true);
416+ tryCompare(delegateB.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
417+ }
418+
419+ function test_dashRemainsRunningIfStageIsToldSo() {
420+ addApps(1);
421+
422+ var delegateDash = findChild(phoneStage, "appDelegate1");
423+ verify(delegateDash);
424+ compare(delegateDash.application.appId, "unity8-dash");
425+
426+ var delegateOther = findChild(phoneStage, "appDelegate0");
427+ verify(delegateOther);
428+
429+ goToSpread();
430+ phoneStage.select("unity8-dash");
431+
432+ tryCompare(delegateDash, "focus", true);
433+ tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
434+ compare(delegateOther.focus, false);
435+ compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
436+
437+ goToSpread();
438+ phoneStage.select(delegateOther.application.appId);
439+
440+ // The other app gets focused and running but dash is kept running despite being unfocused
441+ tryCompare(delegateDash, "focus", false);
442+ tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
443+ compare(delegateOther.focus, true);
444+ compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedRunning);
445+ }
446+
447+ function test_foregroundAppIsSuspendedWhenStageIsSuspended() {
448+ addApps(1);
449+
450+ var delegate = findChild(phoneStage, "appDelegate0");
451+ verify(delegate);
452+
453+ compare(delegate.focus, true);
454+ compare(delegate.application.requestedState, ApplicationInfoInterface.RequestedRunning);
455+
456+ phoneStage.suspended = true;
457+
458+ tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
459+
460+ phoneStage.suspended = false;
461+
462+ tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
463+ }
391464 }
392465}
tests/qmltests/Stages/tst_TabletStage.qml+161−1
@@ -76,10 +76,18 @@ Rectangle {
7676 id: webbrowserCheckBox
7777 appId: "webbrowser-app"
7878 }
79+ ApplicationCheckBox {
80+ id: galleryCheckBox
81+ appId: "gallery-app"
82+ }
7983 ApplicationCheckBox {
8084 id: dialerCheckBox
8185 appId: "dialer-app"
8286 }
87+ ApplicationCheckBox {
88+ id: facebookCheckBox
89+ appId: "facebook-webapp"
90+ }
8391 }
8492 }
8593
@@ -93,6 +101,19 @@ Rectangle {
93101 function init() {
94102 tabletStageLoader.active = true;
95103 tryCompare(tabletStageLoader, "status", Loader.Ready);
104+
105+ // this is very strange, but sometimes the test starts without
106+ // TabletStage components having finished loading themselves
107+ var appWindow = null;
108+ while (!appWindow) {
109+ appWindow = findChild(tabletStage, "appWindow_unity8-dash");
110+ if (!appWindow) {
111+ console.log("didn't find appWindow_unity8-dash in " + tabletStage + ". Trying again...");
112+ wait(50);
113+ }
114+ }
115+
116+ waitUntilAppSurfaceShowsUp("unity8-dash");
96117 }
97118
98119 function cleanup() {
@@ -109,7 +130,9 @@ Rectangle {
109130
110131 // kill all (fake) running apps
111132 webbrowserCheckBox.checked = false;
133+ galleryCheckBox.checked = false;
112134 dialerCheckBox.checked = false;
135+ facebookCheckBox.checked = false;
113136 }
114137
115138 function waitUntilAppSurfaceShowsUp(appId) {
@@ -120,6 +143,34 @@ Rectangle {
120143 tryCompare(appWindowStates, "state", "surface");
121144 }
122145
146+ function switchToApp(targetAppId) {
147+ touchFlick(tabletStage,
148+ tabletStage.width - (tabletStage.dragAreaWidth / 2), tabletStage.height / 2,
149+ tabletStage.x + 1, tabletStage.height / 2);
150+
151+ var spreadView = findChild(tabletStage, "spreadView");
152+ verify(spreadView);
153+ tryCompare(spreadView, "phase", 2);
154+ tryCompare(spreadView, "flicking", false);
155+ tryCompare(spreadView, "moving", false);
156+
157+ waitUntilAppDelegateStopsMoving(targetAppId);
158+
159+ var targetAppWindow = findChild(tabletStage, "appWindow_" + targetAppId);
160+ tap(targetAppWindow, 10, 10);
161+ }
162+
163+ function waitUntilAppDelegateStopsMoving(targetAppId)
164+ {
165+ var targetAppDelegate = findChild(tabletStage, "tabletSpreadDelegate_" + targetAppId);
166+ verify(targetAppDelegate);
167+ var lastValue = undefined;
168+ do {
169+ lastValue = targetAppDelegate.animatedProgress;
170+ wait(30);
171+ } while (lastValue != targetAppDelegate.animatedProgress);
172+ }
173+
123174 function test_tappingSwitchesFocusBetweenStages() {
124175 webbrowserCheckBox.checked = true;
125176 waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId);
@@ -155,7 +206,6 @@ Rectangle {
155206 tryCompare(webbrowserApp.session.surface, "activeFocus", false);
156207 }
157208
158−
159209 function test_closeAppInSideStage() {
160210 dialerCheckBox.checked = true;
161211 waitUntilAppSurfaceShowsUp(dialerCheckBox.appId);
@@ -200,6 +250,116 @@ Rectangle {
200250 appWindow.width / 2, appWindow.height / 2,
201251 appWindow.width / 2, -appWindow.height / 2);
202252 }
253+
254+ function test_suspendsAndResumesAppsInMainStage() {
255+ webbrowserCheckBox.checked = true;
256+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
257+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
258+
259+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
260+
261+ galleryCheckBox.checked = true;
262+ var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
263+ compare(galleryApp.stage, ApplicationInfoInterface.MainStage);
264+
265+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
266+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
267+
268+ switchToApp(webbrowserApp.appId);
269+
270+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Suspended);
271+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
272+
273+ switchToApp(galleryApp.appId);
274+
275+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
276+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
277+ }
278+
279+
280+ function test_foregroundMainAndSideStageAppsAreKeptRunning() {
281+
282+ var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
283+ verify(stagesPriv);
284+
285+ // launch two main stage apps
286+ // gallery will be on foreground and webbrowser on background
287+
288+ webbrowserCheckBox.checked = true;
289+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId)
290+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
291+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
292+
293+ galleryCheckBox.checked = true;
294+ waitUntilAppSurfaceShowsUp(galleryCheckBox.appId)
295+ var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
296+ compare(galleryApp.stage, ApplicationInfoInterface.MainStage);
297+
298+ compare(stagesPriv.mainStageAppId, galleryCheckBox.appId);
299+
300+ // then launch two side stage apps
301+ // facebook will be on foreground and dialer on background
302+
303+ dialerCheckBox.checked = true;
304+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId)
305+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
306+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
307+
308+ facebookCheckBox.checked = true;
309+ waitUntilAppSurfaceShowsUp(facebookCheckBox.appId)
310+ var facebookApp = ApplicationManager.findApplication(facebookCheckBox.appId);
311+ compare(facebookApp.stage, ApplicationInfoInterface.SideStage);
312+
313+ compare(stagesPriv.sideStageAppId, facebookCheckBox.appId);
314+
315+ // Now check that the foreground apps are running and that the background ones
316+ // are suspended
317+
318+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
319+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
320+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Running);
321+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Suspended);
322+
323+ switchToApp(dialerCheckBox.appId);
324+
325+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
326+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
327+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Suspended);
328+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Running);
329+
330+ switchToApp(webbrowserCheckBox.appId);
331+
332+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Suspended);
333+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
334+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Suspended);
335+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Running);
336+ }
337+
338+ function test_foregroundAppsAreSuspendedWhenStageIsSuspended() {
339+ webbrowserCheckBox.checked = true;
340+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId)
341+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
342+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
343+
344+ dialerCheckBox.checked = true;
345+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId)
346+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
347+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
348+
349+
350+ compare(webbrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
351+ compare(dialerApp.requestedState, ApplicationInfoInterface.RequestedRunning);
352+
353+ tabletStage.suspended = true;
354+
355+ tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
356+ tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
357+
358+ tabletStage.suspended = false;
359+
360+ tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
361+ tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
362+ }
203363 }
204364
205365}
tests/qmltests/Tutorial/tst_Tutorial.qml+7−5
@@ -263,12 +263,14 @@ Item {
263263 goToPage(null);
264264 }
265265
266− function test_walkthroughOnDesktop() {
266+ function test_skipOnDesktop() {
267+ var tutorial = findChild(shell, "tutorial");
268+ tryCompare(tutorial, "active", true);
269+ tryCompare(tutorial, "running", true);
270+
267271 shell.usageScenario = "desktop";
268− var page = goToPage("tutorialLeftFinish");
269− var tick = findChild(page, "tick");
270− tap(tick);
271− checkFinished();
272+ tryCompare(tutorial, "active", false);
273+ tryCompare(tutorial, "running", false);
272274 }
273275
274276 function test_launcherShortDrag() {
tests/qmltests/tst_OrientedShell.qml+3−3
@@ -46,7 +46,7 @@ Rectangle {
4646 }
4747
4848 QtObject {
49− id: mockUsageModeSettings
49+ id: mockUnity8Settings
5050 property string usageMode: usageModeSelector.model[usageModeSelector.selectedIndex]
5151 }
5252
@@ -125,7 +125,7 @@ Rectangle {
125125 sourceComponent: Component {
126126 OrientedShell {
127127 anchors.fill: parent
128− usageModeSettings: mockUsageModeSettings
128+ unity8Settings: mockUnity8Settings
129129 oskSettings: mockOskSettings
130130 physicalOrientation: root.physicalOrientation0
131131 orientationLocked: orientationLockedCheckBox.checked
@@ -926,7 +926,7 @@ Rectangle {
926926
927927 function test_attachRemoveInputDevices() {
928928 usageModeSelector.selectedIndex = 2;
929− tryCompare(mockUsageModeSettings, "usageMode", "Automatic")
929+ tryCompare(mockUnity8Settings, "usageMode", "Automatic")
930930
931931 loadShell("mako")
932932 var shell = findChild(orientedShell, "shell");
tests/qmltests/tst_Shell.qml+390−23
@@ -47,6 +47,7 @@ Rectangle {
4747 }
4848
4949 Item {
50+ id: shellContainer
5051 anchors.left: root.left
5152 anchors.right: controls.left
5253 anchors.top: root.top
@@ -74,6 +75,18 @@ Rectangle {
7475 width: units.gu(100)
7576 height: units.gu(71)
7677 }
78+ },
79+ State {
80+ name: "desktop"
81+ PropertyChanges {
82+ target: shellLoader
83+ width: shellContainer.width
84+ height: shellContainer.height
85+ }
86+ PropertyChanges {
87+ target: mouseEmulation
88+ checked: false
89+ }
7790 }
7891 ]
7992
@@ -81,6 +94,7 @@ Rectangle {
8194 property bool itemDestroyed: false
8295 sourceComponent: Component {
8396 Shell {
97+ id: __shell
8498 usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
8599 orientation: Qt.PortraitOrientation
86100 primaryOrientation: Qt.PortraitOrientation
@@ -88,6 +102,10 @@ Rectangle {
88102 Component.onDestruction: {
89103 shellLoader.itemDestroyed = true;
90104 }
105+ Component.onCompleted: {
106+ var keyMapper = testCase.findChild(__shell, "physicalKeysMapper");
107+ keyMapper.controlInsteadOfAlt = true;
108+ }
91109 }
92110 }
93111 }
@@ -112,16 +130,32 @@ Rectangle {
112130 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
113131 spacing: units.gu(1)
114132
115− Button {
116− text: "Show Greeter"
117− activeFocusOnPress: false
118− onClicked: {
119− if (shellLoader.status !== Loader.Ready)
120− return;
121−
122− var greeter = testCase.findChild(shellLoader.item, "greeter");
123− if (!greeter.shown) {
124− LightDM.Greeter.showGreeter();
133+ Row {
134+ spacing: units.gu(1)
135+ Button {
136+ text: "Show Greeter"
137+ activeFocusOnPress: false
138+ onClicked: {
139+ if (shellLoader.status !== Loader.Ready)
140+ return;
141+
142+ var greeter = testCase.findChild(shellLoader.item, "greeter");
143+ if (!greeter.shown) {
144+ LightDM.Greeter.showGreeter();
145+ }
146+ }
147+ }
148+ Button {
149+ text: "Hide Greeter"
150+ activeFocusOnPress: false
151+ onClicked: {
152+ if (shellLoader.status !== Loader.Ready)
153+ return;
154+
155+ var greeter = testCase.findChild(shellLoader.item, "greeter");
156+ if (greeter.shown) {
157+ greeter.hide()
158+ }
125159 }
126160 }
127161 }
@@ -141,7 +175,7 @@ Rectangle {
141175 anchors { left: parent.left; right: parent.right }
142176 activeFocusOnPress: false
143177 text: "Size"
144− model: ["phone", "tablet"]
178+ model: ["phone", "tablet", "desktop"]
145179 onSelectedIndexChanged: {
146180 shellLoader.active = false;
147181 shellLoader.state = model[selectedIndex];
@@ -163,6 +197,17 @@ Rectangle {
163197
164198 Label { text: "Applications"; font.bold: true }
165199
200+ Button {
201+ text: "Start all apps"
202+ width: parent.width
203+ onClicked: {
204+ for (var i = 0; i < ApplicationManager.availableApplications.length; i++) {
205+ var appId = ApplicationManager.availableApplications[i];
206+ ApplicationManager.startApplication(appId)
207+ }
208+ }
209+ }
210+
166211 Repeater {
167212 id: appRepeater
168213 model: ApplicationManager.availableApplications
@@ -226,6 +271,12 @@ Rectangle {
226271 signalName: "actionInvoked"
227272 }
228273
274+ SignalSpy {
275+ id: appRemovedSpy
276+ target: ApplicationManager
277+ signalName: "applicationRemoved"
278+ }
279+
229280 Telephony.CallEntry {
230281 id: phoneCall
231282 phoneNumber: "+447812221111"
@@ -248,9 +299,9 @@ Rectangle {
248299 }
249300
250301 function cleanup() {
302+ mouseEmulation.checked = true;
251303 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
252304 tearDown();
253− GSettingsController.setUsageMode("Staged");
254305 }
255306
256307 function loadShell(formFactor) {
@@ -269,6 +320,20 @@ Rectangle {
269320 waitForGreeterToStabilize();
270321 }
271322
323+ function loadDesktopShellWithApps() {
324+ loadShell("desktop");
325+ shell.usageScenario = "desktop"
326+ waitForRendering(root)
327+ var app1 = ApplicationManager.startApplication("dialer-app")
328+ var app2 = ApplicationManager.startApplication("webbrowser-app")
329+ var app3 = ApplicationManager.startApplication("camera-app")
330+ var app4 = ApplicationManager.startApplication("facebook-webapp")
331+ var app5 = ApplicationManager.startApplication("calendar-app")
332+ var app6 = ApplicationManager.startApplication("gallery-app")
333+ var app7 = ApplicationManager.startApplication("camera-app")
334+ waitUntilAppWindowIsFullyLoaded(app7);
335+ }
336+
272337 function waitForGreeterToStabilize() {
273338 var greeter = findChild(shell, "greeter");
274339 verify(greeter);
@@ -472,6 +537,41 @@ Rectangle {
472537 confirmLoggedIn(data.loggedIn)
473538 }
474539
540+ function test_longLeftEdgeSwipeTakesToAppsAndResetSearchString() {
541+ loadShell("phone");
542+ swipeAwayGreeter();
543+ dragLauncherIntoView();
544+ dashCommunicatorSpy.clear();
545+
546+ tapOnAppIconInLauncher();
547+ waitUntilApplicationWindowIsFullyVisible();
548+
549+ verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
550+
551+ //Long left swipe
552+ swipeFromLeftEdge(units.gu(30));
553+
554+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
555+
556+ compare(dashCommunicatorSpy.count, 1);
557+ }
558+
559+ function test_ClickUbuntuIconInLauncherTakesToAppsAndResetSearchString() {
560+ loadShell("phone");
561+ swipeAwayGreeter();
562+ dragLauncherIntoView();
563+ dashCommunicatorSpy.clear();
564+
565+ var launcher = findChild(shell, "launcher");
566+ var dashIcon = findChild(launcher, "dashItem");
567+ verify(dashIcon != undefined);
568+ mouseClick(dashIcon);
569+
570+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
571+
572+ compare(dashCommunicatorSpy.count, 1);
573+ }
574+
475575 function test_suspend() {
476576 loadShell("phone");
477577 swipeAwayGreeter();
@@ -486,9 +586,9 @@ Rectangle {
486586 verify(mainAppId != "");
487587 var mainApp = ApplicationManager.findApplication(mainAppId);
488588 verify(mainApp);
489− tryCompare(mainApp, "state", ApplicationInfoInterface.Running);
589+ tryCompare(mainApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
490590
491− // Suspend while call is active...
591+ // Display off while call is active...
492592 callManager.foregroundCall = phoneCall;
493593 Powerd.status = Powerd.Off;
494594 tryCompare(greeter, "shown", false);
@@ -499,8 +599,7 @@ Rectangle {
499599 Powerd.status = Powerd.Off;
500600 tryCompare(greeter, "fullyShown", true);
501601
502− tryCompare(ApplicationManager, "suspended", true);
503− compare(mainApp.state, ApplicationInfoInterface.Suspended);
602+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
504603
505604 // And wake up
506605 Powerd.status = Powerd.On;
@@ -516,8 +615,7 @@ Rectangle {
516615 removeTimeConstraintsFromDirectionalDragAreas(greeter);
517616 swipeAwayGreeter();
518617
519− tryCompare(ApplicationManager, "suspended", false);
520− compare(mainApp.state, ApplicationInfoInterface.Running);
618+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedRunning);
521619 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
522620 }
523621
@@ -688,15 +786,15 @@ Rectangle {
688786
689787 function test_launchedAppHasActiveFocus_data() {
690788 return [
691− {tag: "phone", formFactor: "phone", usageMode: "Staged"},
692− {tag: "tablet", formFactor: "tablet", usageMode: "Staged"},
693− {tag: "desktop", formFactor: "tablet", usageMode: "Windowed"}
789+ {tag: "phone", formFactor: "phone", usageScenario: "phone"},
790+ {tag: "tablet", formFactor: "tablet", usageScenario: "tablet"},
791+ {tag: "desktop", formFactor: "tablet", usageScenario: "desktop"}
694792 ]
695793 }
696794
697795 function test_launchedAppHasActiveFocus(data) {
698− GSettingsController.setUsageMode(data.usageMode);
699796 loadShell(data.formFactor);
797+ shell.usageScenario = data.usageScenario;
700798 swipeAwayGreeter();
701799
702800 var webApp = ApplicationManager.startApplication("webbrowser-app");
@@ -724,7 +822,7 @@ Rectangle {
724822
725823 // check that the desktop stage and window have been loaded
726824 {
727− var desktopWindow = findChild(shell, "decoratedWindow_webbrowser-app");
825+ var desktopWindow = findChild(shell, "appWindow_webbrowser-app");
728826 verify(desktopWindow);
729827 }
730828
@@ -1082,6 +1180,8 @@ Rectangle {
10821180 }
10831181
10841182 function test_tapUbuntuIconInLauncherOverAppSpread() {
1183+ launcherShowDashHomeSpy.clear();
1184+
10851185 loadShell("phone");
10861186 swipeAwayGreeter();
10871187
@@ -1228,5 +1328,272 @@ Rectangle {
12281328 var launcher = findChild(shell, "launcher");
12291329 compare(launcher.inverted, data.launcherInverted);
12301330 }
1331+
1332+ function test_unfocusedAppsGetSuspendedAfterEnteringStagedMode() {
1333+ loadShell("tablet");
1334+ shell.usageScenario = "desktop";
1335+
1336+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1337+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1338+
1339+ var galleryApp = ApplicationManager.startApplication("gallery-app");
1340+ waitUntilAppWindowIsFullyLoaded(galleryApp);
1341+
1342+ ApplicationManager.requestFocusApplication("unity8-dash");
1343+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1344+
1345+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1346+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1347+
1348+ shell.usageScenario = "tablet";
1349+
1350+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1351+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1352+ }
1353+
1354+ function test_unfocusedAppsAreResumedWhenEnteringWindowedMode() {
1355+ loadShell("tablet");
1356+ shell.usageScenario = "tablet";
1357+
1358+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1359+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1360+
1361+ var galleryApp = ApplicationManager.startApplication("gallery-app");
1362+ waitUntilAppWindowIsFullyLoaded(galleryApp);
1363+
1364+ ApplicationManager.requestFocusApplication("unity8-dash");
1365+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1366+
1367+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1368+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1369+
1370+ shell.usageScenario = "desktop";
1371+
1372+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1373+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1374+ }
1375+
1376+ function test_altTabSwitchesFocus() {
1377+ loadShell("desktop");
1378+ shell.usageScenario = "desktop"
1379+ waitForRendering(root)
1380+
1381+ var desktopStage = findChild(shell, "stage");
1382+ verify(desktopStage != null)
1383+
1384+ var app1 = ApplicationManager.startApplication("dialer-app")
1385+ waitUntilAppWindowIsFullyLoaded(app1);
1386+ var app2 = ApplicationManager.startApplication("webbrowser-app")
1387+ waitUntilAppWindowIsFullyLoaded(app2);
1388+ var app3 = ApplicationManager.startApplication("camera-app")
1389+ waitUntilAppWindowIsFullyLoaded(app3);
1390+
1391+ // Do a quick alt-tab and see if focus changes
1392+ tryCompare(app3.session.surface, "activeFocus", true)
1393+ keyClick(Qt.Key_Tab, Qt.ControlModifier)
1394+ tryCompare(app2.session.surface, "activeFocus", true)
1395+
1396+
1397+ tryCompare(desktopStage, "state", "")
1398+
1399+ // Just press Alt, make sure the spread comes up
1400+ keyPress(Qt.Key_Control);
1401+ keyClick(Qt.Key_Tab);
1402+ tryCompare(desktopStage, "state", "altTab")
1403+
1404+ // Release control, check if spread disappears
1405+ keyRelease(Qt.Key_Control)
1406+ tryCompare(desktopStage, "state", "")
1407+
1408+ // Focus should have switched back now
1409+ tryCompare(app3.session.surface, "activeFocus", true)
1410+ }
1411+
1412+ function test_altTabWrapAround() {
1413+ loadDesktopShellWithApps();
1414+
1415+ var desktopStage = findChild(shell, "stage");
1416+ verify(desktopStage !== null)
1417+
1418+ var appContainer = findInvisibleChild(shell, "appContainer")
1419+ verify(appContainer !== null)
1420+
1421+ var appRepeater = findInvisibleChild(shell, "appRepeater")
1422+ verify(appRepeater !== null)
1423+
1424+ // remember the focused appId
1425+ var focused = ApplicationManager.get(ApplicationManager.findApplication(ApplicationManager.focusedApplicationId));
1426+
1427+ tryCompare(desktopStage, "state", "")
1428+
1429+ // Just press Alt, make sure the spread comes up
1430+ keyPress(Qt.Key_Control);
1431+ keyClick(Qt.Key_Tab);
1432+ tryCompare(desktopStage, "state", "altTab")
1433+ tryCompare(appRepeater, "highlightedIndex", 1)
1434+ waitForRendering(shell)
1435+
1436+ // Now press and hold Tab, make sure the highlight moves all the way but stops at the last one
1437+ // We can't simulate a pressed key with keyPress() currently, so let's inject the events
1438+ // at API level. Jump for 10 times, verify that it's still at the last one and didn't wrap around.
1439+ for (var i = 0; i < 10; i++) {
1440+ appContainer.selectNext(true); // true == isAutoRepeat
1441+ wait(0); // Trigger the event loop to make sure all the things happen
1442+ }
1443+ tryCompare(appRepeater, "highlightedIndex", 6)
1444+
1445+ // Now release it once, and verify that it does wrap around with an additional Tab press
1446+ keyRelease(Qt.Key_Tab);
1447+ keyClick(Qt.Key_Tab);
1448+ tryCompare(appRepeater, "highlightedIndex", 0)
1449+
1450+ // Release control, check if spread disappears
1451+ keyRelease(Qt.Key_Control)
1452+ tryCompare(desktopStage, "state", "")
1453+
1454+ // Make sure that after wrapping around once, we have the same one focused as at the beginning
1455+ tryCompare(focused.session.surface, "activeFocus", true)
1456+ }
1457+
1458+ function test_altBackTabNavigation() {
1459+ loadDesktopShellWithApps();
1460+
1461+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1462+ verify(appRepeater !== null);
1463+
1464+ keyPress(Qt.Key_Control)
1465+ keyClick(Qt.Key_Tab);
1466+ tryCompare(appRepeater, "highlightedIndex", 1);
1467+
1468+ keyClick(Qt.Key_Tab);
1469+ tryCompare(appRepeater, "highlightedIndex", 2);
1470+
1471+ keyClick(Qt.Key_Tab);
1472+ tryCompare(appRepeater, "highlightedIndex", 3);
1473+
1474+ keyClick(Qt.Key_Tab);
1475+ tryCompare(appRepeater, "highlightedIndex", 4);
1476+
1477+ keyClick(Qt.Key_Backtab);
1478+ tryCompare(appRepeater, "highlightedIndex", 3);
1479+
1480+ keyClick(Qt.Key_Backtab);
1481+ tryCompare(appRepeater, "highlightedIndex", 2);
1482+
1483+ keyClick(Qt.Key_Backtab);
1484+ tryCompare(appRepeater, "highlightedIndex", 1);
1485+
1486+ keyRelease(Qt.Key_Control);
1487+ }
1488+
1489+ function test_highlightFollowsMouse() {
1490+ loadDesktopShellWithApps()
1491+
1492+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1493+ verify(appRepeater !== null);
1494+
1495+ keyPress(Qt.Key_Control)
1496+ keyClick(Qt.Key_Tab);
1497+
1498+ tryCompare(appRepeater, "highlightedIndex", 1);
1499+
1500+ var x = 0;
1501+ var y = shell.height * .75;
1502+ mouseMove(shell, x, y)
1503+
1504+ for (var i = 0; i < 7; i++) {
1505+ while (appRepeater.highlightedIndex != i && x <= 4000) {
1506+ x+=10;
1507+ mouseMove(shell, x, y)
1508+ wait(0); // spin the loop so bindings get evaluated
1509+ }
1510+ }
1511+
1512+ verify(y < 4000);
1513+
1514+ keyRelease(Qt.Key_Control);
1515+ }
1516+
1517+ function test_closeFromSpread() {
1518+ loadDesktopShellWithApps()
1519+
1520+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1521+ verify(appRepeater !== null);
1522+
1523+ keyPress(Qt.Key_Control)
1524+ keyClick(Qt.Key_Tab);
1525+
1526+ appRemovedSpy.clear();
1527+
1528+ var closedAppId = ApplicationManager.get(2).appId;
1529+ var appDelegate2 = appRepeater.itemAt(2);
1530+ var closeMouseArea = findChild(appDelegate2, "closeMouseArea");
1531+
1532+ // Move the mosue over tile 2 and verify the close button becomes visible
1533+ var x = 0;
1534+ var y = shell.height * .5;
1535+ mouseMove(shell, x, y)
1536+ while (appRepeater.highlightedIndex !== 2 && x <= 4000) {
1537+ x+=10;
1538+ mouseMove(shell, x, y)
1539+ wait(0); // spin the loop so bindings get evaluated
1540+ }
1541+ tryCompare(closeMouseArea, "enabled", true)
1542+
1543+ // Close the app using the close button
1544+ mouseClick(closeMouseArea, closeMouseArea.width / 2, closeMouseArea.height / 2)
1545+
1546+ // Verify applicationRemoved has been emitted correctly
1547+ tryCompare(appRemovedSpy, "count", 1)
1548+ compare(appRemovedSpy.signalArguments[0][0], closedAppId);
1549+
1550+ keyRelease(Qt.Key_Control);
1551+ }
1552+
1553+ function test_selectFromSpreadWithMouse_data() {
1554+ return [
1555+ {tag: "click on tileInfo", tileInfo: true },
1556+ {tag: "click on surface", tileInfo: false },
1557+ ]
1558+ }
1559+
1560+ function test_selectFromSpreadWithMouse(data) {
1561+ loadDesktopShellWithApps()
1562+
1563+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1564+ verify(appRepeater !== null);
1565+
1566+ keyPress(Qt.Key_Control)
1567+ keyClick(Qt.Key_Tab);
1568+
1569+ appRemovedSpy.clear();
1570+
1571+ var focusAppId = ApplicationManager.get(2).appId;
1572+ var appDelegate2 = appRepeater.itemAt(2);
1573+ var decoratedWindow = findChild(appDelegate2, "decoratedWindow");
1574+ var stage = findChild(shell, "stage");
1575+
1576+ tryCompare(stage, "state", "altTab");
1577+
1578+ // Move the mouse over tile 2 and verify the highlight becomes visible
1579+ var x = 0;
1580+ var y = shell.height * (data.tileInfo ? .5 : 0.95)
1581+ mouseMove(shell, x, y)
1582+ while (appRepeater.highlightedIndex !== 2 && x <= 4000) {
1583+ x+=10;
1584+ mouseMove(shell, x, y)
1585+ wait(0); // spin the loop so bindings get evaluated
1586+ }
1587+ tryCompare(decoratedWindow, "highlightShown", true);
1588+
1589+ // Click the tile
1590+ mouseClick(decoratedWindow, decoratedWindow.width / 2, decoratedWindow.height / 2)
1591+
1592+ // Verify that we left the spread and app2 is the focused one now
1593+ tryCompare(stage, "state", "");
1594+ tryCompare(ApplicationManager, "focusedApplicationId", focusAppId);
1595+
1596+ keyRelease(Qt.Key_Control);
1597+ }
12311598 }
12321599}
tests/qmltests/tst_ShellWithPin.qml+5−4
@@ -498,12 +498,13 @@ Item {
498498
499499 function test_suspend() {
500500 var greeter = findChild(shell, "greeter");
501+ var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
501502
502503 // Put it to sleep
503504 Powerd.status = Powerd.Off;
504505
505− // If locked, ApplicationManager.suspended should be true
506− tryCompare(ApplicationManager, "suspended", true);
506+ // If locked, applicationsDisplayLoader.item.suspended should be true
507+ tryCompare(applicationsDisplayLoader.item, "suspended", true);
507508
508509 // And wake up
509510 Powerd.status = Powerd.On;
@@ -513,12 +514,12 @@ Item {
513514 swipeAwayGreeter(true);
514515
515516 // We have a lockscreen, make sure we're still suspended
516− tryCompare(ApplicationManager, "suspended", true);
517+ tryCompare(applicationsDisplayLoader.item, "suspended", true);
517518
518519 enterPin("1234")
519520
520521 // Now that the lockscreen has gone too, make sure we're waking up
521− tryCompare(ApplicationManager, "suspended", false);
522+ tryCompare(applicationsDisplayLoader.item, "suspended", false);
522523
523524 }
524525
tests/scripts/READMEnew file+9
@@ -0,0 +1,9 @@
1+This directory holds a few handy shell-scripts to simplify runtime-testing of DBus-APIs (requires qdbus-qt5 to be installed).
2+
3+launcher-icon related:
4+ * list-launcher-icons.sh - get all icons of unity8-launcher
5+ * alert-launcher-icon.sh - trigger alert/wiggle of unfocused launcher-icon
6+ * get-progress.sh - get progress-value of a launcher-icon
7+ * set-progress.sh - set progress-value of a launcher-icon
8+ * set-count.sh - set the count of a launcher-icon
9+ * set-count-visible.sh - set the count-visible flag of a launcher-icon
tests/scripts/alert-launcher-icon.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 com.canonical.Unity.Launcher.Item.Alert
tests/scripts/get-progress.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Get com.canonical.Unity.Launcher.Item progress
tests/scripts/list-launcher-icons.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity | grep Launcher | cut -f6 -d/
tests/scripts/set-count-visible.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item countVisible $2
tests/scripts/set-count.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item count $2
tests/scripts/set-progress.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item progress $2
tests/utils/modules/Unity/Test/UnityTestCase.qml+9
@@ -351,6 +351,15 @@ TestCase {
351351 event.commit()
352352 }
353353
354+ function touchMove(item, tox, toy) {
355+ var root = fetchRootItem(item)
356+ var rootPoint = item.mapToItem(root, tox, toy)
357+
358+ var event = touchEvent(item);
359+ event.move(0 /* touchId */, rootPoint.x, rootPoint.y);
360+ event.commit();
361+ }
362+
354363 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
355364 // Make sure the item is rendered
356365 waitForRendering(item);