on xenial help sign in
rebash on top of lp:~lukas-kde/unity8/closeAppsFromQuicklist
commit6ae5fda0f7f4fef0d413a3c746f7c0f85565e2c8 authorhandsome_feng <445865575@qq.com> dateWed 29 Jul 2015, 11:31 (11 years ago) parentf7a5158ef55e4d
90 files changed+3282−541
debian/changelog+34 debian/control+5−3 plugins/AccountsService/AccountsService.cpp+31−42 plugins/AccountsService/AccountsService.h+4−3 plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+13−11 plugins/AccountsService/AccountsServiceDBusAdaptor.h+3−2 plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1 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/Lights/Lights.cpp+1−1 plugins/Ubuntu/SystemImage/SystemImage.cpp+7−8 plugins/Ubuntu/SystemImage/SystemImage.h+1−5 plugins/Unity/Connectivity/Connectivity.cpp+5−5 plugins/Unity/Connectivity/Connectivity.h+1 plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h+1−1 plugins/Unity/DashCommunicator/plugin.cpp+1 plugins/Unity/Launcher/CMakeLists.txt+1−1 plugins/Unity/Launcher/asadapter.cpp+4−2 plugins/Unity/Launcher/asadapter.h+1−4 plugins/Unity/Launcher/dbusinterface.cpp+39−17 plugins/Unity/Launcher/dbusinterface.h+3−1 plugins/Unity/Launcher/gsettings.cpp+1−1 plugins/Unity/Launcher/launcheritem.cpp+33 plugins/Unity/Launcher/launcheritem.h+4 plugins/Unity/Launcher/launchermodel.cpp+78−38 plugins/Unity/Launcher/launchermodel.h+2 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/dbusunitysessionservice.cpp+47−55 plugins/Unity/Session/dbusunitysessionservice.h+11−8 plugins/Wizard/System.cpp+13−9 po/ar.po+76−61 po/ga.po+697 po/gd.po+29−23 po/pt.po+10−10 po/sw.po+86−80 qml/Dash/Previews/PreviewComment.qml+83 qml/Dash/Previews/PreviewCommentInput.qml+66 qml/Dash/Previews/PreviewWidgetFactory.qml+2 qml/Launcher/LauncherDelegate.qml+100−15 qml/Launcher/LauncherPanel.qml+93−13 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/Notifications/Notifications.qml+1−1 qml/Panel/IndicatorItem.qml+1−1 qml/Panel/Indicators/IndicatorBase.qml−1 qml/Tutorial/TutorialLeft.qml+16−6 tests/autopilot/unity8/settings_wizard/__init__.py+507 tests/autopilot/unity8/settings_wizard/fixture_setup.py+58 tests/autopilot/unity8/settings_wizard/ofono_helper.py+116 tests/autopilot/unity8/settings_wizard/tests/__init__.py tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py+79 tests/mocks/QMenuModel/QDBusActionGroup.qml+2−2 tests/mocks/QMenuModel/actiondata.h+3−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−3 tests/mocks/Unity/Launcher/MockLauncherModel.h+4 tests/plugins/AccountsService/client.cpp+10−20 tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+3 tests/plugins/LightDM/CMakeLists.txt+6 tests/plugins/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+64−7 tests/plugins/Unity/Session/sessionbackendtest.cpp+27−40 tests/qmltests/CMakeLists.txt+2 tests/qmltests/Dash/Previews/tst_PreviewComment.qml+84 tests/qmltests/Dash/Previews/tst_PreviewCommentInput.qml+85 tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+2 tests/qmltests/Launcher/tst_Launcher.qml+210−7 tests/qmltests/Tutorial/tst_Tutorial.qml+31−1 tests/qmltests/tst_ShellWithPin.qml+1−1 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 tools/CMakeLists.txt+1 tools/scopetool.cpp+6 tools/unix-signal-handler.cpp+141 tools/unix-signal-handler.h+54
debian/changelog+34
@@ -1,3 +1,37 @@
1+unity8 (8.10+15.10.20150717-0ubuntu1) wily; urgency=medium
2+
3+ [ Albert Astals ]
4+ * Ensure tutorial doesn't let user drag the launcher past screen edge.
5+ (LP: #1454882)
6+
7+ [ Albert Astals Cid ]
8+ * Fix build with ninja
9+
10+ [ Andrea Cimitan ]
11+ * Add PreviewSocialComment
12+
13+ [ Daniel d'Andrada ]
14+ * Ensure tutorial doesn't let user drag the launcher past screen edge.
15+ (LP: #1454882)
16+ * Update tst_ShellWithPin
17+
18+ [ Josh Arenson ]
19+ * Add a basic autopilot test for the SettingsWizard
20+
21+ [ Lukáš Tinkl ]
22+ * Avoid sync DBUS calls
23+
24+ [ Marcus Tomlinson ]
25+ * Handle TERM, HUP, and INT signals in unity-scope-tool (LP: #1470464)
26+
27+ [ Michael Zanetti ]
28+ * Add PreviewSocialComment
29+
30+ [ Nick Dedekind ]
31+ * Fixed qml warnings in 'make tryShell'
32+
33+ -- CI Train Bot <ci-train-bot@canonical.com> Fri, 17 Jul 2015 15:53:38 +0000
34+
135unity8 (8.10+15.10.20150713-0ubuntu1) wily; urgency=medium
236
337 [ Albert Astals Cid ]
debian/control+5−3
@@ -30,7 +30,7 @@ Build-Depends: cmake,
3030 libqt5xmlpatterns5-dev,
3131 libsystemsettings-dev,
3232 libudev-dev,
33− libunity-api-dev (>= 7.97),
33+ libunity-api-dev (>= 7.98),
3434 libusermetricsoutput1-dev,
3535 libxcb1-dev,
3636 pkg-config,
@@ -101,7 +101,7 @@ Depends: gsettings-desktop-schemas,
101101 qtdeclarative5-ubuntu-telephony0.1,
102102 qtdeclarative5-ubuntu-web-plugin,
103103 ubuntu-system-settings,
104− unity-launcher-impl-4,
104+ unity-launcher-impl-7,
105105 unity8-common (= ${source:Version}),
106106 unity8-private (= ${binary:Version}),
107107 unity8-private | unity-launcher-impl,
@@ -148,6 +148,7 @@ Depends: autopilot-qt5 (>= 1.4),
148148 gir1.2-notify-0.7,
149149 libqt5test5,
150150 libqt5widgets5,
151+ ofono-phonesim,
151152 python3-autopilot,
152153 python3-evdev,
153154 python3-fixtures,
@@ -158,6 +159,7 @@ Depends: autopilot-qt5 (>= 1.4),
158159 unity8 (= ${source:Version}),
159160 unity8-fake-env (= ${source:Version}),
160161 url-dispatcher-tools,
162+ xvfb,
161163 ${misc:Depends},
162164 ${python3:Depends},
163165 ${shlibs:Depends},
@@ -189,7 +191,7 @@ Depends: accountsservice-ubuntu-schemas (>= 0.0.3),
189191 ${misc:Depends},
190192 ${shlibs:Depends},
191193Provides: unity-launcher-impl,
192− unity-launcher-impl-4,
194+ unity-launcher-impl-7,
193195Description: Unity 8 private libs
194196 The Unity 8 shell is the primary user interface for Ubuntu devices.
195197 .
plugins/AccountsService/AccountsService.cpp+31−42
@@ -23,10 +23,9 @@
2323#include <QStringList>
2424#include <QDebug>
2525
26−AccountsService::AccountsService(QObject* parent)
27− : QObject(parent),
26+AccountsService::AccountsService(QObject* parent, const QString &user)
27+ : QObject(parent),
2828 m_service(new AccountsServiceDBusAdaptor(this)),
29− m_user(""),
3029 m_demoEdges(false),
3130 m_enableLauncherWhileLocked(false),
3231 m_enableIndicatorsWhileLocked(false),
@@ -36,12 +35,10 @@ AccountsService::AccountsService(QObject* parent)
3635 m_hereEnabled(false),
3736 m_hereLicensePath() // null means not set yet
3837{
39− connect(m_service, SIGNAL(propertiesChanged(const QString &, const QString &, const QStringList &)),
40− this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
41− connect(m_service, SIGNAL(maybeChanged(const QString &)),
42− this, SLOT(maybeChanged(const QString &)));
38+ connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
39+ connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
4340
44− setUser(qgetenv("USER"));
41+ setUser(!user.isEmpty() ? user : QString::fromUtf8(qgetenv("USER")));
4542}
4643
4744QString AccountsService::user() const
@@ -173,19 +170,18 @@ void AccountsService::updateEnableLauncherWhileLocked(bool async)
173170 connect(watcher, &QDBusPendingCallWatcher::finished,
174171 this, [this](QDBusPendingCallWatcher* watcher) {
175172
176− QDBusPendingReply<QDBusVariant> reply = *watcher;
173+ QDBusPendingReply<QVariant> reply = *watcher;
174+ watcher->deleteLater();
177175 if (reply.isError()) {
178176 qWarning() << "Failed to get 'EnableLauncherWhileLocked' property - " << reply.error().message();
179− watcher->deleteLater();
180177 return;
181178 }
182179
183− auto enableLauncherWhileLocked = reply.value().variant().toBool();
180+ const bool enableLauncherWhileLocked = reply.value().toBool();
184181 if (m_enableLauncherWhileLocked != enableLauncherWhileLocked) {
185182 m_enableLauncherWhileLocked = enableLauncherWhileLocked;
186183 Q_EMIT enableLauncherWhileLockedChanged();
187184 }
188− watcher->deleteLater();
189185 });
190186 if (!async) {
191187 watcher->waitForFinished();
@@ -203,19 +199,18 @@ void AccountsService::updateEnableIndicatorsWhileLocked(bool async)
203199 connect(watcher, &QDBusPendingCallWatcher::finished,
204200 this, [this](QDBusPendingCallWatcher* watcher) {
205201
206− QDBusPendingReply<QDBusVariant> reply = *watcher;
202+ QDBusPendingReply<QVariant> reply = *watcher;
203+ watcher->deleteLater();
207204 if (reply.isError()) {
208205 qWarning() << "Failed to get 'EnableIndicatorsWhileLocked' property - " << reply.error().message();
209− watcher->deleteLater();
210206 return;
211207 }
212208
213− auto enableIndicatorsWhileLocked = reply.value().variant().toBool();
209+ const bool enableIndicatorsWhileLocked = reply.value().toBool();
214210 if (m_enableIndicatorsWhileLocked != enableIndicatorsWhileLocked) {
215211 m_enableIndicatorsWhileLocked = enableIndicatorsWhileLocked;
216212 Q_EMIT enableIndicatorsWhileLockedChanged();
217213 }
218− watcher->deleteLater();
219214 });
220215 if (!async) {
221216 watcher->waitForFinished();
@@ -233,19 +228,18 @@ void AccountsService::updateBackgroundFile(bool async)
233228 connect(watcher, &QDBusPendingCallWatcher::finished,
234229 this, [this](QDBusPendingCallWatcher* watcher) {
235230
236− QDBusPendingReply<QDBusVariant> reply = *watcher;
231+ QDBusPendingReply<QVariant> reply = *watcher;
232+ watcher->deleteLater();
237233 if (reply.isError()) {
238234 qWarning() << "Failed to get 'BackgroundFile' property - " << reply.error().message();
239− watcher->deleteLater();
240235 return;
241236 }
242237
243− auto backgroundFile = reply.value().variant().toString();
238+ const QString backgroundFile = reply.value().toString();
244239 if (m_backgroundFile != backgroundFile) {
245240 m_backgroundFile = backgroundFile;
246241 Q_EMIT backgroundFileChanged();
247242 }
248− watcher->deleteLater();
249243 });
250244 if (!async) {
251245 watcher->waitForFinished();
@@ -263,19 +257,18 @@ void AccountsService::updateStatsWelcomeScreen(bool async)
263257 connect(watcher, &QDBusPendingCallWatcher::finished,
264258 this, [this](QDBusPendingCallWatcher* watcher) {
265259
266− QDBusPendingReply<QDBusVariant> reply = *watcher;
260+ QDBusPendingReply<QVariant> reply = *watcher;
261+ watcher->deleteLater();
267262 if (reply.isError()) {
268263 qWarning() << "Failed to get 'StatsWelcomeScreen' property - " << reply.error().message();
269− watcher->deleteLater();
270264 return;
271265 }
272266
273− auto statsWelcomeScreen = reply.value().variant().toBool();
267+ const bool statsWelcomeScreen = reply.value().toBool();
274268 if (m_statsWelcomeScreen != statsWelcomeScreen) {
275269 m_statsWelcomeScreen = statsWelcomeScreen;
276270 Q_EMIT statsWelcomeScreenChanged();
277271 }
278− watcher->deleteLater();
279272 });
280273 if (!async) {
281274 watcher->waitForFinished();
@@ -293,19 +286,18 @@ void AccountsService::updatePasswordDisplayHint(bool async)
293286 connect(watcher, &QDBusPendingCallWatcher::finished,
294287 this, [this](QDBusPendingCallWatcher* watcher) {
295288
296− QDBusPendingReply<QDBusVariant> reply = *watcher;
289+ QDBusPendingReply<QVariant> reply = *watcher;
290+ watcher->deleteLater();
297291 if (reply.isError()) {
298292 qWarning() << "Failed to get 'PasswordDisplayHint' property - " << reply.error().message();
299− watcher->deleteLater();
300293 return;
301294 }
302295
303− PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)reply.value().variant().toInt();
296+ const PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)reply.value().toInt();
304297 if (m_passwordDisplayHint != passwordDisplayHint) {
305298 m_passwordDisplayHint = passwordDisplayHint;
306299 Q_EMIT passwordDisplayHintChanged();
307300 }
308− watcher->deleteLater();
309301 });
310302 if (!async) {
311303 watcher->waitForFinished();
@@ -323,19 +315,18 @@ void AccountsService::updateFailedLogins(bool async)
323315 connect(watcher, &QDBusPendingCallWatcher::finished,
324316 this, [this](QDBusPendingCallWatcher* watcher) {
325317
326− QDBusPendingReply<QDBusVariant> reply = *watcher;
318+ QDBusPendingReply<QVariant> reply = *watcher;
319+ watcher->deleteLater();
327320 if (reply.isError()) {
328321 qWarning() << "Failed to get 'FailedLogins' property - " << reply.error().message();
329− watcher->deleteLater();
330322 return;
331323 }
332324
333− uint failedLogins = reply.value().variant().toUInt();
325+ const uint failedLogins = reply.value().toUInt();
334326 if (m_failedLogins != failedLogins) {
335327 m_failedLogins = failedLogins;
336328 Q_EMIT failedLoginsChanged();
337329 }
338− watcher->deleteLater();
339330 });
340331 if (!async) {
341332 watcher->waitForFinished();
@@ -353,19 +344,18 @@ void AccountsService::updateHereEnabled(bool async)
353344 connect(watcher, &QDBusPendingCallWatcher::finished,
354345 this, [this](QDBusPendingCallWatcher* watcher) {
355346
356− QDBusPendingReply<QDBusVariant> reply = *watcher;
347+ QDBusPendingReply<QVariant> reply = *watcher;
348+ watcher->deleteLater();
357349 if (reply.isError()) {
358350 qWarning() << "Failed to get 'LicenseAccepted' property - " << reply.error().message();
359− watcher->deleteLater();
360351 return;
361352 }
362353
363− auto hereEnabled = reply.value().variant().toBool();
354+ const bool hereEnabled = reply.value().toBool();
364355 if (m_hereEnabled != hereEnabled) {
365356 m_hereEnabled = hereEnabled;
366357 Q_EMIT hereEnabledChanged();
367358 }
368− watcher->deleteLater();
369359 });
370360 if (!async) {
371361 watcher->waitForFinished();
@@ -383,14 +373,14 @@ void AccountsService::updateHereLicensePath(bool async)
383373 connect(watcher, &QDBusPendingCallWatcher::finished,
384374 this, [this](QDBusPendingCallWatcher* watcher) {
385375
386− QDBusPendingReply<QDBusVariant> reply = *watcher;
376+ QDBusPendingReply<QVariant> reply = *watcher;
377+ watcher->deleteLater();
387378 if (reply.isError()) {
388379 qWarning() << "Failed to get 'LicenseBasePath' property - " << reply.error().message();
389− watcher->deleteLater();
390380 return;
391381 }
392382
393− auto hereLicensePath = reply.value().variant().toString();
383+ QString hereLicensePath = reply.value().toString();
394384 if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
395385 hereLicensePath = "";
396386
@@ -398,7 +388,6 @@ void AccountsService::updateHereLicensePath(bool async)
398388 m_hereLicensePath = hereLicensePath;
399389 Q_EMIT hereLicensePathChanged();
400390 }
401− watcher->deleteLater();
402391 });
403392 if (!async) {
404393 watcher->waitForFinished();
@@ -421,7 +410,7 @@ void AccountsService::setFailedLogins(uint failedLogins)
421410 }
422411}
423412
424−void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
413+void AccountsService::onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
425414{
426415 if (m_user != user) {
427416 return;
@@ -459,7 +448,7 @@ void AccountsService::propertiesChanged(const QString &user, const QString &inte
459448 }
460449}
461450
462−void AccountsService::maybeChanged(const QString &user)
451+void AccountsService::onMaybeChanged(const QString &user)
463452{
464453 if (m_user != user) {
465454 return;
plugins/AccountsService/AccountsService.h+4−3
@@ -72,7 +72,8 @@ public:
7272 Numeric,
7373 };
7474
75− explicit AccountsService(QObject *parent = 0);
75+ explicit AccountsService(QObject *parent = 0, const QString & user = QString());
76+ ~AccountsService() = default;
7677
7778 QString user() const;
7879 void setUser(const QString &user);
@@ -103,8 +104,8 @@ Q_SIGNALS:
103104 void hereLicensePathChanged();
104105
105106private Q_SLOTS:
106− void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
107− void maybeChanged(const QString &user);
107+ void onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
108+ void onMaybeChanged(const QString &user);
108109
109110private:
110111 void updateDemoEdges(bool async = true);
plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+13−11
@@ -21,35 +21,35 @@
2121#include <QDBusConnectionInterface>
2222#include <QDBusMessage>
2323#include <QDBusVariant>
24+#include <QDebug>
2425
2526AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
2627 : QObject(parent),
2728 m_accountsManager(nullptr),
28− m_users(),
2929 m_ignoreNextChanged(false)
3030{
3131 QDBusConnection connection = QDBusConnection::SM_BUSNAME();
3232 QDBusConnectionInterface *interface = connection.interface();
3333 interface->startService("org.freedesktop.Accounts");
3434 m_accountsManager = new QDBusInterface("org.freedesktop.Accounts",
35− "/org/freedesktop/Accounts",
36− "org.freedesktop.Accounts",
37− connection, this);
35+ "/org/freedesktop/Accounts",
36+ "org.freedesktop.Accounts",
37+ connection, this);
3838}
3939
4040QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
4141{
4242 QDBusInterface *iface = getUserInterface(user);
4343 if (iface != nullptr && iface->isValid()) {
44− QDBusMessage answer = iface->call("Get", interface, property);
45− if (answer.type() == QDBusMessage::ReplyMessage) {
46− return answer.arguments()[0].value<QDBusVariant>().variant();
44+ QDBusReply<QVariant> answer = iface->call("Get", interface, property);
45+ if (answer.isValid()) {
46+ return answer;
4747 }
4848 }
4949 return QVariant();
5050}
5151
52−QDBusPendingReply<QDBusVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(const QString &user, const QString &interface, const QString &property)
52+QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(const QString &user, const QString &interface, const QString &property)
5353{
5454 QDBusInterface *iface = getUserInterface(user);
5555 if (iface != nullptr && iface->isValid()) {
@@ -116,9 +116,9 @@ QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user
116116{
117117 QDBusInterface *iface = m_users.value(user);
118118 if (iface == nullptr && m_accountsManager->isValid()) {
119− QDBusMessage answer = m_accountsManager->call("FindUserByName", user);
120− if (answer.type() == QDBusMessage::ReplyMessage) {
121− QString path = answer.arguments()[0].value<QDBusObjectPath>().path();
119+ QDBusReply<QDBusObjectPath> answer = m_accountsManager->asyncCall("FindUserByName", user);
120+ if (answer.isValid()) {
121+ const QString path = answer.value().path();
122122
123123 iface = new QDBusInterface("org.freedesktop.Accounts",
124124 path,
@@ -148,6 +148,8 @@ QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user
148148 SLOT(propertiesChangedSlot(QString, QVariantMap, QStringList)));
149149
150150 m_users.insert(user, iface);
151+ } else {
152+ qWarning() << "Couldn't get user interface" << answer.error().name() << answer.error().message();
151153 }
152154 }
153155 return iface;
plugins/AccountsService/AccountsServiceDBusAdaptor.h+3−2
@@ -33,13 +33,14 @@ class AccountsServiceDBusAdaptor: public QObject, public QDBusContext
3333
3434public:
3535 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
36+ ~AccountsServiceDBusAdaptor() = default;
3637
3738 Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
38− Q_INVOKABLE QDBusPendingReply<QDBusVariant> getUserPropertyAsync(const QString &user, const QString &interface, const QString &property);
39+ Q_INVOKABLE QDBusPendingReply<QVariant> getUserPropertyAsync(const QString &user, const QString &interface, const QString &property);
3940
4041 template <typename T>
4142 inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
42− QVariant variant = getUserProperty(user, interface, property);
43+ const QVariant variant = getUserProperty(user, interface, property);
4344 if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
4445 return qdbus_cast<T>(variant.value<QDBusArgument>());
4546 }
plugins/Greeter/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)
22pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
44
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/Lights/Lights.cpp+1−1
@@ -71,7 +71,7 @@ void Lights::setColor(const QColor &color)
7171 if (m_color != color) {
7272 m_color = color;
7373 Q_EMIT colorChanged(m_color);
74− // FIXME: update the collor if the light is already on
74+ // FIXME: update the color if the light is already on
7575 }
7676}
7777
plugins/Ubuntu/SystemImage/SystemImage.cpp+7−8
@@ -16,19 +16,18 @@
1616
1717#include "SystemImage.h"
1818#include <QDBusConnection>
19−#include <QDBusInterface>
19+#include <QDBusPendingCall>
2020
2121SystemImage::SystemImage(QObject *parent)
22− : QObject(parent),
23− m_interface(new QDBusInterface("com.canonical.SystemImage",
24− "/Service",
25− "com.canonical.SystemImage",
26− QDBusConnection::systemBus(),
27− this))
22+ : QObject(parent)
2823{
2924}
3025
3126void SystemImage::factoryReset()
3227{
33− m_interface->call("FactoryReset");
28+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.SystemImage",
29+ "/Service",
30+ "com.canonical.SystemImage",
31+ "FactoryReset");
32+ QDBusConnection::systemBus().asyncCall(msg);
3433}
plugins/Ubuntu/SystemImage/SystemImage.h+1−5
@@ -19,8 +19,6 @@
1919
2020#include <QObject>
2121
22−class QDBusInterface;
23−
2422class SystemImage : public QObject
2523{
2624 Q_OBJECT
@@ -28,11 +26,9 @@ class SystemImage : public QObject
2826
2927public:
3028 explicit SystemImage(QObject *parent = 0);
29+ ~SystemImage() = default;
3130
3231 Q_INVOKABLE void factoryReset();
33−
34−private:
35− QDBusInterface *m_interface;
3632};
3733
3834#endif // SYSTEMIMAGE_H
plugins/Unity/Connectivity/Connectivity.cpp+5−5
@@ -16,7 +16,6 @@
1616
1717#include "Connectivity.h"
1818#include <QDBusConnection>
19−#include <QDBusInterface>
2019#include <QDBusPendingCall>
2120
2221Connectivity::Connectivity(QObject *parent)
@@ -26,8 +25,9 @@ Connectivity::Connectivity(QObject *parent)
2625
2726void Connectivity::unlockAllModems()
2827{
29− QDBusInterface iface("com.ubuntu.connectivity1",
30− "/com/ubuntu/connectivity1/Private",
31− "com.ubuntu.connectivity1.Private");
32− iface.asyncCall("UnlockAllModems");
28+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.ubuntu.connectivity1",
29+ "/com/ubuntu/connectivity1/Private",
30+ "com.ubuntu.connectivity1.Private",
31+ "UnlockAllModems");
32+ QDBusConnection::sessionBus().asyncCall(msg);
3333}
plugins/Unity/Connectivity/Connectivity.h+1
@@ -26,6 +26,7 @@ class Connectivity : public QObject
2626
2727public:
2828 explicit Connectivity(QObject *parent = 0);
29+ ~Connectivity() = default;
2930
3031 Q_INVOKABLE void unlockAllModems();
3132};
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h+1−1
@@ -36,4 +36,4 @@ public Q_SLOTS:
3636
3737};
3838
39−#endif // DBUSUNITYSESSIONSERVICE_H
39+#endif // DBUSDASHCOMMUNICATORSERVICE_H
plugins/Unity/DashCommunicator/plugin.cpp+1
@@ -25,6 +25,7 @@
2525
2626void DashCommunicatorPlugin::registerTypes(const char *uri)
2727{
28+ // @uri Unity.DashCommunicator
2829 Q_ASSERT(uri == QStringLiteral("Unity.DashCommunicator"));
2930 qmlRegisterType<DashCommunicatorService>(uri, 0, 1, "DashCommunicatorService");
3031 qmlRegisterType<DashCommunicator>(uri, 0, 1, "DashCommunicator");
plugins/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)
22pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
44
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+39−17
@@ -32,10 +32,6 @@ DBusInterface::DBusInterface(LauncherModel *parent):
3232{
3333}
3434
35−DBusInterface::~DBusInterface()
36−{
37−}
38−
3935QString DBusInterface::introspect(const QString &path) const
4036{
4137 /* This case we should just list the nodes */
@@ -66,6 +62,8 @@ QString DBusInterface::introspect(const QString &path) const
6662 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
6763 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
6864 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
65+ "<property name=\"progress\" type=\"i\" access=\"readwrite\" />"
66+ "<method name=\"Alert\" />"
6967 "</interface>";
7068 return nodeiface;
7169}
@@ -126,6 +124,20 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
126124 return false;
127125 }
128126
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+
129141 // First handle methods of the Launcher interface
130142 if (message.interface() == "com.canonical.Unity.Launcher") {
131143 if (message.member() == "Refresh") {
@@ -133,6 +145,13 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
133145 Q_EMIT refreshCalled();
134146 return connection.send(reply);
135147 }
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+ }
136155 }
137156
138157 // Now handle dynamic properties (for launcher emblems)
@@ -144,50 +163,53 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
144163 return false;
145164 }
146165
147− /* Break down the path to just the app id */
148− QString pathtemp = message.path();
149− if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
150− return false;
151− }
152− pathtemp.remove("/com/canonical/Unity/Launcher/");
153− if (pathtemp.indexOf('/') >= 0) {
166+ if (!validpath) {
154167 return false;
155168 }
156169
157− /* Find ourselves an appid */
158− QString appid = decodeAppId(pathtemp);
159170 int index = m_launcherModel->findApplication(appid);
160171 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
161172
162173 QVariantList retval;
163174 if (message.member() == "Get") {
175+ QString cachedString = message.arguments()[1].toString();
164176 if (!item) {
165177 return false;
166178 }
167− if (message.arguments()[1].toString() == "count") {
179+ if (cachedString == "count") {
168180 retval.append(QVariant::fromValue(QDBusVariant(item->count())));
169− } else if (message.arguments()[1].toString() == "countVisible") {
181+ } else if (cachedString == "countVisible") {
170182 retval.append(QVariant::fromValue(QDBusVariant(item->countVisible())));
183+ } else if (cachedString == "progress") {
184+ retval.append(QVariant::fromValue(QDBusVariant(item->progress())));
171185 }
172186 } else if (message.member() == "Set") {
173− if (message.arguments()[1].toString() == "count") {
187+ QString cachedString = message.arguments()[1].toString();
188+ if (cachedString == "count") {
174189 int newCount = message.arguments()[2].value<QDBusVariant>().variant().toInt();
175190 if (!item || newCount != item->count()) {
176191 Q_EMIT countChanged(appid, newCount);
177192 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "count", QVariant(newCount));
178193 }
179− } else if (message.arguments()[1].toString() == "countVisible") {
194+ } else if (cachedString == "countVisible") {
180195 bool newVisible = message.arguments()[2].value<QDBusVariant>().variant().toBool();
181196 if (!item || newVisible != item->countVisible()) {
182197 Q_EMIT countVisibleChanged(appid, newVisible);
183198 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "countVisible", newVisible);
184199 }
200+ } else if (cachedString == "progress") {
201+ int newProgress = message.arguments()[2].value<QDBusVariant>().variant().toInt();
202+ if (!item || newProgress != item->progress()) {
203+ Q_EMIT progressChanged(appid, newProgress);
204+ notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "progress", QVariant(newProgress));
205+ }
185206 }
186207 } else if (message.member() == "GetAll") {
187208 if (item) {
188209 QVariantMap all;
189210 all.insert("count", item->count());
190211 all.insert("countVisible", item->countVisible());
212+ all.insert("progress", item->progress());
191213 retval.append(all);
192214 }
193215 } else {
plugins/Unity/Launcher/dbusinterface.h+3−1
@@ -27,7 +27,7 @@ class DBusInterface: public UnityDBusVirtualObject
2727 Q_OBJECT
2828public:
2929 DBusInterface(LauncherModel *parent);
30− ~DBusInterface();
30+ ~DBusInterface() = default;
3131
3232 // QDBusVirtualObject implementaition
3333 QString introspect (const QString &path) const override;
@@ -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+78−38
@@ -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
@@ -74,8 +76,6 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
7476 return item->icon();
7577 case RolePinned:
7678 return item->pinned();
77− case RoleRunning:
78− return item->running();
7979 case RoleCount:
8080 return item->count();
8181 case RoleCountVisible:
@@ -84,11 +84,30 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
8484 return item->progress();
8585 case RoleFocused:
8686 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();
8794 }
8895
8996 return QVariant();
9097}
9198
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+
92111unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
93112{
94113 if (index < 0 || index >= m_list.count()) {
@@ -136,7 +155,7 @@ void LauncherModel::pin(const QString &appId, int index)
136155 if (index == -1 || index == currentIndex) {
137156 m_list.at(currentIndex)->setPinned(true);
138157 QModelIndex modelIndex = this->index(currentIndex);
139− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
158+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
140159 } else {
141160 move(currentIndex, index);
142161 // move() will store the list to the backend itself, so just exit at this point.
@@ -149,7 +168,7 @@ void LauncherModel::pin(const QString &appId, int index)
149168
150169 DesktopFileHandler desktopFile(appId);
151170 if (!desktopFile.isValid()) {
152− 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.";
153172 return;
154173 }
155174
@@ -174,7 +193,7 @@ void LauncherModel::requestRemove(const QString &appId)
174193
175194void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
176195{
177− int index = findApplication(appId);
196+ const int index = findApplication(appId);
178197 if (index < 0) {
179198 return;
180199 }
@@ -182,7 +201,7 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
182201 LauncherItem *item = m_list.at(index);
183202 QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
184203 if (model) {
185− QString actionId = model->get(actionIndex).actionId();
204+ const QString actionId = model->get(actionIndex).actionId();
186205
187206 // Check if this is one of the launcher actions we handle ourselves
188207 if (actionId == "pin_item") {
@@ -193,7 +212,10 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
193212 }
194213 } else if (actionId == "launch_item") {
195214 QDesktopServices::openUrl(getUrlForAppId(appId));
196−
215+ } else if (actionId == "stop_item") { // Quit
216+ if (m_appManager) {
217+ m_appManager->stopApplication(appId);
218+ }
197219 // Nope, we don't know this action, let the backend forward it to the application
198220 } else {
199221 // TODO: forward quicklist action to app, possibly via m_dbusIface
@@ -291,7 +313,7 @@ void LauncherModel::storeAppList()
291313
292314void LauncherModel::unpin(const QString &appId)
293315{
294− int index = findApplication(appId);
316+ const int index = findApplication(appId);
295317 if (index < 0) {
296318 return;
297319 }
@@ -300,7 +322,7 @@ void LauncherModel::unpin(const QString &appId)
300322 if (m_list.at(index)->pinned()) {
301323 m_list.at(index)->setPinned(false);
302324 QModelIndex modelIndex = this->index(index);
303− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
325+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
304326 }
305327 } else {
306328 beginRemoveRows(QModelIndex(), index, index);
@@ -322,22 +344,25 @@ int LauncherModel::findApplication(const QString &appId)
322344
323345void LauncherModel::progressChanged(const QString &appId, int progress)
324346{
325− int idx = findApplication(appId);
347+ const int idx = findApplication(appId);
326348 if (idx >= 0) {
327349 LauncherItem *item = m_list.at(idx);
328350 item->setProgress(progress);
329− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
351+ Q_EMIT dataChanged(index(idx), index(idx), {RoleProgress});
330352 }
331353}
332354
333355void LauncherModel::countChanged(const QString &appId, int count)
334356{
335− int idx = findApplication(appId);
357+ const int idx = findApplication(appId);
336358 if (idx >= 0) {
337359 LauncherItem *item = m_list.at(idx);
338360 item->setCount(count);
339− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
361+ if (item->countVisible()) {
362+ setAlerting(item->appId(), true);
363+ }
340364 m_asAdapter->syncItems(m_list);
365+ Q_EMIT dataChanged(index(idx), index(idx), {RoleCount});
341366 }
342367}
343368
@@ -347,7 +372,10 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
347372 if (idx >= 0) {
348373 LauncherItem *item = m_list.at(idx);
349374 item->setCountVisible(countVisible);
350− 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});
351379
352380 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
353381 if (!countVisible && !item->pinned() && !item->recent()) {
@@ -389,7 +417,8 @@ void LauncherModel::refresh()
389417 item->setName(desktopFile.displayName());
390418 item->setIcon(desktopFile.icon());
391419 item->setPinned(item->pinned()); // update pinned text if needed
392− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
420+ item->setRunning(item->running());
421+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleRunning});
393422 }
394423 }
395424
@@ -407,7 +436,7 @@ void LauncherModel::refresh()
407436
408437 // Now walk through settings and see if we need to add something
409438 for (int settingsIndex = 0; settingsIndex < m_settings->storedApplications().count(); ++settingsIndex) {
410− QString entry = m_settings->storedApplications().at(settingsIndex);
439+ const QString entry = m_settings->storedApplications().at(settingsIndex);
411440 int itemIndex = -1;
412441 for (int i = 0; i < m_list.count(); ++i) {
413442 if (m_list.at(i)->appId() == entry) {
@@ -455,6 +484,16 @@ void LauncherModel::refresh()
455484 m_asAdapter->syncItems(m_list);
456485}
457486
487+void LauncherModel::alert(const QString &appId)
488+{
489+ int idx = findApplication(appId);
490+ if (idx >= 0) {
491+ LauncherItem *item = m_list.at(idx);
492+ setAlerting(item->appId(), true);
493+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleAlerting);
494+ }
495+}
496+
458497void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
459498{
460499 Q_UNUSED(parent);
@@ -470,18 +509,14 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
470509 return;
471510 }
472511
473− int itemIndex = findApplication(app->appId());
512+ const int itemIndex = findApplication(app->appId());
474513 if (itemIndex != -1) {
475514 LauncherItem *item = m_list.at(itemIndex);
476− QVector<int> roles;
477515 if (!item->recent()) {
478516 item->setRecent(true);
479− m_asAdapter->syncItems(m_list);
480− roles << RoleRecent;
517+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRecent});
481518 }
482519 item->setRunning(true);
483− roles << RoleRunning;
484− Q_EMIT dataChanged(index(itemIndex), index(itemIndex), roles);
485520 } else {
486521 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
487522 item->setRecent(true);
@@ -491,8 +526,9 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
491526 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
492527 m_list.append(item);
493528 endInsertRows();
494− m_asAdapter->syncItems(m_list);
495529 }
530+ m_asAdapter->syncItems(m_list);
531+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRunning});
496532}
497533
498534void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
@@ -507,31 +543,35 @@ void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
507543 }
508544 }
509545
510− if (appIndex > -1) {
511− if (!m_list.at(appIndex)->pinned()) {
512− beginRemoveRows(QModelIndex(), appIndex, appIndex);
513− m_list.takeAt(appIndex)->deleteLater();
514− endRemoveRows();
515− m_asAdapter->syncItems(m_list);
516− } else {
517− LauncherItem *item = m_list.at(appIndex);
518− item->setRunning(false);
519− Q_EMIT dataChanged(index(appIndex), index(appIndex), QVector<int>() << RoleRunning);
520− }
546+ if (appIndex < 0) {
547+ qWarning() << Q_FUNC_INFO << "appIndex not found";
548+ return;
549+ }
550+
551+ LauncherItem * item = m_list.at(appIndex);
552+ item->setRunning(false);
553+
554+ if (!item->pinned()) {
555+ beginRemoveRows(QModelIndex(), appIndex, appIndex);
556+ m_list.takeAt(appIndex)->deleteLater();
557+ endRemoveRows();
558+ m_asAdapter->syncItems(m_list);
559+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RolePinned});
521560 }
561+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RoleRunning});
522562}
523563
524564void LauncherModel::focusedAppIdChanged()
525565{
526− QString appId = m_appManager->focusedApplicationId();
566+ const QString appId = m_appManager->focusedApplicationId();
527567 for (int i = 0; i < m_list.count(); ++i) {
528568 LauncherItem *item = m_list.at(i);
529569 if (!item->focused() && item->appId() == appId) {
530570 item->setFocused(true);
531− Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
571+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
532572 } else if (item->focused() && item->appId() != appId) {
533573 item->setFocused(false);
534− Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
574+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
535575 }
536576 }
537577}
plugins/Unity/Launcher/launchermodel.h+2
@@ -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();
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/dbusunitysessionservice.cpp+47−55
@@ -18,77 +18,69 @@
1818#include "dbusunitysessionservice.h"
1919
2020// Qt
21−#include <QDBusConnection>
22−#include <QDBusInterface>
21+#include <QDBusPendingCall>
2322
2423DBusUnitySessionService::DBusUnitySessionService()
2524 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
2625{
2726}
2827
29−DBusUnitySessionService::~DBusUnitySessionService()
30−{
31−}
32−
3328void DBusUnitySessionService::Logout()
3429{
35− Q_EMIT logoutReady();
30+ // TODO ask the apps to quit and then emit the signal
31+ Q_EMIT logoutReady();
3632}
3733
3834void DBusUnitySessionService::EndSession()
3935{
40− QDBusConnection connection = QDBusConnection::sessionBus();
41− QDBusInterface iface1 ("com.ubuntu.Upstart",
42− "/com/ubuntu/Upstart",
43− "com.ubuntu.Upstart0_6",
44− connection);
45−
46− iface1.call("EndSession");
36+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.ubuntu.Upstart",
37+ "/com/ubuntu/Upstart",
38+ "com.ubuntu.Upstart0_6",
39+ "EndSession");
40+ QDBusConnection::sessionBus().asyncCall(msg);
4741}
4842
4943void DBusUnitySessionService::RequestLogout()
5044{
51− Q_EMIT logoutRequested(false);
45+ Q_EMIT logoutRequested(false);
5246}
5347
5448void DBusUnitySessionService::Reboot()
5549{
56− QDBusConnection connection = QDBusConnection::systemBus();
57− QDBusInterface iface1 ("org.freedesktop.login1",
58− "/org/freedesktop/login1",
59− "org.freedesktop.login1.Manager",
60− connection);
61−
62− iface1.call("Reboot", false);
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);
6356}
6457
6558void DBusUnitySessionService::RequestReboot()
6659{
67− Q_EMIT rebootRequested(false);
60+ Q_EMIT rebootRequested(false);
6861}
6962
7063void DBusUnitySessionService::Shutdown()
7164{
72− QDBusConnection connection = QDBusConnection::systemBus();
73− QDBusInterface iface1 ("org.freedesktop.login1",
74− "/org/freedesktop/login1",
75− "org.freedesktop.login1.Manager",
76− connection);
77−
78− iface1.call("PowerOff", false);
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);
7971}
8072
8173void DBusUnitySessionService::RequestShutdown()
8274{
83− Q_EMIT shutdownRequested(false);
75+ Q_EMIT shutdownRequested(false);
8476}
8577
8678enum class Action : unsigned
8779{
88− LOGOUT = 0,
89− SHUTDOWN,
90− REBOOT,
91− NONE
80+ LOGOUT = 0,
81+ SHUTDOWN,
82+ REBOOT,
83+ NONE
9284};
9385
9486DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
@@ -96,38 +88,38 @@ DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
9688{
9789}
9890
99−DBusGnomeSessionManagerWrapper::~DBusGnomeSessionManagerWrapper()
91+void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
10092{
93+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
94+ "/com/canonical/Unity/Session",
95+ "com.canonical.Unity.Session",
96+ method);
97+ QDBusConnection::sessionBus().asyncCall(msg);
10198}
10299
103100void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
104101{
105− Q_UNUSED(arg_1);
106− Q_UNUSED(max_wait);
107− Q_UNUSED(inhibitors);
108−
109− QDBusConnection connection = QDBusConnection::sessionBus();
110− QDBusInterface iface1 ("com.canonical.Unity",
111− "/com/canonical/Unity/Session",
112− "com.canonical.Unity.Session",
113− connection);
102+ Q_UNUSED(arg_1);
103+ Q_UNUSED(max_wait);
104+ Q_UNUSED(inhibitors);
114105
115− Action action = (Action)type;
106+ const Action action = (Action)type;
116107
117− switch (action)
118− {
108+ switch (action)
109+ {
119110 case Action::LOGOUT:
120− iface1.call("RequestLogout");
121− break;
111+ performAsyncCall("RequestLogout");
112+ break;
122113
123114 case Action::REBOOT:
124− iface1.call("RequestShutdown");
125− break;
115+ performAsyncCall("RequestReboot");
116+ break;
126117
127118 case Action::SHUTDOWN:
128− break;
119+ performAsyncCall("RequestShutdown");
120+ break;
129121
130122 default:
131− break;
132− }
123+ break;
124+ }
133125}
plugins/Unity/Session/dbusunitysessionservice.h+11−8
@@ -38,7 +38,7 @@ class DBusUnitySessionService : public UnityDBusObject
3838
3939public:
4040 DBusUnitySessionService();
41− ~DBusUnitySessionService();
41+ ~DBusUnitySessionService() = default;
4242
4343 // For use in QML. Javascript doesn't accept functions beginning with capital letters
4444 Q_INVOKABLE void logout() { Logout(); }
@@ -90,7 +90,7 @@ public Q_SLOTS:
9090 /**
9191 * Logout the system.
9292 *
93− * This method directly logout the system without user's confirmation.
93+ * This method directly logs out the system without user's confirmation.
9494 * Ordinary applications should avoid calling this method. Please call
9595 * RequestLogout() to ask the user to decide logout or not.
9696 */
@@ -99,7 +99,7 @@ public Q_SLOTS:
9999 /**
100100 * Reboot the system.
101101 *
102− * This method directly reboot the system without user's confirmation.
102+ * This method directly reboots the system without user's confirmation.
103103 * Ordinary applications should avoid calling this method. Please call
104104 * RequestReboot() to ask the user to decide reboot or not.
105105 */
@@ -108,7 +108,7 @@ public Q_SLOTS:
108108 /**
109109 * Shutdown the system.
110110 *
111− * This method directly shutdown the system without user's confirmation.
111+ * This method directly shuts down the system without user's confirmation.
112112 * Ordinary applications should avoid calling this method. Please call
113113 * RequestShutdown() to ask the user to decide shutdown or not.
114114 */
@@ -117,7 +117,7 @@ public Q_SLOTS:
117117 /**
118118 * Issue a logout request.
119119 *
120− * This method emit the logoutRequested signal to the shell with a boolean
120+ * This method emits the logoutRequested signal to the shell with a boolean
121121 * which indicates if there's any inhibitors. The shell should receive
122122 * this signal and display a dialog to ask the user to confirm the logout
123123 * action. If confirmed, shell can call Logout() method to logout.
@@ -127,7 +127,7 @@ public Q_SLOTS:
127127 /**
128128 * Issue a reboot request.
129129 *
130− * This method emit the rebootRequested signal to the shell with a boolean
130+ * This method emits the rebootRequested signal to the shell with a boolean
131131 * which indicates if there's any inhibitors. The shell should receive
132132 * this signal and display a dialog to ask the user to confirm the reboot
133133 * action. If confirmed, shell can call Reboot() method to reboot
@@ -137,7 +137,7 @@ public Q_SLOTS:
137137 /**
138138 * Issue a shutdown request.
139139 *
140− * This method emit the shutdownRequested signal to the shell with a
140+ * This method emits the shutdownRequested signal to the shell with a
141141 * boolean which indicates if there's any inhibitors.
142142 * The shell should receive
143143 * this signal and display a dialog to ask the user to confirm the reboot
@@ -161,9 +161,12 @@ class DBusGnomeSessionManagerWrapper : public UnityDBusObject
161161
162162public:
163163 DBusGnomeSessionManagerWrapper();
164− ~DBusGnomeSessionManagerWrapper();
164+ ~DBusGnomeSessionManagerWrapper() = default;
165165
166166public Q_SLOTS:
167167 Q_SCRIPTABLE void Open(const unsigned int type, const unsigned int arg_1, const unsigned int max_wait, const QList<QDBusObjectPath> &inhibitors);
168+
169+private:
170+ void performAsyncCall(const QString &method);
168171};
169172#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Wizard/System.cpp+13−9
@@ -16,7 +16,9 @@
1616
1717#include "System.h"
1818
19−#include <QDBusInterface>
19+#include <QDBusPendingCall>
20+#include <QDBusMessage>
21+#include <QDBusConnection>
2022#include <QDBusMetaType>
2123#include <QDir>
2224#include <QFile>
@@ -65,21 +67,23 @@ void System::setWizardEnabled(bool enabled)
6567void System::setSessionVariable(const QString &variable, const QString &value)
6668{
6769 // We need to update both upstart's and DBus's environment
68− QProcess::execute(QString("initctl set-env --global %1=%2").arg(variable, value));
69−
70− QDBusInterface iface("org.freedesktop.DBus",
71− "/org/freedesktop/DBus",
72− "org.freedesktop.DBus",
73− QDBusConnection::sessionBus());
70+ QProcess::startDetached(QStringLiteral("initctl set-env --global %1=%2").arg(variable, value));
7471
7572 QMap<QString,QString> valueMap;
7673 valueMap.insert(variable, value);
77− iface.call("UpdateActivationEnvironment", QVariant::fromValue(valueMap));
74+
75+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus",
76+ "/org/freedesktop/DBus",
77+ "org.freedesktop.DBus",
78+ "UpdateActivationEnvironment");
79+
80+ msg << QVariant::fromValue(valueMap);
81+ QDBusConnection::sessionBus().asyncCall(msg);
7882}
7983
8084void System::updateSessionLanguage(const QString &locale)
8185{
82− QString language = locale.split(".")[0];
86+ const QString language = locale.split(".")[0];
8387
8488 setSessionVariable("LANGUAGE", language);
8589 setSessionVariable("LANG", locale);
po/ar.po+76−61
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-07-14 11:45+0000\n"
11+"PO-Revision-Date: 2015-07-17 12:36+0000\n"
1212"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
1313"Language-Team: Arabic <ar@li.org>\n"
1414"MIME-Version: 1.0\n"
@@ -16,7 +16,7 @@ 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-16 05:44+0000\n"
19+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
2020"X-Generator: Launchpad (build 17628)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
@@ -364,19 +364,19 @@ msgstr "أدخِل الرمز السرّي"
364364#: qml/Greeter/NarrowView.qml:118
365365#, qt-format
366366msgid "Sorry, incorrect %1"
367−msgstr ""
367+msgstr "نأسف، %1 غير صحيحية"
368368
369369#: qml/Greeter/NarrowView.qml:119
370370msgid "Sorry, incorrect passphrase"
371−msgstr ""
371+msgstr "نأسف، عبارة السّر غير صحيحية"
372372
373373#: qml/Greeter/NarrowView.qml:120
374374msgid "Please re-enter"
375−msgstr ""
375+msgstr "برجاء إعادة إدخالها"
376376
377377#: qml/Greeter/NarrowView.qml:121
378378msgid "Sorry, incorrect passcode"
379−msgstr ""
379+msgstr "نأسف، الرمز السري غير صحيح"
380380
381381#: qml/Notifications/NotificationMenuItemFactory.qml:128
382382msgid "Show password"
@@ -384,7 +384,7 @@ msgstr "عرض كلمة السر"
384384
385385#: qml/Panel/ActiveCallHint.qml:79
386386msgid "Tap to return to call..."
387−msgstr ""
387+msgstr "انقر للعودة إلى المكالمة..."
388388
389389#: qml/Panel/ActiveCallHint.qml:92
390390msgid "Conference"
@@ -398,52 +398,64 @@ msgstr ""
398398#, qt-format
399399msgid "%1 hour"
400400msgid_plural "%1 hours"
401−msgstr[0] ""
402−msgstr[1] ""
401+msgstr[0] "أقل من ساعة (%1)"
402+msgstr[1] "ساعة واحدة (%1)"
403+msgstr[2] "ساعتان (%1)"
404+msgstr[3] "%1 ساعات"
405+msgstr[4] "%1 ساعة"
406+msgstr[5] "%1 ساعة"
403407
404408#: qml/Panel/Indicators/MenuItemFactory.qml:772
405409#, qt-format
406410msgid "%1 minute"
407411msgid_plural "%1 minutes"
408−msgstr[0] ""
409−msgstr[1] ""
412+msgstr[0] "أقل من دقيقة (%1)"
413+msgstr[1] "دقيقة واحدة (%1)"
414+msgstr[2] "دقيقتان (%1)"
415+msgstr[3] "%1 دقائق"
416+msgstr[4] "%1 دقيقة"
417+msgstr[5] "%1 دقيقة"
410418
411419#: qml/Panel/Indicators/MenuItemFactory.qml:777
412420#, qt-format
413421msgid "%1 second"
414422msgid_plural "%1 seconds"
415−msgstr[0] ""
416−msgstr[1] ""
423+msgstr[0] "أقل من ثانية (%1)"
424+msgstr[1] "ثانية واحدة (%1)"
425+msgstr[2] "ثانيتان (%1)"
426+msgstr[3] "%1 ثوان"
427+msgstr[4] "%1 ثانية"
428+msgstr[5] "%1 ثانية"
417429
418430#: qml/Panel/Indicators/MenuItemFactory.qml:780
419431msgid "0 seconds"
420−msgstr ""
432+msgstr "أقل من ثانية"
421433
422434#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
423435#: qml/Panel/Indicators/MenuItemFactory.qml:782
424436#, qt-format
425437msgid "%1 remaining"
426−msgstr ""
438+msgstr "باق %1"
427439
428440#: qml/Panel/Indicators/MenuItemFactory.qml:788
429441msgid "In queue…"
430−msgstr ""
442+msgstr "في الانتظار..."
431443
432444#: qml/Panel/Indicators/MenuItemFactory.qml:792
433445msgid "Downloading"
434−msgstr ""
446+msgstr "التنزيل"
435447
436448#: qml/Panel/Indicators/MenuItemFactory.qml:794
437449msgid "Paused, tap to resume"
438−msgstr ""
450+msgstr "مُلبث، انقر للاستئناف"
439451
440452#: qml/Panel/Indicators/MenuItemFactory.qml:796
441453msgid "Canceled"
442−msgstr ""
454+msgstr "تم إلغائه"
443455
444456#: qml/Panel/Indicators/MenuItemFactory.qml:798
445457msgid "Finished"
446−msgstr ""
458+msgstr "انتهى"
447459
448460#: qml/Panel/Indicators/MenuItemFactory.qml:800
449461msgid "Failed, tap to retry"
@@ -460,36 +472,36 @@ msgstr ""
460472
461473#: qml/Panel/Indicators/RoamingIndication.qml:27
462474msgid "Roaming"
463−msgstr ""
475+msgstr "التجوال"
464476
465477#: qml/Tutorial/TutorialBottomFinish.qml:24
466478msgid "This action does different things for different apps"
467−msgstr ""
479+msgstr "هذا الإجراء يقوم بأشياء مختلفة على التطبيقات المختلفة"
468480
469481#: qml/Tutorial/TutorialBottomFinish.qml:25
470482msgid "Tap here to finish."
471−msgstr ""
483+msgstr "انقر هنا للإنهاء."
472484
473485#: qml/Tutorial/TutorialBottom.qml:28
474486msgid "Open special menus"
475−msgstr ""
487+msgstr "فتح قوائم خاصة"
476488
477489#: qml/Tutorial/TutorialBottom.qml:29
478490msgid "Swipe up from the bottom edge."
479−msgstr ""
491+msgstr "اسحب لأعلى من الحافة السفلى."
480492
481493#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
482494#: qml/Tutorial/TutorialRight.qml:144
483495msgid "Tap here to continue."
484−msgstr ""
496+msgstr "انقر هنا للمتابعة."
485497
486498#: qml/Tutorial/TutorialContent.qml:81
487499msgid "Click here to finish."
488−msgstr ""
500+msgstr "انقر هنا للإنهاء."
489501
490502#: qml/Tutorial/TutorialLeftFinish.qml:24
491503msgid "These are the shortcuts to favorite apps"
492−msgstr ""
504+msgstr "هذه اختصارات للتطبيقات المُفضلة"
493505
494506#: qml/Tutorial/TutorialLeft.qml:26
495507msgid "Open the launcher"
@@ -497,7 +509,7 @@ msgstr ""
497509
498510#: qml/Tutorial/TutorialLeft.qml:27
499511msgid "Short swipe from the left edge."
500−msgstr ""
512+msgstr "اسحب سحبة قصيرة من الحافة اليسرى."
501513
502514#: qml/Tutorial/TutorialPage.qml:178
503515msgid "You almost got it!"
@@ -505,7 +517,7 @@ msgstr ""
505517
506518#: qml/Tutorial/TutorialPage.qml:195
507519msgid "Try again."
508−msgstr ""
520+msgstr "حاول مجددًا."
509521
510522#: qml/Tutorial/TutorialRight.qml:31
511523msgid "To view open apps"
@@ -513,138 +525,141 @@ msgstr ""
513525
514526#: qml/Tutorial/TutorialRight.qml:32
515527msgid "Long swipe from the right edge."
516−msgstr ""
528+msgstr "سحبة طويلة من الحافة اليمنى."
517529
518530#: qml/Tutorial/TutorialRight.qml:122
519531msgid "View all your running tasks."
520−msgstr ""
532+msgstr "عرض جميع المهام النشطة."
521533
522534#: qml/Wizard/Page.qml:89
523535msgctxt "Button: Go back one page in the Wizard"
524536msgid "Back"
525−msgstr ""
537+msgstr "عودة"
526538
527539#: qml/Wizard/Pages/here-terms.qml:27
528540msgid "Terms & Conditions"
529−msgstr ""
541+msgstr "الشروط والأحكام"
530542
531543#: qml/Wizard/Pages/passwd-confirm.qml:49
532544msgid "Confirm passphrase"
533−msgstr ""
545+msgstr "تأكيد العبارة السرية"
534546
535547#: qml/Wizard/Pages/passwd-confirm.qml:50
536548msgid "Confirm passcode"
537−msgstr ""
549+msgstr "تأكيد الرمز السري"
538550
539551#: qml/Wizard/Pages/passwd-confirm.qml:53
540552#: qml/Wizard/Pages/passwd-confirm.qml:54
541553msgid "Please try again."
542−msgstr ""
554+msgstr "حاول مجددا."
543555
544556#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
545557#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
546558#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
547559#: qml/Wizard/Pages/60-reporting.qml:50
548560msgid "Continue"
549−msgstr ""
561+msgstr "متابعة"
550562
551563#: qml/Wizard/Pages/passwd-set.qml:61
552564msgid "Choose your passcode"
553−msgstr ""
565+msgstr "اختر الرمز السري خاصتك"
554566
555567#: qml/Wizard/Pages/passwd-set.qml:68
556568msgid "Passphrase must be 4 characters long"
557−msgstr ""
569+msgstr "يجب أن تحتوي العبارة السرية على 4 أحرف على الأقل"
558570
559571#: qml/Wizard/Pages/10-welcome.qml:27
560572msgid "Hi!"
561−msgstr ""
573+msgstr "أهلا!"
562574
563575#: qml/Wizard/Pages/10-welcome.qml:44
564576msgid "Welcome to your Ubuntu phone."
565−msgstr ""
577+msgstr "مرحبا بك في أوبونتو للهواتف."
566578
567579#: qml/Wizard/Pages/10-welcome.qml:52
568580msgid "Let’s get started."
569−msgstr ""
581+msgstr "هيا بنا نبدأ."
570582
571583#: qml/Wizard/Pages/20-sim.qml:25
572584msgid "Add a SIM card and restart your device"
573−msgstr ""
585+msgstr "أدخِل شريحة SIM ثم أعِد تشغيل الجهاز"
574586
575587#: qml/Wizard/Pages/20-sim.qml:59
576588msgid "Without it, you won’t be able to make calls or use text messaging."
577589msgstr ""
590+"بدونها لن تتمكن من إجراء أي مكالمات هاتفية أو استخدام الرسائل النصية."
578591
579592#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
580593msgid "Skip"
581−msgstr ""
594+msgstr "تخطي"
582595
583596#: qml/Wizard/Pages/30-passwd-type.qml:39
584597msgid "Lock security"
585−msgstr ""
598+msgstr "قِفل الأمان"
586599
587600#: qml/Wizard/Pages/30-passwd-type.qml:74
588601msgid "Please select how you’d like to unlock your phone."
589−msgstr ""
602+msgstr "رجاءً اختر الطريقة التي ترغب بأن توصد هاتفك بها."
590603
591604#: qml/Wizard/Pages/30-passwd-type.qml:97
592605msgctxt "Label: Type of security method"
593606msgid "Swipe"
594−msgstr ""
607+msgstr "سحبة"
595608
596609#: qml/Wizard/Pages/30-passwd-type.qml:98
597610msgctxt "Label: Description of security method"
598611msgid "No security"
599−msgstr ""
612+msgstr "بدون أمان"
600613
601614#: qml/Wizard/Pages/30-passwd-type.qml:100
602615msgctxt "Label: Type of security method"
603616msgid "Passcode"
604−msgstr ""
617+msgstr "رمز سري"
605618
606619#: qml/Wizard/Pages/30-passwd-type.qml:101
607620msgctxt "Label: Description of security method"
608621msgid "4 digits only"
609−msgstr ""
622+msgstr "4 أرقام فقط"
610623
611624#: qml/Wizard/Pages/30-passwd-type.qml:103
612625msgctxt "Label: Type of security method"
613626msgid "Passphrase"
614−msgstr ""
627+msgstr "عبارة سرية"
615628
616629#: qml/Wizard/Pages/30-passwd-type.qml:104
617630msgctxt "Label: Description of security method"
618631msgid "Numbers and letters"
619−msgstr ""
632+msgstr "أرقام وحروف"
620633
621634#: qml/Wizard/Pages/40-wifi.qml:29
622635msgid "Connect to Wi‑Fi"
623−msgstr ""
636+msgstr "اتصل بالواي فاي"
624637
625638#: qml/Wizard/Pages/40-wifi.qml:168
626639msgid "Available networks…"
627−msgstr ""
640+msgstr "الشبكات المتاحة..."
628641
629642#: qml/Wizard/Pages/40-wifi.qml:169
630643msgid "No available networks."
631−msgstr ""
644+msgstr "لا توجد شبكات متاحة."
632645
633646#: qml/Wizard/Pages/50-location.qml:27
634647msgid "Location"
635−msgstr ""
648+msgstr "الموقع"
636649
637650#: qml/Wizard/Pages/50-location.qml:62
638651msgid "Let the phone detect your location:"
639−msgstr ""
652+msgstr "دع هاتفك يحدد موقعك:"
640653
641654#: qml/Wizard/Pages/50-location.qml:69
642655msgid "Using GPS only (less accurate)"
643−msgstr ""
656+msgstr "باستخدام نظام تحديد المواقع (GPS) فقط (أقل دقّة)"
644657
645658#: qml/Wizard/Pages/50-location.qml:86
646659msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
647660msgstr ""
661+"باستخدام نظام تحديد المواقع (GPS)، والواي فاي، وبيانات الشبكة الخلوية "
662+"(مستحسن)"
648663
649664#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
650665#: qml/Wizard/Pages/50-location.qml:103
@@ -659,11 +674,11 @@ msgstr ""
659674
660675#: qml/Wizard/Pages/50-location.qml:124
661676msgid "You can change your mind later in <b>System Settings</b>."
662−msgstr ""
677+msgstr "يمكنك تغيير رأيك في وقت لاحق عن طريق <b>إعدادات النظام</b>."
663678
664679#: qml/Wizard/Pages/60-reporting.qml:24
665680msgid "Improving your experience"
666−msgstr ""
681+msgstr "تحسين تجربتك"
667682
668683#: qml/Wizard/Pages/60-reporting.qml:36
669684msgid ""
po/ga.ponew file+697
@@ -0,0 +1,697 @@
1+# Irish translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11+"PO-Revision-Date: 2015-07-20 16:25+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Irish <ga@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
18+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
19+"X-Generator: Launchpad (build 17628)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr "Focal Faire: "
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
35+#. This format string is used for showing, on a 12-hour clock, times that happen yesterday.
36+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
37+#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
38+#: plugins/Utils/relativetimeformatter.cpp:157
39+msgid "Yesterday %l:%M %p"
40+msgstr ""
41+
42+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
43+#. This format string is used for showing, on a 12-hour clock, times that happened today.
44+#. en_US example: "%l:%M %p" --> "1:00 PM"
45+#: plugins/Utils/relativetimeformatter.cpp:164
46+msgid "%l:%M %p"
47+msgstr ""
48+
49+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
50+#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
51+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
52+#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
53+#: plugins/Utils/relativetimeformatter.cpp:172
54+msgid "Tomorrow %l:%M %p"
55+msgstr ""
56+
57+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
58+#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
59+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
60+#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
61+#: plugins/Utils/relativetimeformatter.cpp:181
62+msgid "%a %l:%M %p"
63+msgstr ""
64+
65+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
66+#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
67+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
68+#. en_US example: "%a %d %b\u2003%l:%M %p" --> "Fri Oct 31 1:00 PM"
69+#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM"
70+#: plugins/Utils/relativetimeformatter.cpp:190
71+msgid "%a %d %b %l:%M %p"
72+msgstr ""
73+
74+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
75+#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
76+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
77+#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
78+#: plugins/Utils/relativetimeformatter.cpp:201
79+msgid "Yesterday %H:%M"
80+msgstr ""
81+
82+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
83+#. This format string is used for showing, on a 24-hour clock, times that happened today.
84+#. en_US example: "%H:%M" --> "13:00"
85+#: plugins/Utils/relativetimeformatter.cpp:208
86+msgid "%H:%M"
87+msgstr ""
88+
89+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
90+#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
91+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
92+#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
93+#: plugins/Utils/relativetimeformatter.cpp:216
94+msgid "Tomorrow %H:%M"
95+msgstr ""
96+
97+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
98+#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
99+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
100+#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
101+#: plugins/Utils/relativetimeformatter.cpp:225
102+msgid "%a %H:%M"
103+msgstr ""
104+
105+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
106+#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
107+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
108+#. en_US example: "%a %d %b\u2003%H:%M" --> "Fri Oct 31 13:00"
109+#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
110+#: plugins/Utils/relativetimeformatter.cpp:234
111+msgid "%a %d %b %H:%M"
112+msgstr ""
113+
114+#: qml/Components/DelayedLockscreen.qml:49
115+msgid "Device Locked"
116+msgstr ""
117+
118+#: qml/Components/DelayedLockscreen.qml:64
119+msgid "You have been locked out due to too many failed passphrase attempts."
120+msgstr ""
121+
122+#: qml/Components/DelayedLockscreen.qml:65
123+msgid "You have been locked out due to too many failed passcode attempts."
124+msgstr ""
125+
126+#: qml/Components/DelayedLockscreen.qml:74
127+#, qt-format
128+msgid "Please wait %1 minute and then try again…"
129+msgid_plural "Please wait %1 minutes and then try again…"
130+msgstr[0] ""
131+msgstr[1] ""
132+
133+#: qml/Components/Dialogs.qml:69
134+msgctxt "Title: Lock/Log out dialog"
135+msgid "Log out"
136+msgstr ""
137+
138+#: qml/Components/Dialogs.qml:70
139+msgid "Are you sure you want to log out?"
140+msgstr ""
141+
142+#: qml/Components/Dialogs.qml:72
143+msgctxt "Button: Lock the system"
144+msgid "Lock"
145+msgstr ""
146+
147+#: qml/Components/Dialogs.qml:79
148+msgctxt "Button: Log out from the system"
149+msgid "Log Out"
150+msgstr ""
151+
152+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153+#: qml/Components/Dialogs.qml:173
154+msgid "Cancel"
155+msgstr ""
156+
157+#: qml/Components/Dialogs.qml:98
158+msgctxt "Title: Reboot/Shut down dialog"
159+msgid "Shut down"
160+msgstr ""
161+
162+#: qml/Components/Dialogs.qml:99
163+msgid "Are you sure you want to shut down?"
164+msgstr ""
165+
166+#: qml/Components/Dialogs.qml:101
167+msgctxt "Button: Reboot the system"
168+msgid "Reboot"
169+msgstr ""
170+
171+#: qml/Components/Dialogs.qml:109
172+msgctxt "Button: Shut down the system"
173+msgid "Shut down"
174+msgstr ""
175+
176+#: qml/Components/Dialogs.qml:129
177+msgctxt "Title: Reboot dialog"
178+msgid "Reboot"
179+msgstr ""
180+
181+#: qml/Components/Dialogs.qml:130
182+msgid "Are you sure you want to reboot?"
183+msgstr ""
184+
185+#: qml/Components/Dialogs.qml:132
186+msgid "No"
187+msgstr ""
188+
189+#: qml/Components/Dialogs.qml:138
190+msgid "Yes"
191+msgstr ""
192+
193+#: qml/Components/Dialogs.qml:152
194+msgctxt "Title: Power off/Restart dialog"
195+msgid "Power"
196+msgstr ""
197+
198+#: qml/Components/Dialogs.qml:153
199+msgid ""
200+"Are you sure you would like\n"
201+"to power off?"
202+msgstr ""
203+
204+#: qml/Components/Dialogs.qml:155
205+msgctxt "Button: Power off the system"
206+msgid "Power off"
207+msgstr ""
208+
209+#: qml/Components/Dialogs.qml:164
210+msgctxt "Button: Restart the system"
211+msgid "Restart"
212+msgstr ""
213+
214+#: qml/Components/Lockscreen.qml:245
215+msgid "Return to Call"
216+msgstr ""
217+
218+#: qml/Components/Lockscreen.qml:245
219+msgid "Emergency Call"
220+msgstr ""
221+
222+#: qml/Components/Lockscreen.qml:277
223+msgid "OK"
224+msgstr ""
225+
226+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
227+msgid "See less"
228+msgstr ""
229+
230+#: qml/Dash/GenericScopeView.qml:532
231+msgid "See all"
232+msgstr ""
233+
234+#: qml/Dash/GenericScopeView.qml:594
235+msgctxt "Label: Hint for dash search line edit"
236+msgid "Search"
237+msgstr ""
238+
239+#: qml/Dash/PageHeader.qml:269
240+msgctxt "Button: Open the Ubuntu Store"
241+msgid "Store"
242+msgstr ""
243+
244+#: qml/Dash/PageHeader.qml:276
245+msgctxt "Button: Start a search in the current dash scope"
246+msgid "Search"
247+msgstr ""
248+
249+#: qml/Dash/PageHeader.qml:286
250+msgctxt "Button: Show the current dash scope settings"
251+msgid "Settings"
252+msgstr ""
253+
254+#: qml/Dash/PageHeader.qml:293
255+msgid "Remove from Favorites"
256+msgstr ""
257+
258+#: qml/Dash/PageHeader.qml:293
259+msgid "Add to Favorites"
260+msgstr ""
261+
262+#: qml/Dash/Previews/PreviewActionCombo.qml:35
263+msgid "More..."
264+msgstr ""
265+
266+#: qml/Dash/Previews/PreviewActionCombo.qml:35
267+msgid "Less..."
268+msgstr ""
269+
270+#: qml/Dash/Previews/PreviewRatingInput.qml:84
271+msgid "Rate this"
272+msgstr ""
273+
274+#: qml/Dash/Previews/PreviewRatingInput.qml:129
275+msgid "Add a review"
276+msgstr ""
277+
278+#: qml/Dash/Previews/PreviewRatingInput.qml:169
279+msgid "Send"
280+msgstr ""
281+
282+#: qml/Dash/PullToRefreshScopeStyle.qml:55
283+msgid "Pull to refresh…"
284+msgstr ""
285+
286+#: qml/Dash/PullToRefreshScopeStyle.qml:60
287+msgid "Release to refresh…"
288+msgstr ""
289+
290+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
291+msgid "Enable location data"
292+msgstr ""
293+
294+#: qml/Dash/ScopesList.qml:67
295+msgid "Manage"
296+msgstr ""
297+
298+#: qml/Dash/ScopesList.qml:113
299+msgid "Home"
300+msgstr ""
301+
302+#: qml/Dash/ScopesList.qml:114
303+msgid "Also installed"
304+msgstr ""
305+
306+#: qml/Greeter/CoverPage.qml:107
307+msgid "Unlock"
308+msgstr ""
309+
310+#: qml/Greeter/LoginList.qml:236
311+msgid "Retry"
312+msgstr ""
313+
314+#: qml/Greeter/LoginList.qml:237
315+msgid "Tap to unlock"
316+msgstr ""
317+
318+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
319+msgid "Sorry, incorrect passphrase."
320+msgstr ""
321+
322+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
323+msgid "Sorry, incorrect passcode."
324+msgstr ""
325+
326+#: qml/Greeter/NarrowView.qml:55
327+msgid "This will be your last attempt."
328+msgstr ""
329+
330+#: qml/Greeter/NarrowView.qml:57
331+msgid ""
332+"If passphrase is entered incorrectly, your phone will conduct a factory "
333+"reset and all personal data will be deleted."
334+msgstr ""
335+
336+#: qml/Greeter/NarrowView.qml:58
337+msgid ""
338+"If passcode is entered incorrectly, your phone will conduct a factory reset "
339+"and all personal data will be deleted."
340+msgstr ""
341+
342+#: qml/Greeter/NarrowView.qml:115
343+#, qt-format
344+msgid "Enter %1"
345+msgstr ""
346+
347+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
348+msgid "Enter passphrase"
349+msgstr ""
350+
351+#: qml/Greeter/NarrowView.qml:117
352+msgid "Enter passcode"
353+msgstr ""
354+
355+#: qml/Greeter/NarrowView.qml:118
356+#, qt-format
357+msgid "Sorry, incorrect %1"
358+msgstr ""
359+
360+#: qml/Greeter/NarrowView.qml:119
361+msgid "Sorry, incorrect passphrase"
362+msgstr ""
363+
364+#: qml/Greeter/NarrowView.qml:120
365+msgid "Please re-enter"
366+msgstr ""
367+
368+#: qml/Greeter/NarrowView.qml:121
369+msgid "Sorry, incorrect passcode"
370+msgstr ""
371+
372+#: qml/Notifications/NotificationMenuItemFactory.qml:128
373+msgid "Show password"
374+msgstr ""
375+
376+#: qml/Panel/ActiveCallHint.qml:79
377+msgid "Tap to return to call..."
378+msgstr ""
379+
380+#: qml/Panel/ActiveCallHint.qml:92
381+msgid "Conference"
382+msgstr ""
383+
384+#: qml/Panel/Indicators/MenuItemFactory.qml:639
385+msgid "Nothing is playing"
386+msgstr ""
387+
388+#: qml/Panel/Indicators/MenuItemFactory.qml:768
389+#, qt-format
390+msgid "%1 hour"
391+msgid_plural "%1 hours"
392+msgstr[0] ""
393+msgstr[1] ""
394+
395+#: qml/Panel/Indicators/MenuItemFactory.qml:772
396+#, qt-format
397+msgid "%1 minute"
398+msgid_plural "%1 minutes"
399+msgstr[0] ""
400+msgstr[1] ""
401+
402+#: qml/Panel/Indicators/MenuItemFactory.qml:777
403+#, qt-format
404+msgid "%1 second"
405+msgid_plural "%1 seconds"
406+msgstr[0] ""
407+msgstr[1] ""
408+
409+#: qml/Panel/Indicators/MenuItemFactory.qml:780
410+msgid "0 seconds"
411+msgstr ""
412+
413+#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
414+#: qml/Panel/Indicators/MenuItemFactory.qml:782
415+#, qt-format
416+msgid "%1 remaining"
417+msgstr ""
418+
419+#: qml/Panel/Indicators/MenuItemFactory.qml:788
420+msgid "In queue…"
421+msgstr ""
422+
423+#: qml/Panel/Indicators/MenuItemFactory.qml:792
424+msgid "Downloading"
425+msgstr ""
426+
427+#: qml/Panel/Indicators/MenuItemFactory.qml:794
428+msgid "Paused, tap to resume"
429+msgstr ""
430+
431+#: qml/Panel/Indicators/MenuItemFactory.qml:796
432+msgid "Canceled"
433+msgstr ""
434+
435+#: qml/Panel/Indicators/MenuItemFactory.qml:798
436+msgid "Finished"
437+msgstr ""
438+
439+#: qml/Panel/Indicators/MenuItemFactory.qml:800
440+msgid "Failed, tap to retry"
441+msgstr ""
442+
443+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
444+msgctxt "Label: Hint in message indicator line edit"
445+msgid "Reply"
446+msgstr ""
447+
448+#: qml/Panel/Indicators/ModemInfoItem.qml:105
449+msgid "Unlock SIM"
450+msgstr ""
451+
452+#: qml/Panel/Indicators/RoamingIndication.qml:27
453+msgid "Roaming"
454+msgstr ""
455+
456+#: qml/Tutorial/TutorialBottomFinish.qml:24
457+msgid "This action does different things for different apps"
458+msgstr ""
459+
460+#: qml/Tutorial/TutorialBottomFinish.qml:25
461+msgid "Tap here to finish."
462+msgstr ""
463+
464+#: qml/Tutorial/TutorialBottom.qml:28
465+msgid "Open special menus"
466+msgstr ""
467+
468+#: qml/Tutorial/TutorialBottom.qml:29
469+msgid "Swipe up from the bottom edge."
470+msgstr ""
471+
472+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
473+#: qml/Tutorial/TutorialRight.qml:144
474+msgid "Tap here to continue."
475+msgstr ""
476+
477+#: qml/Tutorial/TutorialContent.qml:81
478+msgid "Click here to finish."
479+msgstr ""
480+
481+#: qml/Tutorial/TutorialLeftFinish.qml:24
482+msgid "These are the shortcuts to favorite apps"
483+msgstr ""
484+
485+#: qml/Tutorial/TutorialLeft.qml:26
486+msgid "Open the launcher"
487+msgstr ""
488+
489+#: qml/Tutorial/TutorialLeft.qml:27
490+msgid "Short swipe from the left edge."
491+msgstr ""
492+
493+#: qml/Tutorial/TutorialPage.qml:178
494+msgid "You almost got it!"
495+msgstr ""
496+
497+#: qml/Tutorial/TutorialPage.qml:195
498+msgid "Try again."
499+msgstr ""
500+
501+#: qml/Tutorial/TutorialRight.qml:31
502+msgid "To view open apps"
503+msgstr ""
504+
505+#: qml/Tutorial/TutorialRight.qml:32
506+msgid "Long swipe from the right edge."
507+msgstr ""
508+
509+#: qml/Tutorial/TutorialRight.qml:122
510+msgid "View all your running tasks."
511+msgstr ""
512+
513+#: qml/Wizard/Page.qml:89
514+msgctxt "Button: Go back one page in the Wizard"
515+msgid "Back"
516+msgstr ""
517+
518+#: qml/Wizard/Pages/here-terms.qml:27
519+msgid "Terms & Conditions"
520+msgstr ""
521+
522+#: qml/Wizard/Pages/passwd-confirm.qml:49
523+msgid "Confirm passphrase"
524+msgstr ""
525+
526+#: qml/Wizard/Pages/passwd-confirm.qml:50
527+msgid "Confirm passcode"
528+msgstr ""
529+
530+#: qml/Wizard/Pages/passwd-confirm.qml:53
531+#: qml/Wizard/Pages/passwd-confirm.qml:54
532+msgid "Please try again."
533+msgstr ""
534+
535+#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
536+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
537+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
538+#: qml/Wizard/Pages/60-reporting.qml:50
539+msgid "Continue"
540+msgstr ""
541+
542+#: qml/Wizard/Pages/passwd-set.qml:61
543+msgid "Choose your passcode"
544+msgstr ""
545+
546+#: qml/Wizard/Pages/passwd-set.qml:68
547+msgid "Passphrase must be 4 characters long"
548+msgstr ""
549+
550+#: qml/Wizard/Pages/10-welcome.qml:27
551+msgid "Hi!"
552+msgstr ""
553+
554+#: qml/Wizard/Pages/10-welcome.qml:44
555+msgid "Welcome to your Ubuntu phone."
556+msgstr ""
557+
558+#: qml/Wizard/Pages/10-welcome.qml:52
559+msgid "Let’s get started."
560+msgstr ""
561+
562+#: qml/Wizard/Pages/20-sim.qml:25
563+msgid "Add a SIM card and restart your device"
564+msgstr ""
565+
566+#: qml/Wizard/Pages/20-sim.qml:59
567+msgid "Without it, you won’t be able to make calls or use text messaging."
568+msgstr ""
569+
570+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
571+msgid "Skip"
572+msgstr ""
573+
574+#: qml/Wizard/Pages/30-passwd-type.qml:39
575+msgid "Lock security"
576+msgstr ""
577+
578+#: qml/Wizard/Pages/30-passwd-type.qml:74
579+msgid "Please select how you’d like to unlock your phone."
580+msgstr ""
581+
582+#: qml/Wizard/Pages/30-passwd-type.qml:97
583+msgctxt "Label: Type of security method"
584+msgid "Swipe"
585+msgstr ""
586+
587+#: qml/Wizard/Pages/30-passwd-type.qml:98
588+msgctxt "Label: Description of security method"
589+msgid "No security"
590+msgstr ""
591+
592+#: qml/Wizard/Pages/30-passwd-type.qml:100
593+msgctxt "Label: Type of security method"
594+msgid "Passcode"
595+msgstr ""
596+
597+#: qml/Wizard/Pages/30-passwd-type.qml:101
598+msgctxt "Label: Description of security method"
599+msgid "4 digits only"
600+msgstr ""
601+
602+#: qml/Wizard/Pages/30-passwd-type.qml:103
603+msgctxt "Label: Type of security method"
604+msgid "Passphrase"
605+msgstr ""
606+
607+#: qml/Wizard/Pages/30-passwd-type.qml:104
608+msgctxt "Label: Description of security method"
609+msgid "Numbers and letters"
610+msgstr ""
611+
612+#: qml/Wizard/Pages/40-wifi.qml:29
613+msgid "Connect to Wi‑Fi"
614+msgstr ""
615+
616+#: qml/Wizard/Pages/40-wifi.qml:168
617+msgid "Available networks…"
618+msgstr ""
619+
620+#: qml/Wizard/Pages/40-wifi.qml:169
621+msgid "No available networks."
622+msgstr ""
623+
624+#: qml/Wizard/Pages/50-location.qml:27
625+msgid "Location"
626+msgstr ""
627+
628+#: qml/Wizard/Pages/50-location.qml:62
629+msgid "Let the phone detect your location:"
630+msgstr ""
631+
632+#: qml/Wizard/Pages/50-location.qml:69
633+msgid "Using GPS only (less accurate)"
634+msgstr ""
635+
636+#: qml/Wizard/Pages/50-location.qml:86
637+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
638+msgstr ""
639+
640+#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
641+#: qml/Wizard/Pages/50-location.qml:103
642+msgid ""
643+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
644+"conditions</a>."
645+msgstr ""
646+
647+#: qml/Wizard/Pages/50-location.qml:112
648+msgid "Not at all"
649+msgstr ""
650+
651+#: qml/Wizard/Pages/50-location.qml:124
652+msgid "You can change your mind later in <b>System Settings</b>."
653+msgstr ""
654+
655+#: qml/Wizard/Pages/60-reporting.qml:24
656+msgid "Improving your experience"
657+msgstr ""
658+
659+#: qml/Wizard/Pages/60-reporting.qml:36
660+msgid ""
661+"Your phone is set up to automatically report errors to Canonical and its "
662+"partners, the makers of the operating system."
663+msgstr ""
664+
665+#: qml/Wizard/Pages/60-reporting.qml:43
666+msgid ""
667+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
668+"Privacy</b>"
669+msgstr ""
670+
671+#: qml/Wizard/Pages/80-finished.qml:24
672+msgid "All done"
673+msgstr ""
674+
675+#: qml/Wizard/Pages/80-finished.qml:39
676+msgid "Nice work!"
677+msgstr ""
678+
679+#: qml/Wizard/Pages/80-finished.qml:46
680+msgid "Your phone is now ready to use."
681+msgstr ""
682+
683+#: qml/Wizard/Pages/80-finished.qml:53
684+msgid "Finish"
685+msgstr ""
686+
687+#. Translators: This is the arrow for "Back" buttons
688+#: qml/Wizard/StackButton.qml:39
689+#, qt-format
690+msgid "〈 %1"
691+msgstr ""
692+
693+#. Translators: This is the arrow for "Forward" buttons
694+#: qml/Wizard/StackButton.qml:42
695+#, qt-format
696+msgid "%1 〉"
697+msgstr ""
po/gd.po+29−23
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-05-01 16:39+0000\n"
11+"PO-Revision-Date: 2015-07-16 10:56+0000\n"
1212"Last-Translator: GunChleoc <Unknown>\n"
1313"Language-Team: Fòram na Gàidhlig\n"
1414"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
1818"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-07-04 05:37+0000\n"
20−"X-Generator: Launchpad (build 17597)\n"
19+"X-Launchpad-Export-Date: 2015-07-17 05:37+0000\n"
20+"X-Generator: Launchpad (build 17628)\n"
2121"Language: gd\n"
2222
2323#: plugins/LightDM/Greeter.cpp:112
@@ -39,14 +39,14 @@ msgstr "Neo-phrìnich an ath-ghoirid"
3939#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
4040#: plugins/Utils/relativetimeformatter.cpp:157
4141msgid "Yesterday %l:%M %p"
42−msgstr ""
42+msgstr "An-dè aig %l:%M %p"
4343
4444#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
4545#. This format string is used for showing, on a 12-hour clock, times that happened today.
4646#. en_US example: "%l:%M %p" --> "1:00 PM"
4747#: plugins/Utils/relativetimeformatter.cpp:164
4848msgid "%l:%M %p"
49−msgstr ""
49+msgstr "%l:%M %p"
5050
5151#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
5252#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
@@ -54,7 +54,7 @@ msgstr ""
5454#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
5555#: plugins/Utils/relativetimeformatter.cpp:172
5656msgid "Tomorrow %l:%M %p"
57−msgstr ""
57+msgstr "A-màireach aig %l:%M %p"
5858
5959#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
6060#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
@@ -62,7 +62,7 @@ msgstr ""
6262#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
6363#: plugins/Utils/relativetimeformatter.cpp:181
6464msgid "%a %l:%M %p"
65−msgstr ""
65+msgstr "%a %l:%M %p"
6666
6767#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
6868#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
@@ -71,7 +71,7 @@ msgstr ""
7171#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM"
7272#: plugins/Utils/relativetimeformatter.cpp:190
7373msgid "%a %d %b %l:%M %p"
74−msgstr ""
74+msgstr "%a %b %d %l:%M %p"
7575
7676#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
7777#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
@@ -79,14 +79,14 @@ msgstr ""
7979#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
8080#: plugins/Utils/relativetimeformatter.cpp:201
8181msgid "Yesterday %H:%M"
82−msgstr ""
82+msgstr "An-dè aig %H:%M"
8383
8484#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
8585#. This format string is used for showing, on a 24-hour clock, times that happened today.
8686#. en_US example: "%H:%M" --> "13:00"
8787#: plugins/Utils/relativetimeformatter.cpp:208
8888msgid "%H:%M"
89−msgstr ""
89+msgstr "%H:%M"
9090
9191#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
9292#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
@@ -94,7 +94,7 @@ msgstr ""
9494#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
9595#: plugins/Utils/relativetimeformatter.cpp:216
9696msgid "Tomorrow %H:%M"
97−msgstr ""
97+msgstr "A-màireach aig %H:%M"
9898
9999#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
100100#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
@@ -102,7 +102,7 @@ msgstr ""
102102#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
103103#: plugins/Utils/relativetimeformatter.cpp:225
104104msgid "%a %H:%M"
105−msgstr ""
105+msgstr "%a %H:%M"
106106
107107#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
108108#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
@@ -111,7 +111,7 @@ msgstr ""
111111#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
112112#: plugins/Utils/relativetimeformatter.cpp:234
113113msgid "%a %d %b %H:%M"
114−msgstr ""
114+msgstr "%a %b %d %H:%M"
115115
116116#: qml/Components/DelayedLockscreen.qml:49
117117msgid "Device Locked"
@@ -405,32 +405,38 @@ msgstr "Chan eil dad 'ga chluich"
405405#, qt-format
406406msgid "%1 hour"
407407msgid_plural "%1 hours"
408−msgstr[0] ""
409−msgstr[1] ""
408+msgstr[0] "%1 uair a thìde"
409+msgstr[1] "%1 uair a thìde"
410+msgstr[2] "%1 uairean a thìde"
411+msgstr[3] "%1 uair a thìde"
410412
411413#: qml/Panel/Indicators/MenuItemFactory.qml:772
412414#, qt-format
413415msgid "%1 minute"
414416msgid_plural "%1 minutes"
415−msgstr[0] ""
416−msgstr[1] ""
417+msgstr[0] "%1 mhionaid"
418+msgstr[1] "%1 mhionaid"
419+msgstr[2] "%1 mionaidean"
420+msgstr[3] "%1 mionaid"
417421
418422#: qml/Panel/Indicators/MenuItemFactory.qml:777
419423#, qt-format
420424msgid "%1 second"
421425msgid_plural "%1 seconds"
422−msgstr[0] ""
423−msgstr[1] ""
426+msgstr[0] "%1 diog"
427+msgstr[1] "%1 dhiog"
428+msgstr[2] "%1 diogan"
429+msgstr[3] "%1 diog"
424430
425431#: qml/Panel/Indicators/MenuItemFactory.qml:780
426432msgid "0 seconds"
427−msgstr ""
433+msgstr "0 diog"
428434
429435#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
430436#: qml/Panel/Indicators/MenuItemFactory.qml:782
431437#, qt-format
432438msgid "%1 remaining"
433−msgstr ""
439+msgstr "%1 air fhàgail"
434440
435441#: qml/Panel/Indicators/MenuItemFactory.qml:788
436442msgid "In queue…"
@@ -492,7 +498,7 @@ msgstr "Thoir gnogag an-seo gus leantainn air adhart."
492498
493499#: qml/Tutorial/TutorialContent.qml:81
494500msgid "Click here to finish."
495−msgstr ""
501+msgstr "Briog an-seo gus crìoch a chur air."
496502
497503#: qml/Tutorial/TutorialLeftFinish.qml:24
498504msgid "These are the shortcuts to favorite apps"
@@ -613,7 +619,7 @@ msgstr "Còd-faire"
613619#: qml/Wizard/Pages/30-passwd-type.qml:101
614620msgctxt "Label: Description of security method"
615621msgid "4 digits only"
616−msgstr ""
622+msgstr "4 àireamhan a-mhàin"
617623
618624#: qml/Wizard/Pages/30-passwd-type.qml:103
619625msgctxt "Label: Type of security method"
po/pt.po+10−10
@@ -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-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-05-19 03:42+0000\n"
12−"Last-Translator: António Miranda <Unknown>\n"
11+"PO-Revision-Date: 2015-07-16 12:23+0000\n"
12+"Last-Translator: João Fernandes <Unknown>\n"
1313"Language-Team: Portuguese <pt@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-04 05:37+0000\n"
19−"X-Generator: Launchpad (build 17597)\n"
18+"X-Launchpad-Export-Date: 2015-07-17 05:37+0000\n"
19+"X-Generator: Launchpad (build 17628)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -37,7 +37,7 @@ msgstr "Remover atalho"
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 "Ontem %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.
@@ -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 "Amanhã %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.
@@ -77,7 +77,7 @@ 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 "Ontem %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.
@@ -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 "Amanhã %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.
@@ -399,8 +399,8 @@ msgstr "Nada está a ser tocado"
399399#, qt-format
400400msgid "%1 hour"
401401msgid_plural "%1 hours"
402−msgstr[0] ""
403−msgstr[1] ""
402+msgstr[0] "%1 hora"
403+msgstr[1] "%1 horas"
404404
405405#: qml/Panel/Indicators/MenuItemFactory.qml:772
406406#, qt-format
po/sw.po+86−80
@@ -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-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-07-10 20:22+0000\n"
11+"PO-Revision-Date: 2015-07-17 20:02+0000\n"
1212"Last-Translator: Valentine <mwangi_valentino@yahoo.com>\n"
1313"Language-Team: Swahili <sw@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-11 05:33+0000\n"
19−"X-Generator: Launchpad (build 17620)\n"
18+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
19+"X-Generator: Launchpad (build 17628)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -25,11 +25,11 @@ msgstr "Neno siri "
2525#: plugins/Unity/Launcher/launcheritem.cpp:45
2626#: plugins/Unity/Launcher/launcheritem.cpp:100
2727msgid "Pin shortcut"
28−msgstr ""
28+msgstr "Njia ya mkato ya nambari ya siri"
2929
3030#: plugins/Unity/Launcher/launcheritem.cpp:100
3131msgid "Unpin shortcut"
32−msgstr ""
32+msgstr "Toa njia ya mkato ya nambari ya siri"
3333
3434#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
3535#. This format string is used for showing, on a 12-hour clock, times that happen yesterday.
@@ -60,7 +60,7 @@ msgstr "Kesho %l:%M %p"
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.
@@ -118,10 +118,12 @@ msgstr "kifaa kimefungwa"
118118#: qml/Components/DelayedLockscreen.qml:64
119119msgid "You have been locked out due to too many failed passphrase attempts."
120120msgstr ""
121+"Huwezi kulogi kwa sababu umenoa kubonyeza vizuri neno la siri mara nyingi."
121122
122123#: qml/Components/DelayedLockscreen.qml:65
123124msgid "You have been locked out due to too many failed passcode attempts."
124125msgstr ""
126+"Huwezi kulogi kwa sababu umenoa kubonyeza vizuri neno la siri mara nyingi."
125127
126128#: qml/Components/DelayedLockscreen.qml:74
127129#, qt-format
@@ -193,7 +195,7 @@ msgstr "Ndio"
193195#: qml/Components/Dialogs.qml:152
194196msgctxt "Title: Power off/Restart dialog"
195197msgid "Power"
196−msgstr ""
198+msgstr "Zima"
197199
198200#: qml/Components/Dialogs.qml:153
199201msgid ""
@@ -213,7 +215,7 @@ msgstr "Zima na uwashe"
213215
214216#: qml/Components/Lockscreen.qml:245
215217msgid "Return to Call"
216−msgstr ""
218+msgstr "Rudi nyuma upige simu"
217219
218220#: qml/Components/Lockscreen.qml:245
219221msgid "Emergency Call"
@@ -269,11 +271,11 @@ msgstr "Kidogo..."
269271
270272#: qml/Dash/Previews/PreviewRatingInput.qml:84
271273msgid "Rate this"
272−msgstr ""
274+msgstr "Pea kiwango"
273275
274276#: qml/Dash/Previews/PreviewRatingInput.qml:129
275277msgid "Add a review"
276−msgstr ""
278+msgstr "Ongeza pitio"
277279
278280#: qml/Dash/Previews/PreviewRatingInput.qml:169
279281msgid "Send"
@@ -281,11 +283,11 @@ msgstr "Tuma"
281283
282284#: qml/Dash/PullToRefreshScopeStyle.qml:55
283285msgid "Pull to refresh…"
284−msgstr ""
286+msgstr "Vuta unawarishe"
285287
286288#: qml/Dash/PullToRefreshScopeStyle.qml:60
287289msgid "Release to refresh…"
288−msgstr ""
290+msgstr "Achilia unawarishe"
289291
290292#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
291293msgid "Enable location data"
@@ -317,27 +319,31 @@ msgstr "Gusa ufungue"
317319
318320#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
319321msgid "Sorry, incorrect passphrase."
320−msgstr ""
322+msgstr "Samahani, neno siri si hilo"
321323
322324#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
323325msgid "Sorry, incorrect passcode."
324−msgstr ""
326+msgstr "Samahani, neno siri si hilo"
325327
326328#: qml/Greeter/NarrowView.qml:55
327329msgid "This will be your last attempt."
328−msgstr ""
330+msgstr "Hii ndiyo nafasi ya mwisho"
329331
330332#: qml/Greeter/NarrowView.qml:57
331333msgid ""
332334"If passphrase is entered incorrectly, your phone will conduct a factory "
333335"reset and all personal data will be deleted."
334336msgstr ""
337+"Kama utabonyeza neno la siri lisilofaa, simu yako itajiweka upya na data "
338+"yako yote itafutwa."
335339
336340#: qml/Greeter/NarrowView.qml:58
337341msgid ""
338342"If passcode is entered incorrectly, your phone will conduct a factory reset "
339343"and all personal data will be deleted."
340344msgstr ""
345+"Kama utabonyeza neno la siri lisilofaa, simu yako itajiweka upya na data "
346+"yako yote itafutwa."
341347
342348#: qml/Greeter/NarrowView.qml:115
343349#, qt-format
@@ -350,75 +356,75 @@ msgstr "Bonyeza neno siri"
350356
351357#: qml/Greeter/NarrowView.qml:117
352358msgid "Enter passcode"
353−msgstr ""
359+msgstr "Bonyeza neno la siri"
354360
355361#: qml/Greeter/NarrowView.qml:118
356362#, qt-format
357363msgid "Sorry, incorrect %1"
358−msgstr ""
364+msgstr "Samahani, %1 halidhihirishi"
359365
360366#: qml/Greeter/NarrowView.qml:119
361367msgid "Sorry, incorrect passphrase"
362−msgstr ""
368+msgstr "Samahani, neno la siri si hilo"
363369
364370#: qml/Greeter/NarrowView.qml:120
365371msgid "Please re-enter"
366−msgstr ""
372+msgstr "Tafadhali bonyeza tena"
367373
368374#: qml/Greeter/NarrowView.qml:121
369375msgid "Sorry, incorrect passcode"
370−msgstr ""
376+msgstr "Samahani, neno la siri si hilo"
371377
372378#: qml/Notifications/NotificationMenuItemFactory.qml:128
373379msgid "Show password"
374−msgstr ""
380+msgstr "Onyesha neno la siri"
375381
376382#: qml/Panel/ActiveCallHint.qml:79
377383msgid "Tap to return to call..."
378−msgstr ""
384+msgstr "Gusa urudi ili upige simu"
379385
380386#: qml/Panel/ActiveCallHint.qml:92
381387msgid "Conference"
382−msgstr ""
388+msgstr "Mkutano"
383389
384390#: qml/Panel/Indicators/MenuItemFactory.qml:639
385391msgid "Nothing is playing"
386−msgstr ""
392+msgstr "Hakuna linalocheza"
387393
388394#: qml/Panel/Indicators/MenuItemFactory.qml:768
389395#, qt-format
390396msgid "%1 hour"
391397msgid_plural "%1 hours"
392−msgstr[0] ""
393−msgstr[1] ""
398+msgstr[0] "Saa %1"
399+msgstr[1] "Mass %1"
394400
395401#: qml/Panel/Indicators/MenuItemFactory.qml:772
396402#, qt-format
397403msgid "%1 minute"
398404msgid_plural "%1 minutes"
399−msgstr[0] ""
400−msgstr[1] ""
405+msgstr[0] "Dakika %1"
406+msgstr[1] "Dakika %1"
401407
402408#: qml/Panel/Indicators/MenuItemFactory.qml:777
403409#, qt-format
404410msgid "%1 second"
405411msgid_plural "%1 seconds"
406−msgstr[0] ""
407−msgstr[1] ""
412+msgstr[0] "Sekunde %1"
413+msgstr[1] "Sekunde %1"
408414
409415#: qml/Panel/Indicators/MenuItemFactory.qml:780
410416msgid "0 seconds"
411−msgstr ""
417+msgstr "sekunde 0"
412418
413419#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
414420#: qml/Panel/Indicators/MenuItemFactory.qml:782
415421#, qt-format
416422msgid "%1 remaining"
417−msgstr ""
423+msgstr "%1 imebaki"
418424
419425#: qml/Panel/Indicators/MenuItemFactory.qml:788
420426msgid "In queue…"
421−msgstr ""
427+msgstr "Kwenye foleni"
422428
423429#: qml/Panel/Indicators/MenuItemFactory.qml:792
424430msgid "Downloading"
@@ -426,204 +432,204 @@ msgstr ""
426432
427433#: qml/Panel/Indicators/MenuItemFactory.qml:794
428434msgid "Paused, tap to resume"
429−msgstr ""
435+msgstr "Imepumzishwa, gusa uendelee"
430436
431437#: qml/Panel/Indicators/MenuItemFactory.qml:796
432438msgid "Canceled"
433−msgstr ""
439+msgstr "Imeghairishwa"
434440
435441#: qml/Panel/Indicators/MenuItemFactory.qml:798
436442msgid "Finished"
437−msgstr ""
443+msgstr "Imemaliza"
438444
439445#: qml/Panel/Indicators/MenuItemFactory.qml:800
440446msgid "Failed, tap to retry"
441−msgstr ""
447+msgstr "Imefeli, gusa urudie"
442448
443449#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
444450msgctxt "Label: Hint in message indicator line edit"
445451msgid "Reply"
446−msgstr ""
452+msgstr "Jibu"
447453
448454#: qml/Panel/Indicators/ModemInfoItem.qml:105
449455msgid "Unlock SIM"
450−msgstr ""
456+msgstr "Fungua SIM"
451457
452458#: qml/Panel/Indicators/RoamingIndication.qml:27
453459msgid "Roaming"
454−msgstr ""
460+msgstr "Uzururaji"
455461
456462#: qml/Tutorial/TutorialBottomFinish.qml:24
457463msgid "This action does different things for different apps"
458−msgstr ""
464+msgstr "Hatua hii inafanya vitu tofauti kwenye programu tofauti"
459465
460466#: qml/Tutorial/TutorialBottomFinish.qml:25
461467msgid "Tap here to finish."
462−msgstr ""
468+msgstr "Gusa hapa umalize"
463469
464470#: qml/Tutorial/TutorialBottom.qml:28
465471msgid "Open special menus"
466−msgstr ""
472+msgstr "Fungua orodha maalum"
467473
468474#: qml/Tutorial/TutorialBottom.qml:29
469475msgid "Swipe up from the bottom edge."
470−msgstr ""
476+msgstr "Vuta juu kutoka chini"
471477
472478#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
473479#: qml/Tutorial/TutorialRight.qml:144
474480msgid "Tap here to continue."
475−msgstr ""
481+msgstr "Gusa hapa uendelee."
476482
477483#: qml/Tutorial/TutorialContent.qml:81
478484msgid "Click here to finish."
479−msgstr ""
485+msgstr "Click hapa umalize."
480486
481487#: qml/Tutorial/TutorialLeftFinish.qml:24
482488msgid "These are the shortcuts to favorite apps"
483−msgstr ""
489+msgstr "Hizi ni njia za mkato za programu ulizopenda"
484490
485491#: qml/Tutorial/TutorialLeft.qml:26
486492msgid "Open the launcher"
487−msgstr ""
493+msgstr "Fungua launcher"
488494
489495#: qml/Tutorial/TutorialLeft.qml:27
490496msgid "Short swipe from the left edge."
491−msgstr ""
497+msgstr "Vuta kidogo kutoka kushoto."
492498
493499#: qml/Tutorial/TutorialPage.qml:178
494500msgid "You almost got it!"
495−msgstr ""
501+msgstr "Karibu upate!"
496502
497503#: qml/Tutorial/TutorialPage.qml:195
498504msgid "Try again."
499−msgstr ""
505+msgstr "Jaribu tena."
500506
501507#: qml/Tutorial/TutorialRight.qml:31
502508msgid "To view open apps"
503−msgstr ""
509+msgstr "Kuona fungua programu"
504510
505511#: qml/Tutorial/TutorialRight.qml:32
506512msgid "Long swipe from the right edge."
507−msgstr ""
513+msgstr "Vuta kabisa kutoka kulia."
508514
509515#: qml/Tutorial/TutorialRight.qml:122
510516msgid "View all your running tasks."
511−msgstr ""
517+msgstr "Ona kazi zote zinazoendelea."
512518
513519#: qml/Wizard/Page.qml:89
514520msgctxt "Button: Go back one page in the Wizard"
515521msgid "Back"
516−msgstr ""
522+msgstr "Nyuma"
517523
518524#: qml/Wizard/Pages/here-terms.qml:27
519525msgid "Terms & Conditions"
520−msgstr ""
526+msgstr "Suala na Masharti"
521527
522528#: qml/Wizard/Pages/passwd-confirm.qml:49
523529msgid "Confirm passphrase"
524−msgstr ""
530+msgstr "Thibitisha neno la siri"
525531
526532#: qml/Wizard/Pages/passwd-confirm.qml:50
527533msgid "Confirm passcode"
528−msgstr ""
534+msgstr "Thibitisha neno la siri"
529535
530536#: qml/Wizard/Pages/passwd-confirm.qml:53
531537#: qml/Wizard/Pages/passwd-confirm.qml:54
532538msgid "Please try again."
533−msgstr ""
539+msgstr "Jaribu tena."
534540
535541#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
536542#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
537543#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
538544#: qml/Wizard/Pages/60-reporting.qml:50
539545msgid "Continue"
540−msgstr ""
546+msgstr "Endelea"
541547
542548#: qml/Wizard/Pages/passwd-set.qml:61
543549msgid "Choose your passcode"
544−msgstr ""
550+msgstr "Chagua neno lako la siri"
545551
546552#: qml/Wizard/Pages/passwd-set.qml:68
547553msgid "Passphrase must be 4 characters long"
548−msgstr ""
554+msgstr "Neno la siri ni lazima liwe na urefu wa herufi 4"
549555
550556#: qml/Wizard/Pages/10-welcome.qml:27
551557msgid "Hi!"
552−msgstr ""
558+msgstr "Jambo!"
553559
554560#: qml/Wizard/Pages/10-welcome.qml:44
555561msgid "Welcome to your Ubuntu phone."
556−msgstr ""
562+msgstr "Karibu kwenye simu yako ya Ubuntu."
557563
558564#: qml/Wizard/Pages/10-welcome.qml:52
559565msgid "Let’s get started."
560−msgstr ""
566+msgstr "Tuanze pamoja."
561567
562568#: qml/Wizard/Pages/20-sim.qml:25
563569msgid "Add a SIM card and restart your device"
564−msgstr ""
570+msgstr "Ongeza kadi ya SIM halafu uanzishe upya kifaa chako"
565571
566572#: qml/Wizard/Pages/20-sim.qml:59
567573msgid "Without it, you won’t be able to make calls or use text messaging."
568−msgstr ""
574+msgstr "Bila hiyo, hutaweza kupiga simu ama kutuma teksti."
569575
570576#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
571577msgid "Skip"
572−msgstr ""
578+msgstr "Ruka"
573579
574580#: qml/Wizard/Pages/30-passwd-type.qml:39
575581msgid "Lock security"
576−msgstr ""
582+msgstr "Funga usalama"
577583
578584#: qml/Wizard/Pages/30-passwd-type.qml:74
579585msgid "Please select how you’d like to unlock your phone."
580−msgstr ""
586+msgstr "Tafadhali chagua vile ungependa kufunga simu."
581587
582588#: qml/Wizard/Pages/30-passwd-type.qml:97
583589msgctxt "Label: Type of security method"
584590msgid "Swipe"
585−msgstr ""
591+msgstr "Vuta"
586592
587593#: qml/Wizard/Pages/30-passwd-type.qml:98
588594msgctxt "Label: Description of security method"
589595msgid "No security"
590−msgstr ""
596+msgstr "Hakuna usalama"
591597
592598#: qml/Wizard/Pages/30-passwd-type.qml:100
593599msgctxt "Label: Type of security method"
594600msgid "Passcode"
595−msgstr ""
601+msgstr "Neno la siri"
596602
597603#: qml/Wizard/Pages/30-passwd-type.qml:101
598604msgctxt "Label: Description of security method"
599605msgid "4 digits only"
600−msgstr ""
606+msgstr "Herufi nne peke yake"
601607
602608#: qml/Wizard/Pages/30-passwd-type.qml:103
603609msgctxt "Label: Type of security method"
604610msgid "Passphrase"
605−msgstr ""
611+msgstr "Kaulisiri"
606612
607613#: qml/Wizard/Pages/30-passwd-type.qml:104
608614msgctxt "Label: Description of security method"
609615msgid "Numbers and letters"
610−msgstr ""
616+msgstr "Nambari na herufi"
611617
612618#: qml/Wizard/Pages/40-wifi.qml:29
613619msgid "Connect to Wi‑Fi"
614−msgstr ""
620+msgstr "Tumia Wi-fi"
615621
616622#: qml/Wizard/Pages/40-wifi.qml:168
617623msgid "Available networks…"
618−msgstr ""
624+msgstr "Mitandao inayopatikana"
619625
620626#: qml/Wizard/Pages/40-wifi.qml:169
621627msgid "No available networks."
622−msgstr ""
628+msgstr "Hakuna mitandao inayopatikana"
623629
624630#: qml/Wizard/Pages/50-location.qml:27
625631msgid "Location"
626−msgstr ""
632+msgstr "Mahali"
627633
628634#: qml/Wizard/Pages/50-location.qml:62
629635msgid "Let the phone detect your location:"
qml/Dash/Previews/PreviewComment.qmlnew file+83
@@ -0,0 +1,83 @@
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 Ubuntu.Components 1.2
19+
20+/*! \brief Preview widget for comments.
21+
22+ This widget shows an (optional) avatar contained in widgetData["source"]
23+ along with a label that comes from widgetData["author"],
24+ a (optional) subtitle from widgetData["subtitle"] and the comment widgetData["comment"].
25+*/
26+
27+PreviewWidget {
28+ id: root
29+ implicitHeight: Math.max(avatar.height, column.implicitHeight)
30+
31+ UbuntuShape {
32+ id: avatar
33+ objectName: "avatar"
34+ anchors {
35+ left: parent.left
36+ top: parent.top
37+ }
38+ width: units.gu(6)
39+ height: width
40+ source: Image {
41+ source: widgetData["source"]
42+ }
43+ radius: "medium"
44+ opacity: source.status === Image.Ready ? 1 : 0
45+ visible: widgetData["source"] !== ""
46+ }
47+
48+ Column {
49+ id: column
50+ objectName: "column"
51+ anchors {
52+ left: avatar.visible ? avatar.right : parent.left
53+ right: parent.right
54+ top: parent.top
55+ topMargin: units.gu(0.5)
56+ leftMargin: avatar.visible ? units.gu(1) : 0
57+ }
58+ spacing: units.gu(0.24)
59+
60+ Label {
61+ width: parent.width
62+ text: widgetData["author"] || ""
63+ fontSize: "small"
64+ maximumLineCount: 1
65+ elide: Text.ElideRight
66+ }
67+ Label {
68+ objectName: "subtitle"
69+ width: parent.width
70+ visible: text !== ""
71+ text: widgetData["subtitle"] || ""
72+ fontSize: "xx-small"
73+ maximumLineCount: 1
74+ elide: Text.ElideRight
75+ }
76+ Label {
77+ width: parent.width
78+ text: widgetData["comment"] || ""
79+ fontSize: "small"
80+ wrapMode: Text.Wrap
81+ }
82+ }
83+}
qml/Dash/Previews/PreviewCommentInput.qmlnew file+66
@@ -0,0 +1,66 @@
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 Ubuntu.Components 1.1
19+import "../../Components"
20+
21+/*! \brief Preview widget for commenting.
22+
23+ The widget can show a field to enter a comment.
24+ It is possible to customise the submit button's label by setting widgetData["submit-label"]
25+ The successeful submit emits triggered(widgetId, "commented", data),
26+ with data being {"comment": comment}.
27+*/
28+
29+PreviewWidget {
30+ id: root
31+ implicitHeight: Math.max(commentTextArea.implicitHeight, submitButton.implicitHeight)
32+
33+ function submit() {
34+ var data = { "comment": commentTextArea.text };
35+ triggered(root.widgetId, "commented", data);
36+ }
37+
38+ TextArea {
39+ id: commentTextArea
40+ objectName: "commentTextArea"
41+ anchors {
42+ top: parent.top
43+ left: parent.left
44+ right: submitButton.left
45+ rightMargin: units.gu(1)
46+ }
47+ autoSize: true
48+ }
49+
50+ Button {
51+ id: submitButton
52+ objectName: "submitButton"
53+
54+ readonly property bool readyToSubmit: commentTextArea.text.trim().length > 0
55+
56+ anchors {
57+ top: parent.top
58+ right: parent.right
59+ }
60+ color: readyToSubmit ? Theme.palette.selected.base : Theme.palette.normal.base
61+ text: widgetData["submit-label"] || i18n.tr("Send")
62+ onClicked: {
63+ if (readyToSubmit) root.submit()
64+ }
65+ }
66+}
qml/Dash/Previews/PreviewWidgetFactory.qml+2
@@ -50,6 +50,8 @@ Loader {
5050 switch (widgetType) {
5151 case "actions": return "PreviewActions.qml";
5252 case "audio": return "PreviewAudioPlayback.qml";
53+ case "comment": return "PreviewComment.qml";
54+ case "comment-input": return "PreviewCommentInput.qml";
5355 case "expandable": return "PreviewExpandable.qml";
5456 case "gallery": return "PreviewImageGallery.qml";
5557 case "header": return "PreviewHeader.qml";
qml/Launcher/LauncherDelegate.qml+100−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
@@ -28,6 +28,8 @@ Item {
2828 property bool itemFocused: false
2929 property real maxAngle: 0
3030 property bool inverted: false
31+ property bool alerting: false
32+ readonly property alias wiggling: wiggleAnim.running
3133
3234 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
3335 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
@@ -40,6 +42,82 @@ Item {
4042 property real offset: 0
4143 property real itemOpacity: 1
4244 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+ }
43121
44122 Item {
45123 id: iconItem
@@ -76,7 +154,7 @@ Item {
76154 objectName: "countEmblem"
77155 anchors {
78156 right: parent.right
79− top: parent.top
157+ bottom: parent.bottom
80158 margins: units.dp(3)
81159 }
82160 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
@@ -101,23 +179,21 @@ Item {
101179 }
102180 }
103181
104− BorderImage {
182+ UbuntuShape {
105183 id: progressOverlay
106184 objectName: "progressOverlay"
185+
107186 anchors {
108187 left: iconItem.left
109188 right: iconItem.right
110− bottom: iconItem.bottom
111− leftMargin: units.gu(1)
112− rightMargin: units.gu(1)
113− bottomMargin: units.gu(1)
189+ verticalCenter: parent.verticalCenter
190+ leftMargin: units.gu(1.5)
191+ rightMargin: units.gu(1.5)
114192 }
115− height: units.gu(1.5)
193+ height: units.gu(1)
116194 visible: root.progress > -1
117− source: "graphics/progressbar-trough.sci"
118−
119− // For fill calculation we need to remove the 2 units of border defined in .sci file
120− property int adjustedWidth: width - units.gu(2)
195+ color: UbuntuColors.darkGrey
196+ borderSource: "none"
121197
122198 Item {
123199 anchors {
@@ -125,17 +201,18 @@ Item {
125201 top: parent.top
126202 bottom: parent.bottom
127203 }
128− width: Math.min(100, root.progress) / 100 * parent.adjustedWidth + units.gu(1)
204+ width: Math.min(100, root.progress) / 100 * parent.width
129205 clip: true
130206
131− BorderImage {
207+ UbuntuShape {
132208 anchors {
133209 left: parent.left
134210 top: parent.top
135211 bottom: parent.bottom
136212 }
213+ color: "white"
214+ borderSource: "none"
137215 width: progressOverlay.width
138− source: "graphics/progressbar-fill.sci"
139216 }
140217 }
141218 }
@@ -180,6 +257,14 @@ Item {
180257 }
181258
182259 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+ },
183268 // Rotating 3 times at top/bottom because that increases the perspective.
184269 // This is a hack, but as QML does not support real 3D coordinates
185270 // getting a higher perspective can only be done by a hack. This is the most
qml/Launcher/LauncherPanel.qml+93−13
@@ -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,12 +110,21 @@ 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
115119 // need to be smaller than this size to avoid breakage.
116120 property int extensionSize: 0
117121
122+ // These properties are needed to facilitate implicitly unfolding the
123+ // launcher to the peeking icon, if it is not already fully flat
124+ // and in view for the user
125+ property real moveToIndexYFrom
126+ property real moveToIndexYTo
127+
118128 // Setting extensionSize after the list has been populated because it has
119129 // the potential to mess up with the intial positioning in combination
120130 // with snapping to the center of the list. This catches all the cases
@@ -128,6 +138,14 @@ Rectangle {
128138 flick(0, clickFlickSpeed)
129139 }
130140
141+ UbuntuNumberAnimation {
142+ id: moveToIndexAnimation
143+ target: launcherListView
144+ property: "contentY"
145+ from: launcherListView.moveToIndexYFrom
146+ to: launcherListView.moveToIndexYTo
147+ }
148+
131149 // The height of the area where icons start getting folded
132150 property int foldingStartHeight: units.gu(6.5)
133151 // The height of the area where the items reach the final folding angle
@@ -181,10 +199,63 @@ Rectangle {
181199 itemRunning: model.running
182200 itemFocused: model.focused
183201 inverted: root.inverted
202+ alerting: model.alerting
184203 z: -Math.abs(offset)
185204 maxAngle: 55
186205 property bool dragging: false
187206
207+ SequentialAnimation {
208+ id: peekingAnimation
209+
210+ // revealing
211+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 1 : 0 }
212+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 0 }
213+
214+ UbuntuNumberAnimation {
215+ target: launcherDelegate
216+ alwaysRunToEnd: true
217+ loops: 1
218+ properties: "x"
219+ to: (units.gu(.5) + launcherListView.width * .5) * (root.inverted ? -1 : 1)
220+ duration: UbuntuAnimation.BriskDuration
221+ }
222+
223+ // hiding
224+ UbuntuNumberAnimation {
225+ target: launcherDelegate
226+ alwaysRunToEnd: true
227+ loops: 1
228+ properties: "x"
229+ to: 0
230+ duration: UbuntuAnimation.BriskDuration
231+ }
232+
233+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 1 }
234+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 0 : 1 }
235+ }
236+
237+ onAlertingChanged: {
238+ if(alerting) {
239+ if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
240+ launcherListView.moveToIndexYFrom = launcherListView.contentY
241+ launcherListView.positionViewAtIndex(index, ListView.Center)
242+ launcherListView.moveToIndexYTo = launcherListView.contentY
243+ moveToIndexAnimation.start()
244+ if (!dragging && launcher.state !== "visible") {
245+ peekingAnimation.start()
246+ }
247+ }
248+
249+ if (launcherListView.peekingIndex === -1) {
250+ launcherListView.peekingIndex = index
251+ }
252+ } else {
253+ if (launcherListView.peekingIndex === index) {
254+ launcherListView.peekingIndex = -1
255+ }
256+ }
257+ }
258+
188259 ThinDivider {
189260 id: dropIndicator
190261 objectName: "dropIndicator"
@@ -287,6 +358,7 @@ Rectangle {
287358 MouseArea {
288359 id: dndArea
289360 objectName: "dndArea"
361+ acceptedButtons: Qt.LeftButton | Qt.RightButton
290362 anchors {
291363 fill: parent
292364 topMargin: launcherListView.topMargin
@@ -298,7 +370,7 @@ Rectangle {
298370 property int draggedIndex: -1
299371 property var selectedItem
300372 property bool preDragging: false
301− property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging
373+ property bool dragging: !!selectedItem && selectedItem.dragging
302374 property bool postDragging: false
303375 property int startX
304376 property int startY
@@ -316,6 +388,15 @@ Rectangle {
316388 return;
317389 }
318390
391+ if (mouse.button & Qt.RightButton) { // context menu
392+ // Opening QuickList
393+ quickList.item = clickedItem;
394+ quickList.model = launcherListView.model.get(index).quickList;
395+ quickList.appId = launcherListView.model.get(index).appId;
396+ quickList.state = "open";
397+ return
398+ }
399+
319400 // First/last item do the scrolling at more than 12 degrees
320401 if (index == 0 || index == launcherListView.count - 1) {
321402 if (clickedItem.angle > 12) {
@@ -509,7 +590,7 @@ Rectangle {
509590 id: quickListShape
510591 objectName: "quickListShape"
511592 anchors.fill: quickList
512− opacity: quickList.state === "open" ? 0.96 : 0
593+ opacity: quickList.state === "open" ? 0.8 : 0
513594 visible: opacity > 0
514595 rotation: root.rotation
515596
@@ -521,15 +602,15 @@ Rectangle {
521602
522603 Image {
523604 anchors {
524− left: parent.left
525− leftMargin: (quickList.item.width - units.gu(1)) / 2 - width / 2
605+ right: parent.left
606+ rightMargin: -units.dp(4)
526607 verticalCenter: parent.verticalCenter
527− verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
608+ verticalCenterOffset: -quickList.offset * (root.inverted ? -1 : 1)
528609 }
529610 height: units.gu(1)
530611 width: units.gu(2)
531612 source: "graphics/quicklist_tooltip.png"
532− rotation: (quickList.offset > 0 ? 0 : 180) + (root.inverted ? 0 : 180)
613+ rotation: 90
533614 }
534615
535616 InverseMouseArea {
@@ -552,11 +633,11 @@ Rectangle {
552633 height: quickListColumn.height
553634 visible: quickListShape.visible
554635 anchors {
555− left: root.inverted ? undefined : parent.left
556− right: root.inverted ? parent.right : undefined
636+ left: root.inverted ? undefined : parent.right
637+ right: root.inverted ? parent.left : undefined
557638 margins: units.gu(1)
558639 }
559− y: itemCenter + offset
640+ y: itemCenter - (height / 2) + offset
560641 rotation: root.rotation
561642
562643 property var model
@@ -565,9 +646,8 @@ Rectangle {
565646
566647 // internal
567648 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
568− property int offset: itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
569− -(item.height/2) - height - units.gu(.5) :
570− (item.height/2) + units.gu(.5)
649+ property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
650+ itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
571651
572652 Column {
573653 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/Notifications/Notifications.qml+1−1
@@ -33,7 +33,7 @@ ListView {
3333 id: snapDecisionProxyModel
3434 objectName: "snapDecisionProxyModel"
3535
36− model: notificationList.model
36+ model: notificationList.model ? notificationList.model : null
3737 filterRole: UnityNotifications.ModelInterface != undefined ? UnityNotifications.ModelInterface.RoleType : 0
3838 filterRegExp: RegExp(UnityNotifications.Notification.SnapDecision)
3939 }
qml/Panel/IndicatorItem.qml+1−1
@@ -234,7 +234,7 @@ IndicatorDelegate {
234234 ]
235235 }
236236
237− onRootActionStateChanged: {
237+ rootActionState.onUpdated: {
238238 if (rootActionState == undefined) {
239239 title = "";
240240 leftLabel = "";
qml/Panel/Indicators/IndicatorBase.qml−1
@@ -43,6 +43,5 @@ Item {
4343 ModelActionRootState {
4444 id: rootAction
4545 menu: menuModel ? menuModel : null
46− onUpdated: indicatorItem.rootActionStateChanged()
4746 }
4847}
qml/Tutorial/TutorialLeft.qml+16−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
@@ -46,24 +46,34 @@ TutorialPage {
4646
4747 SequentialAnimation {
4848 id: teaseAnimation
49+ objectName: "teaseAnimation"
4950 paused: running && root.paused
5051 running: !slider.active && root.launcher.visibleWidth === 0 && root.shown
5152 loops: Animation.Infinite
53+ property real bounce: 0
54+ readonly property real maxBounce: units.gu(2)
5255
5356 UbuntuNumberAnimation {
54− target: root.launcher
55− property: "x"
56− to: units.gu(2)
57+ target: teaseAnimation
58+ property: "bounce"
59+ to: teaseAnimation.maxBounce
5760 duration: UbuntuAnimation.SleepyDuration
5861 }
5962 UbuntuNumberAnimation {
60− target: root.launcher
61− property: "x"
63+ target: teaseAnimation
64+ property: "bounce"
6265 to: 0
6366 duration: UbuntuAnimation.SleepyDuration
6467 }
6568 }
6669
70+ Binding {
71+ target: root.launcher
72+ when: root.shown
73+ property: "x"
74+ value: Math.min(root.launcher.panelWidth - root.launcher.visibleWidth, teaseAnimation.bounce)
75+ }
76+
6777 Timer {
6878 id: finishTimer
6979 interval: 1
tests/autopilot/unity8/settings_wizard/__init__.pynew file+507
@@ -0,0 +1,507 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+import logging
21+import re
22+
23+import autopilot
24+from autopilot import introspection
25+from ubuntuuitoolkit import UbuntuUIToolkitCustomProxyObjectBase
26+
27+from contextlib import contextmanager
28+
29+
30+logger = logging.getLogger(__name__)
31+
32+
33+@contextmanager
34+def override_proxy_timeout(proxy, timeout_seconds):
35+ original_timeout = proxy._poll_time
36+ try:
37+ proxy._poll_time = timeout_seconds
38+ yield proxy
39+ finally:
40+ proxy._poll_time = original_timeout
41+
42+
43+def get_wizard(current_page):
44+ return current_page.get_root_instance().select_single(Wizard)
45+
46+
47+class Wizard(UbuntuUIToolkitCustomProxyObjectBase):
48+ """High-level helper to navigate through the pages of the wizard"""
49+
50+ def get_language_page(self):
51+ return self.wait_select_single(
52+ objectName='languagePage', visible='True')
53+
54+ def get_current_page(self):
55+ return self.wait_select_single('Page', visible='True')
56+
57+ def get_sim_page(self):
58+ return self.wait_select_single(
59+ objectName='simPage', visible='True')
60+
61+ def get_password_page(self):
62+ return self.wait_select_single(
63+ objectName='passwdPage', visible='True')
64+
65+ def get_password_entry_page(self):
66+ return self.wait_select_single(
67+ objectName='passwdSetPage', visible='True')
68+
69+ def get_confirm_password_page(self):
70+ return self.wait_select_single(
71+ objectName='passwdConfirmPage', visible='True')
72+
73+ def get_wifi_connect_page(self):
74+ return self.wait_select_single(
75+ objectName='wifiPage', visible='True')
76+
77+ def get_reporting_page(self):
78+ return self.wait_select_single(
79+ objectName='reportingPage', visible='True')
80+
81+ def get_finished_page(self):
82+ return self.wait_select_single(
83+ objectName='finishedPage', visible='True')
84+
85+
86+class WizardLanguagePage(UbuntuUIToolkitCustomProxyObjectBase):
87+ """Helper class to interact with the language welcome page"""
88+
89+ # This class was renamed from LanguagePage to WizardLanguagePage because
90+ # there is a class with the same name in the system settings custom proxy
91+ # objects. Reported in http://pad.lv/1422904. --elopio - 2015-02-17
92+
93+ @classmethod
94+ def validate_dbus_object(cls, path, state):
95+ name = introspection.get_classname_from_path(path)
96+ if name == b'Page':
97+ if state['objectName'][1] == 'languagePage':
98+ return True
99+ return False
100+
101+ def _get_language_button(self):
102+ return self.select_single(
103+ 'ComboButton', objectName='languageCombo')
104+
105+ def _get_continue_button(self):
106+ return self.select_single('StackButton', text='Continue')
107+
108+ def _swipe_to_language(self, list, language):
109+ """Swipe to the chosen language in the given list"""
110+ list.swipe_to_top()
111+ while not list.atYEnd:
112+ try:
113+ item = list.select_single(
114+ 'LabelVisual', text=language, visible='True')
115+ item.swipe_into_view()
116+ return item
117+ except introspection.dbus.StateNotFoundError:
118+ list.swipe_to_show_more_below()
119+ # item could not be found
120+ raise introspection.dbus.StateNotFoundError
121+
122+ @autopilot.logging.log_action(logger.info)
123+ def select_language(self, language):
124+ """Select a different language from the list"""
125+ combo_button = self._get_language_button()
126+ self.pointing_device.click_object(combo_button)
127+ language_list = combo_button.wait_select_single(
128+ 'UbuntuListView11', visible='True')
129+ item = self._swipe_to_language(language_list, language)
130+ self.pointing_device.click_object(item)
131+
132+ def get_selected_language(self):
133+ return self._get_language_button().get_properties()['text']
134+
135+ @autopilot.logging.log_action(logger.info)
136+ def continue_(self):
137+ self.pointing_device.click_object(self._get_continue_button())
138+ wizard = get_wizard(self)
139+ next_page = wizard.get_current_page()
140+ sim_inserted = True
141+ if next_page.objectName == 'simPage':
142+ # no sim is inserted
143+ next_page = wizard.get_sim_page()
144+ sim_inserted = False
145+ else:
146+ # sim is inserted
147+ next_page = wizard.get_password_page()
148+ return sim_inserted, next_page
149+
150+
151+class SimPage(UbuntuUIToolkitCustomProxyObjectBase):
152+ """Helper class to interact with the no sim notification page"""
153+
154+ @classmethod
155+ def validate_dbus_object(cls, path, state):
156+ name = introspection.get_classname_from_path(path)
157+ if name == b'Page':
158+ if state['objectName'][1] == 'simPage':
159+ return True
160+ return False
161+
162+ def _get_back_button(self):
163+ return self.select_single('StackButton', text='Back')
164+
165+ def _get_skip_button(self):
166+ return self.select_single('StackButton', text='Skip')
167+
168+ @autopilot.logging.log_action(logger.info)
169+ def back(self):
170+ self.pointing_device.click_object(self._get_back_button())
171+ return get_wizard(self).get_languge_page()
172+
173+ @autopilot.logging.log_action(logger.info)
174+ def skip(self):
175+ self.pointing_device.click_object(self._get_skip_button())
176+ return get_wizard(self).get_password_page()
177+
178+
179+class PasswordPage(UbuntuUIToolkitCustomProxyObjectBase):
180+ """Helper class to interact with the password security page"""
181+
182+ SECURITY_SWIPE = 'Swipe'
183+ SECURITY_PASSCODE = 'Passcode'
184+ SECURITY_PASSPHRASE = 'Passphrase'
185+
186+ @classmethod
187+ def validate_dbus_object(cls, path, state):
188+ name = introspection.get_classname_from_path(path)
189+ if name == b'Page':
190+ if state['objectName'][1] == 'passwdPage':
191+ return True
192+ return False
193+
194+ def _get_option_name(self, formatted_name):
195+ # the option name is formatted as follows:
196+ # need to read the name between bold tags
197+ # '<b>Passcode</b> (4 digits only)'
198+ formatted_pattern = re.compile(r'<b>(.*?)</b>.*',
199+ re.IGNORECASE | re.DOTALL)
200+ name = None
201+ match = formatted_pattern.search(formatted_name)
202+ if match:
203+ name = match.group(1)
204+ return name
205+
206+ def _get_all_options(self):
207+ return self.select_many('OptionSelectorDelegate')
208+
209+ def _get_continue_button(self):
210+ return self.select_single('StackButton', text='Continue')
211+
212+ def _get_back_button(self):
213+ return self.select_single('StackButton', text='Back')
214+
215+ def _get_selected_option(self):
216+ return self.select_single(
217+ 'OptionSelectorDelegate', visible='True', selected='True')
218+
219+ def get_selected_security_option(self):
220+ name_fmt = self._get_selected_option().get_properties()['subText']
221+ return self._get_option_name(name_fmt)
222+
223+ @autopilot.logging.log_action(logger.info)
224+ def select_security_option(self, selected_option):
225+ options = self._get_all_options()
226+ for option in options:
227+ name = self._get_option_name(option.get_properties()['subText'])
228+ if name == selected_option:
229+ self.pointing_device.click_object(option)
230+
231+ @autopilot.logging.log_action(logger.info)
232+ def back(self, sim_inserted):
233+ self.pointing_device.click_object(self._get_back_button())
234+ wizard = get_wizard(self)
235+ if sim_inserted:
236+ page = wizard.get_languge_page()
237+ else:
238+ page = wizard.get_sim_page()
239+ return page
240+
241+ @autopilot.logging.log_action(logger.info)
242+ def continue_(self):
243+ option = self.get_selected_security_option()
244+ self.pointing_device.click_object(self._get_continue_button())
245+ wizard = get_wizard(self)
246+ if option == self.SECURITY_SWIPE:
247+ next_page = wizard.get_wifi_connect_page()
248+ else:
249+ next_page = wizard.get_password_entry_page()
250+ return next_page
251+
252+
253+class PasswordEntryPage(UbuntuUIToolkitCustomProxyObjectBase):
254+ """Helper class to interact with the pin entry page"""
255+
256+ @classmethod
257+ def validate_dbus_object(cls, path, state):
258+ name = introspection.get_classname_from_path(path)
259+ if name == b'Page':
260+ if state['objectName'][1] == 'passwdSetPage':
261+ return True
262+ return False
263+
264+ def _get_pinpad_button(self, char):
265+ return self.select_single('PinPadButton', text=char)
266+
267+ def _get_continue_button(self):
268+ return self.select_single('StackButton', text='Continue')
269+
270+ def _get_back_button(self):
271+ return self.select_single('StackButton', text='Back')
272+
273+ def _get_text_field(self):
274+ return self.select_single('QQuickTextInput')
275+
276+ @autopilot.logging.log_action(logger.info)
277+ def enter_pin(self, pin):
278+ for char in pin:
279+ self.pointing_device.click_object(self._get_pinpad_button(char))
280+ return get_wizard(self).get_confirm_password_page()
281+
282+ @autopilot.logging.log_action(logger.info)
283+ def enter_text(self, text):
284+ self.pointing_device.click_object(self._get_text_field())
285+ autopilot.input.Keyboard.create().type(text)
286+
287+ @autopilot.logging.log_action(logger.info)
288+ def back(self):
289+ self.pointing_device.click_object(self._get_back_button())
290+ return get_wizard(self).get_password_page()
291+
292+ @autopilot.logging.log_action(logger.info)
293+ def continue_(self):
294+ autopilot.input.Keyboard.create().press_and_release('Enter')
295+ return get_wizard(self).get_confirm_password_page()
296+
297+
298+class ConfirmPasswordPage(UbuntuUIToolkitCustomProxyObjectBase):
299+ """Helper class to interact with the pin confirmation page"""
300+
301+ @classmethod
302+ def validate_dbus_object(cls, path, state):
303+ name = introspection.get_classname_from_path(path)
304+ if name == b'Page':
305+ if state['objectName'][1] == 'passwdConfirmPage':
306+ return True
307+ return False
308+
309+ def _get_pinpad_button(self, char):
310+ return self.select_single('PinPadButton', text=char)
311+
312+ def _get_continue_button(self):
313+ return self.select_single('StackButton', text='Continue')
314+
315+ def _get_back_button(self):
316+ return self.select_single('StackButton', text='Back')
317+
318+ def _get_text_field(self):
319+ return self.select_single('QQuickTextInput')
320+
321+ @autopilot.logging.log_action(logger.info)
322+ def enter_pin(self, pin):
323+ for char in pin:
324+ self.pointing_device.click_object(self._get_pinpad_button(char))
325+ return get_wizard(self).get_wifi_connect_page()
326+
327+ @autopilot.logging.log_action(logger.info)
328+ def enter_text(self, text):
329+ self.pointing_device.click_object(self._get_text_field())
330+ autopilot.input.Keyboard.create().type(text)
331+
332+ @autopilot.logging.log_action(logger.info)
333+ def back(self):
334+ self.pointing_device.click_object(self._get_back_button())
335+ return get_wizard(self).get_password_page()
336+
337+ @autopilot.logging.log_action(logger.info)
338+ def continue_(self):
339+ autopilot.input.Keyboard.create().press_and_release('Enter')
340+ return get_wizard(self).get_wifi_connect_page()
341+
342+
343+class WifiConnectPage(UbuntuUIToolkitCustomProxyObjectBase):
344+ """Helper class to interact with the Wi-Fi network list page"""
345+
346+ @classmethod
347+ def validate_dbus_object(cls, path, state):
348+ name = introspection.get_classname_from_path(path)
349+ if name == b'Page':
350+ if state['objectName'][1] == 'wifiPage':
351+ return True
352+ return False
353+
354+ def _get_all_networks(self):
355+ try:
356+ networks = self.select_many('Standard', objectName='accessPoint',
357+ visible='True')
358+ except:
359+ networks = []
360+ return networks
361+
362+ def _get_network(self, ssid):
363+ return self.wait_select_single(
364+ 'Standard', objectName='accessPoint', text=ssid, visible='True')
365+
366+ def _get_network_checkbox(self, ssid):
367+ return self._get_network(ssid).select_single(
368+ 'CheckBox', visible='True')
369+
370+ def _get_notification(self, unity):
371+ logger.info('Waiting longer for notification object')
372+ with override_proxy_timeout(unity, 30):
373+ return unity.wait_select_single(
374+ Notification, objectName='notification1', visible='True')
375+
376+ def _get_back_button(self):
377+ return self.wait_select_single(
378+ 'StackButton', text='Back', visible='True')
379+
380+ def _get_skip_button(self):
381+ return self.wait_select_single(
382+ 'StackButton', text='Skip', visible='True')
383+
384+ def _get_continue_button(self):
385+ return self.wait_select_single(
386+ 'StackButton', text='Continue', visible='True')
387+
388+ def is_any_network_checked(self):
389+ networks = self._get_all_networks()
390+ for network in networks:
391+ checkbox = network.select_single('CheckBox', visible='True')
392+ if checkbox.get_properties()['checked']:
393+ return True
394+ return False
395+
396+ def is_any_network_found(self):
397+ num_neworks = len(self._get_all_networks())
398+ return True if num_neworks > 0 else False
399+
400+ def is_network_checked(self, ssid):
401+ return self._get_network_checkbox(ssid).get_properties()['checked']
402+
403+ @autopilot.logging.log_action(logger.info)
404+ def select_network(self, unity, ssid):
405+ self.pointing_device.click_object(self._get_network_checkbox(ssid))
406+ return PasswordNotification(self._get_notification(unity))
407+
408+ @autopilot.logging.log_action(logger.info)
409+ def back(self):
410+ self.pointing_device.click_object(self._get_back_button())
411+ return get_wizard(self).get_password_page()
412+
413+ @autopilot.logging.log_action(logger.info)
414+ def skip(self):
415+ self.pointing_device.click_object(self._get_skip_button())
416+ return get_wizard(self).get_reporting_page()
417+
418+ @autopilot.logging.log_action(logger.info)
419+ def continue_(self):
420+ self.pointing_device.click_object(self._get_continue_button())
421+ return get_wizard(self).get_reporting_page()
422+
423+
424+class Notification(UbuntuUIToolkitCustomProxyObjectBase):
425+ """Base class for notification objects"""
426+ pass
427+
428+
429+class PasswordNotification(Notification):
430+ """Helper class to interact with the password entry notification"""
431+
432+ def __init__(self, notification):
433+ self.notification = notification
434+
435+ def _get_connect_button(self):
436+ return self.notification.wait_select_single(
437+ 'Button', text='Connect', objectName='notify_button0')
438+
439+ def _get_cancel_buttoon(self):
440+ return self.notification.wait_select_single(
441+ 'Button', text='Cancel', objectName='notify_button1')
442+
443+ def _get_text_field(self):
444+ return self.notification.wait_select_single('TextField')
445+
446+ @autopilot.logging.log_action(logger.info)
447+ def enter_text(self, text):
448+ self._get_text_field().write(text)
449+
450+ @autopilot.logging.log_action(logger.info)
451+ def connect(self):
452+ self.notification.pointing_device.click_object(
453+ self._get_connect_button())
454+
455+ @autopilot.logging.log_action(logger.info)
456+ def cancel(self):
457+ self.notification.pointing_device.click_object(
458+ self._get_cancel_buttoon())
459+
460+
461+class ReportingPage(UbuntuUIToolkitCustomProxyObjectBase):
462+ """Helper class to interact with the reporting page"""
463+
464+ @classmethod
465+ def validate_dbus_object(cls, path, state):
466+ name = introspection.get_classname_from_path(path)
467+ if name == b'Page':
468+ if state['objectName'][1] == 'reportingPage':
469+ return True
470+ return False
471+
472+ def _get_continue_button(self):
473+ return self.select_single('StackButton', text='Continue')
474+
475+ def _get_back_button(self):
476+ return self.select_single('StackButton', text='Back')
477+
478+ @autopilot.logging.log_action(logger.info)
479+ def back(self):
480+ self.pointing_device.click_object(self._get_back_button())
481+ return get_wizard(self).get_wifi_connect_page()
482+
483+ @autopilot.logging.log_action(logger.info)
484+ def continue_(self):
485+ self.pointing_device.click_object(self._get_continue_button())
486+ return get_wizard(self).get_finished_page()
487+
488+
489+class FinishedPage(UbuntuUIToolkitCustomProxyObjectBase):
490+ """Helper class to interact with the finished page"""
491+
492+ @classmethod
493+ def validate_dbus_object(cls, path, state):
494+ name = introspection.get_classname_from_path(path)
495+ if name == b'Page':
496+ if state['objectName'][1] == 'finishedPage':
497+ return True
498+ return False
499+
500+ def _get_finish_button(self):
501+ return self.wait_select_single(
502+ 'StackButton', text='Finish', visible='True')
503+
504+ @autopilot.logging.log_action(logger.info)
505+ def finish(self):
506+ self.pointing_device.click_object(self._get_finish_button())
507+ self.wait_until_destroyed()
tests/autopilot/unity8/settings_wizard/fixture_setup.pynew file+58
@@ -0,0 +1,58 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Set up and clean up fixtures for the Unity SettingsWizard tests."""
21+
22+import fixtures
23+import os
24+
25+from os.path import expanduser
26+
27+
28+class SettingsWizard(fixtures.Fixture):
29+ WIZARD_FILE = expanduser("~") + \
30+ '/.config/ubuntu-system-settings/wizard-has-run'
31+
32+ def __init__(self, enable):
33+ super().__init__()
34+ self.enable = enable
35+
36+ def setUp(self):
37+ super().setUp()
38+ original_state = self.is_settings_wizard_enabled()
39+ self.addCleanup(self.set_settings_wizard, original_state)
40+ if self.enable != original_state:
41+ self.set_settings_wizard(self.enable)
42+
43+ def is_settings_wizard_enabled(self):
44+ return not os.path.exists(self.WIZARD_FILE)
45+
46+ def set_settings_wizard(self, enabled):
47+ if enabled:
48+ self.enable_settings_wizard()
49+ else:
50+ self.disable_settings_wizard()
51+
52+ def enable_settings_wizard(self):
53+ if os.path.exists(self.WIZARD_FILE):
54+ os.remove(self.WIZARD_FILE)
55+
56+ def disable_settings_wizard(self):
57+ if not os.path.exists(self.WIZARD_FILE):
58+ open(self.WIZARD_FILE, 'a').close()
tests/autopilot/unity8/settings_wizard/ofono_helper.pynew file+116
@@ -0,0 +1,116 @@
1+#!/usr/bin/python -tt
2+
3+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
4+#
5+# Unity Indicators Autopilot Test Suite
6+# Copyright (C) 2014, 2015 Canonical
7+#
8+# This program is free software: you can redistribute it and/or modify
9+# it under the terms of the GNU General Public License as published by
10+# the Free Software Foundation, either version 3 of the License, or
11+# (at your option) any later version.
12+#
13+# This program is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+# GNU General Public License for more details.
17+#
18+# You should have received a copy of the GNU General Public License
19+# along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
21+
22+from __future__ import absolute_import
23+
24+import dbus
25+import subprocess
26+from time import sleep
27+
28+
29+class OfonoManager(object):
30+
31+ def __init__(self):
32+ self.system_bus = dbus.SystemBus()
33+ self.ofono = self.system_bus.get_object('org.ofono', '/')
34+
35+ self.real_manager = dbus.Interface(
36+ self.ofono,
37+ 'org.ofono.Manager'
38+ )
39+
40+ def _get_sim_manager(self):
41+ modems = self.real_manager.GetModems()
42+ path = modems[0][0]
43+ return dbus.Interface(self.system_bus.get_object('org.ofono', path),
44+ 'org.ofono.SimManager')
45+
46+ def physical_sim_is_present(self):
47+ sim_manager = self._get_sim_manager()
48+ properties = sim_manager.GetProperties()
49+ return properties["Present"] == 1
50+
51+
52+class PhonesimManager(object):
53+
54+ def __init__(self, sims, exe=None):
55+ if exe is None:
56+ self.phonesim_exe = '/usr/bin/ofono-phonesim'
57+ else:
58+ self.phonesim_exe = exe
59+ self.sims = sims
60+ self.sim_processes = {}
61+ self.system_bus = dbus.SystemBus()
62+ self.ofono = self.system_bus.get_object('org.ofono', '/')
63+
64+ self.phonesim_manager = dbus.Interface(
65+ self.ofono,
66+ 'org.ofono.phonesim.Manager'
67+ )
68+
69+ def start_phonesim_processes(self):
70+ for simname, simport, conffile in self.sims:
71+ cmd = ['/usr/bin/xvfb-run',
72+ '-a',
73+ self.phonesim_exe,
74+ '-p',
75+ str(simport),
76+ conffile]
77+
78+ p = subprocess.Popen(cmd)
79+ self.sim_processes[simname] = p
80+ # give the processes some time to start
81+ sleep(3)
82+
83+ def shutdown(self):
84+ for p in self.sim_processes.values():
85+ p.kill()
86+ self.sim_processes = {}
87+
88+ def reset_ofono(self):
89+ self.phonesim_manager.Reset()
90+
91+ def remove_all_ofono(self):
92+ self.phonesim_manager.RemoveAll()
93+
94+ def add_ofono(self, name):
95+ for simname, simport, _ in self.sims:
96+ if name == simname:
97+ self.phonesim_manager.Add(simname, '127.0.0.1', str(simport))
98+ return
99+ raise RuntimeError('Tried to add unknown modem %s.' % name)
100+
101+ def power_on(self, sim_name):
102+ sim = self.system_bus.get_object('org.ofono', '/'+sim_name)
103+ modem = dbus.Interface(sim, dbus_interface='org.ofono.Modem')
104+ modem.SetProperty('Powered', True)
105+ sleep(1)
106+
107+ def power_off(self, sim_name):
108+ sim = self.system_bus.get_object('org.ofono', '/'+sim_name)
109+ modem = dbus.Interface(sim, dbus_interface='org.ofono.Modem')
110+ modem.SetProperty('Powered', False)
111+ sleep(1)
112+
113+ def get_required_pin(self, sim_name):
114+ sim = self.system_bus.get_object('org.ofono', '/'+sim_name)
115+ interface = dbus.Interface(sim, dbus_interface='org.ofono.SimManager')
116+ return str(interface.GetProperties()['PinRequired'])
tests/autopilot/unity8/settings_wizard/tests/__init__.pynew file

No changes to the content.

tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.pynew file+79
@@ -0,0 +1,79 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+from unity8.settings_wizard import fixture_setup, Wizard
21+from unity8.shell import tests
22+
23+DEFAULT_LANGUAGE = 'English (United States)'
24+DEFAULT_PHONESIM_CONFIG_FILE = '/usr/share/phonesim/default.xml'
25+DEFAULT_SECURITY_METHOD = 'Passcode'
26+
27+
28+class SkipThroughSettingsWizardTestCase(tests.UnityTestCase):
29+ """ Autopilot test for completing settings wizard """
30+
31+ def setUp(self):
32+ super().setUp()
33+ self.wizard_helper = self.useFixture(
34+ fixture_setup.SettingsWizard(True))
35+ self.unity = self.launch_unity()
36+ self.wizard = self._get_settings_wizard()
37+
38+ def _get_settings_wizard(self):
39+ return self.unity.wait_select_single(Wizard)
40+
41+ def _test_language_page(self):
42+ """ Get the language page, check the default language and continue """
43+ language_page = self.wizard.get_language_page()
44+ default_selection = language_page.get_selected_language()
45+ self.assertEqual(default_selection, DEFAULT_LANGUAGE)
46+ return language_page.continue_()
47+
48+ def _test_password_page(self, password_page):
49+ """ Check default selection for password type and change
50+ password type to swipe to keep this test as uncomplicated
51+ as possible """
52+ default_selection = password_page.get_selected_security_option()
53+ self.assertEqual(default_selection, DEFAULT_SECURITY_METHOD)
54+ password_page.select_security_option('Swipe')
55+ return password_page.continue_()
56+
57+ def _test_reporting_page(self, reporting_page):
58+ return reporting_page.continue_()
59+
60+ def _test_wifi_connect_page(self, wifi_connect_page):
61+ if wifi_connect_page.is_any_network_checked() or not \
62+ wifi_connect_page.is_any_network_found():
63+ return wifi_connect_page.continue_()
64+ else:
65+ return wifi_connect_page.skip()
66+
67+ def test_skipping_through_wizard(self):
68+ """ Most basic test of the settings wizard. Skip all skipable pages """
69+ sim_inserted, next_page = self._test_language_page()
70+ if sim_inserted:
71+ password_page = next_page
72+ else:
73+ password_page = next_page.skip()
74+ wifi_connect_page = self._test_password_page(password_page)
75+ reporting_page = self._test_wifi_connect_page(wifi_connect_page)
76+ finish_page = self._test_reporting_page(reporting_page)
77+ finish_page.finish()
78+ self.assertFalse(
79+ self.wizard_helper.is_settings_wizard_enabled())
tests/mocks/QMenuModel/QDBusActionGroup.qml+2−2
@@ -41,8 +41,8 @@ QtObject {
4141 signal activated
4242
4343 property string actionName: \"" + actionName + "\"
44− property bool valid: ActionData.data[actionName] != undefined ? ActionData.data[actionName].valid : false
45− property var state: ActionData.data[actionName] != undefined ? ActionData.data[actionName].state : undefined
44+ property bool valid: ActionData.data.hasOwnProperty(actionName) ? ActionData.data[actionName].valid : false
45+ property var state: ActionData.data.hasOwnProperty(actionName) ? ActionData.data[actionName].state : undefined
4646
4747 function activate() {
4848 activated();
tests/mocks/QMenuModel/actiondata.h+3−1
@@ -31,7 +31,9 @@ class Q_DECL_EXPORT ActionData : public QObject
3131public:
3232 ActionData(QObject* parent = 0)
3333 : QObject(parent)
34− {}
34+ , m_data(QVariantMap())
35+ {
36+ }
3537
3638 QVariant data() const { return m_data; }
3739 void setData(const QVariant& data)
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−3
@@ -26,7 +26,6 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
2626 item->setProgress(0);
2727 item->setPinned(true);
2828 item->setFocused(true);
29− item->setRunning(true);
3029 m_list.append(item);
3130 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
3231 item->setProgress(10);
@@ -35,6 +34,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
3534 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
3635 item->setProgress(50);
3736 item->setCountVisible(true);
37+ item->setAlerting(false);
3838 m_list.append(item);
3939 item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
4040 m_list.append(item);
@@ -44,26 +44,31 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
4444 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
4545 item->setCount(1);
4646 item->setCountVisible(true);
47+ item->setRunning(true);
48+ item->setAlerting(false);
4749 m_list.append(item);
4850 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
4951 item->setCount(12);
5052 item->setCountVisible(true);
53+ item->setAlerting(false);
5154 item->setPinned(true);
5255 m_list.append(item);
5356 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
5457 item->setCount(123);
5558 item->setCountVisible(true);
59+ item->setAlerting(false);
5660 m_list.append(item);
5761 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
5862 item->setCount(1234567890);
5963 item->setCountVisible(true);
64+ item->setAlerting(false);
6065 item->setPinned(true);
61− item->setRunning(true);
6266 m_list.append(item);
6367 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
6468 item->setProgress(50);
6569 item->setCount(5);
6670 item->setCountVisible(true);
71+ item->setAlerting(false);
6772 item->setFocused(true);
6873 item->setPinned(true);
6974 m_list.append(item);
@@ -107,6 +112,8 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
107112 return item->countVisible();
108113 case RoleFocused:
109114 return item->focused();
115+ case RoleAlerting:
116+ return item->alerting();
110117 }
111118
112119 return QVariant();
@@ -121,6 +128,18 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
121128 return m_list.at(index);
122129}
123130
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+
124143void MockLauncherModel::move(int oldIndex, int newIndex)
125144{
126145 // Make sure its not moved outside the lists
@@ -176,7 +195,9 @@ void MockLauncherModel::requestRemove(const QString &appId)
176195 int index = findApp(appId);
177196 if (index >= 0) {
178197 beginRemoveRows(QModelIndex(), index, 0);
179− m_list.takeAt(index)->deleteLater();
198+ MockLauncherItem * item = m_list.takeAt(index);
199+ item->setRunning(false);
200+ item->deleteLater();
180201 endRemoveRows();
181202 }
182203}
@@ -197,6 +218,16 @@ int MockLauncherModel::findApp(const QString &appId)
197218 return -1;
198219}
199220
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+
200231void MockLauncherModel::setUser(const QString &username)
201232{
202233 Q_UNUSED(username)
@@ -228,6 +259,26 @@ void MockLauncherModel::emitHint()
228259 Q_EMIT hint();
229260}
230261
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, QVector<int>() << RoleCount);
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+
231282unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
232283{
233284 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/plugins/AccountsService/client.cpp+10−20
@@ -94,8 +94,7 @@ private Q_SLOTS:
9494
9595 void testGetSetService()
9696 {
97− AccountsService session;
98− session.setUser(QTest::currentTestFunction());
97+ AccountsService session(this, QTest::currentTestFunction());
9998
10099 QCOMPARE(session.demoEdges(), false);
101100 session.setDemoEdges(true);
@@ -112,8 +111,7 @@ private Q_SLOTS:
112111
113112 void testAsynchornousChangeForDemoEdges()
114113 {
115− AccountsService session;
116− session.setUser(QTest::currentTestFunction());
114+ AccountsService session(this, QTest::currentTestFunction());
117115
118116 QCOMPARE(session.demoEdges(), false);
119117 ASSERT_DBUS_CALL(m_userInterface->call("Set",
@@ -125,8 +123,7 @@ private Q_SLOTS:
125123
126124 void testAsynchornousChangeForFailedLogins()
127125 {
128− AccountsService session;
129− session.setUser(QTest::currentTestFunction());
126+ AccountsService session(this, QTest::currentTestFunction());
130127
131128 QCOMPARE(session.failedLogins(), (uint)0);
132129 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -138,8 +135,7 @@ private Q_SLOTS:
138135
139136 void testAsynchornousChangeForStatsWelcomeScreen()
140137 {
141− AccountsService session;
142− session.setUser(QTest::currentTestFunction());
138+ AccountsService session(this, QTest::currentTestFunction());
143139
144140 QCOMPARE(session.statsWelcomeScreen(), true);
145141 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -151,8 +147,7 @@ private Q_SLOTS:
151147
152148 void testAsynchornousChangeForStatsEnableLauncherWhileLocked()
153149 {
154− AccountsService session;
155− session.setUser(QTest::currentTestFunction());
150+ AccountsService session(this, QTest::currentTestFunction());
156151
157152 QCOMPARE(session.enableLauncherWhileLocked(), true);
158153 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -164,8 +159,7 @@ private Q_SLOTS:
164159
165160 void testAsynchornousChangeForStatsEnableIndicatorsWhileLocked()
166161 {
167− AccountsService session;
168− session.setUser(QTest::currentTestFunction());
162+ AccountsService session(this, QTest::currentTestFunction());
169163
170164 QCOMPARE(session.enableIndicatorsWhileLocked(), true);
171165 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -177,8 +171,7 @@ private Q_SLOTS:
177171
178172 void testAsynchornousChangeForStatsPasswordDisplayHint()
179173 {
180− AccountsService session;
181− session.setUser(QTest::currentTestFunction());
174+ AccountsService session(this, QTest::currentTestFunction());
182175
183176 QCOMPARE(session.passwordDisplayHint(), AccountsService::Keyboard);
184177 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -190,8 +183,7 @@ private Q_SLOTS:
190183
191184 void testAsynchornousChangeForStatsLicenseAccepted()
192185 {
193− AccountsService session;
194− session.setUser(QTest::currentTestFunction());
186+ AccountsService session(this, QTest::currentTestFunction());
195187
196188 QCOMPARE(session.hereEnabled(), false);
197189 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -203,8 +195,7 @@ private Q_SLOTS:
203195
204196 void testAsynchornousChangeForLicenseBasePath()
205197 {
206− AccountsService session;
207− session.setUser(QTest::currentTestFunction());
198+ AccountsService session(this, QTest::currentTestFunction());
208199
209200 QCOMPARE(session.hereLicensePath(), QString());
210201 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -216,8 +207,7 @@ private Q_SLOTS:
216207
217208 void testAsynchornousChangeForStatsBackgroundFile()
218209 {
219− AccountsService session;
220− session.setUser(QTest::currentTestFunction());
210+ AccountsService session(this, QTest::currentTestFunction());
221211
222212 QCOMPARE(session.backgroundFile(), QString());
223213 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
tests/plugins/Greeter/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)
22pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33
44include_directories(
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/LightDM/CMakeLists.txt+6
@@ -9,6 +9,9 @@ add_executable(GreeterDBusTestExec
99 dbus.cpp
1010 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
1111 )
12+# This add_dependencies is needed since we're linking with with -L and -l below
13+# make seems to work but ninja breaks without it
14+add_dependencies(GreeterDBusTestExec MockLightDM)
1215qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test)
1316target_link_libraries(GreeterDBusTestExec
1417 -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
@@ -44,6 +47,9 @@ add_executable(GreeterUsersModelTestExec
4447 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
4548 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
4649 )
50+# This add_dependencies is needed since we're linking with with -L and -l below
51+# make seems to work but ninja breaks without it
52+add_dependencies(GreeterUsersModelTestExec MockLightDM)
4753qt5_use_modules(GreeterUsersModelTestExec Core Test)
4854target_link_libraries(GreeterUsersModelTestExec
4955 -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
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+64−7
@@ -68,6 +68,7 @@ class MockAppManager: public unity::shell::application::ApplicationManagerInterf
6868 Q_OBJECT
6969public:
7070 MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
71+ ~MockAppManager() {}
7172 int rowCount(const QModelIndex &) const override { return m_list.count(); }
7273 QVariant data(const QModelIndex &, int ) const override { return QVariant(); }
7374 QString focusedApplicationId() const override {
@@ -341,6 +342,46 @@ private Q_SLOTS:
341342 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
342343 }
343344
345+ void testQuitMenuItem() {
346+ // we have 2 apps running, both should have the Quit action in its quick list
347+ QCOMPARE(launcherModel->rowCount(), 2);
348+
349+ // stop the second one keeping it pinned so that it doesn't go away
350+ launcherModel->pin("no-icon");
351+ appManager->stopApplication("no-icon");
352+
353+ // find the first Quit item, should be there
354+ QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
355+ int quitActionIndex = -1;
356+ for (int i = 0; i < model->rowCount(); ++i) {
357+ if (model->get(i).actionId() == "stop_item") {
358+ quitActionIndex = i;
359+ break;
360+ }
361+ }
362+ QVERIFY(quitActionIndex >= 0);
363+
364+ // find the second Quit item, should NOT be there, the app is stopped
365+ QuickListModel *model2 = qobject_cast<QuickListModel*>(launcherModel->get(1)->quickList());
366+ int quitActionIndex2 = -1;
367+ for (int i = 0; i < model2->rowCount(); ++i) {
368+ if (model2->get(i).actionId() == "stop_item") {
369+ quitActionIndex2 = i;
370+ break;
371+ }
372+ }
373+ QVERIFY(quitActionIndex2 == -1);
374+
375+ // trigger the first quit item quicklist action
376+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), quitActionIndex);
377+ // first app should be gone...
378+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
379+ // ... the second app (now at index 0) should still be there, pinned and stopped
380+ QCOMPARE(launcherModel->get(0)->appId(), QStringLiteral("no-icon"));
381+ QCOMPARE(launcherModel->get(0)->pinned(), true);
382+ QCOMPARE(launcherModel->get(0)->running(), false);
383+ }
384+
344385 void testGetUrlForAppId() {
345386 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
346387 QCOMPARE(launcherModel->getUrlForAppId(""), QString());
@@ -384,11 +425,18 @@ private Q_SLOTS:
384425 }
385426
386427 void testCountEmblems() {
428+ QSignalSpy spy(launcherModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
429+
387430 // Call GetAll on abs-icon
388431 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
389432 QDBusReply<QVariantMap> reply = interface.call("GetAll");
390433 QVariantMap map = reply.value();
391434
435+ // Check that the alerting-status is still false, and the item on the upper side of the API
436+ int index = launcherModel->findApplication("abs-icon");
437+ QCOMPARE(index >= 0, true);
438+ QVERIFY(launcherModel->get(index)->alerting() == false);
439+
392440 // Make sure GetAll returns a map with count and countVisible props
393441 QCOMPARE(map.contains("count"), true);
394442 QCOMPARE(map.contains("countVisible"), true);
@@ -409,13 +457,8 @@ private Q_SLOTS:
409457 QCOMPARE(map.value("count").toInt(), 55);
410458 QCOMPARE(map.value("countVisible").toBool(), true);
411459
412− // Now the item on the upper side of the API
413− int index = launcherModel->findApplication("abs-icon");
414− QCOMPARE(index >= 0, true);
415−
416− // And make sure values have changed there as well
417− QCOMPARE(launcherModel->get(index)->countVisible(), true);
418− QCOMPARE(launcherModel->get(index)->count(), 55);
460+ // Finally check, that the change to "count" implicitly also set the alerting-state to true
461+ QVERIFY(launcherModel->get(index)->alerting() == true);
419462 }
420463
421464 void testCountEmblemAddsRemovesItem_data() {
@@ -475,6 +518,20 @@ private Q_SLOTS:
475518 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
476519 }
477520
521+ void testAlert() {
522+ // Check that the alerting-status is still false
523+ int index = launcherModel->findApplication("abs-icon");
524+ QCOMPARE(index >= 0, true);
525+ QVERIFY(launcherModel->get(index)->alerting() == false);
526+
527+ // Call Alert() on "abs-icon"
528+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "com.canonical.Unity.Launcher.Item");
529+ interface.call("Alert");
530+
531+ // Check that the alerting-status is now true
532+ QVERIFY(launcherModel->get(index)->alerting() == true);
533+ }
534+
478535 void testRefreshAfterDeletedDesktopFiles_data() {
479536 QTest::addColumn<bool>("deleted");
480537 QTest::newRow("have .desktop files") << false;
tests/plugins/Unity/Session/sessionbackendtest.cpp+27−40
@@ -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
@@ -15,6 +15,7 @@
1515 *
1616 * Authors:
1717 * Ying-Chun Liu (PaulLiu) <paul.liu@canonical.com>
18+ * Lukáš Tinkl <lukas.tinkl@canonical.com>
1819 */
1920
2021#include <QtTest>
@@ -49,17 +50,22 @@ private Q_SLOTS:
4950
5051 void testUnitySessionLogoutRequested_data() {
5152 QTest::addColumn<QString>("method");
53+ QTest::addColumn<QString>("signal");
5254
53− QTest::newRow("Logout") << "RequestLogout";
55+ QTest::newRow("Logout") << "RequestLogout" << "logoutRequested(bool)";
56+ QTest::newRow("Reboot") << "RequestReboot" << "rebootRequested(bool)";
57+ QTest::newRow("Shutdown") << "RequestShutdown" << "shutdownRequested(bool)";
5458 }
5559
5660 void testUnitySessionLogoutRequested() {
5761 QFETCH(QString, method);
62+ QFETCH(QString, signal);
5863
5964 DBusUnitySessionService dbusUnitySessionService;
6065 QCoreApplication::processEvents(); // to let the service register on DBus
6166
62− QSignalSpy spy(&dbusUnitySessionService, SIGNAL(logoutRequested(bool)));
67+ // .. 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());
6369
6470 QDBusReply<void> reply = dbusUnitySession->call(method);
6571 QCOMPARE(reply.isValid(), true);
@@ -67,51 +73,34 @@ private Q_SLOTS:
6773 QCOMPARE(spy.count(), 1);
6874 }
6975
70− void testGnomeSessionWrapperLogoutRequested() {
71− DBusUnitySessionService dbusUnitySessionService;
72− QCoreApplication::processEvents(); // to let the service register on DBus
73−
74− // Spy on the logoutRequested signal on the /com/canonical/Unity/Session object
75− // as proof we are actually calling the actual method.
76− QSignalSpy spy(&dbusUnitySessionService, SIGNAL(logoutRequested(bool)));
76+ void testGnomeSessionWrapper_data() {
77+ QTest::addColumn<uint>("method");
78+ QTest::addColumn<QString>("signal");
7779
78− DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
79− QCoreApplication::processEvents(); // to let the service register on DBus
80−
81− QDBusInterface dbusGnomeSessionWrapper ("com.canonical.Unity",
82− "/org/gnome/SessionManager/EndSessionDialog",
83− "org.gnome.SessionManager.EndSessionDialog",
84− QDBusConnection::sessionBus());
85−
86− QCOMPARE(dbusGnomeSessionWrapper.isValid(), true);
87−
88− // Set the QVariant as a QList<QDBusObjectPath> type
89− QDbusList var;
90− QVariant inhibitors;
91− inhibitors.setValue(var);
92−
93− QDBusReply<void> reply = dbusGnomeSessionWrapper.call("Open", (unsigned)Action::LOGOUT, (unsigned)0, (unsigned)0, inhibitors);
94− QCOMPARE(reply.isValid(), true);
95−
96− // Make sure we see the signal being emitted.
97− QCOMPARE(spy.count(), 1);
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)";
9883 }
9984
100− void testGnomeSessionWrapperShutdownRequested() {
85+ void testGnomeSessionWrapper() {
86+ QFETCH(uint, method);
87+ QFETCH(QString, signal);
88+
10189 DBusUnitySessionService dbusUnitySessionService;
10290 QCoreApplication::processEvents(); // to let the service register on DBus
10391
104− // Spy on the shutdownRequested signal on the /com/canonical/Unity/Session object
92+ // Spy on the given signal on the /com/canonical/Unity/Session object
10593 // as proof we are actually calling the actual method.
106− QSignalSpy spy(&dbusUnitySessionService, SIGNAL(shutdownRequested(bool)));
94+ // .. 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());
10796
10897 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
10998 QCoreApplication::processEvents(); // to let the service register on DBus
11099
111− QDBusInterface dbusGnomeSessionWrapper ("com.canonical.Unity",
112− "/org/gnome/SessionManager/EndSessionDialog",
113− "org.gnome.SessionManager.EndSessionDialog",
114− QDBusConnection::sessionBus());
100+ QDBusInterface dbusGnomeSessionWrapper("com.canonical.Unity",
101+ "/org/gnome/SessionManager/EndSessionDialog",
102+ "org.gnome.SessionManager.EndSessionDialog",
103+ QDBusConnection::sessionBus());
115104
116105 QCOMPARE(dbusGnomeSessionWrapper.isValid(), true);
117106
@@ -120,9 +109,7 @@ private Q_SLOTS:
120109 QVariant inhibitors;
121110 inhibitors.setValue(var);
122111
123− // * Reboot action translates to the shutdown signal due to some weird idiosyncracy
124− // in the indicator-session/Unity interaction. *
125− QDBusReply<void> reply = dbusGnomeSessionWrapper.call("Open", (unsigned)Action::REBOOT, (unsigned)0, (unsigned)0, inhibitors);
112+ QDBusReply<void> reply = dbusGnomeSessionWrapper.call("Open", method, (unsigned)0, (unsigned)0, inhibitors);
126113 QCOMPARE(reply.isValid(), true);
127114
128115 // Make sure we see the signal being emitted.
tests/qmltests/CMakeLists.txt+2
@@ -27,6 +27,8 @@ add_unity8_qmltest(Dash PreviewListView)
2727add_unity8_qmltest(Dash/Previews Preview)
2828add_unity8_qmltest(Dash/Previews PreviewActions)
2929add_unity8_qmltest(Dash/Previews PreviewAudioPlayback)
30+add_unity8_qmltest(Dash/Previews PreviewComment)
31+add_unity8_qmltest(Dash/Previews PreviewCommentInput)
3032add_unity8_qmltest(Dash/Previews PreviewExpandable)
3133add_unity8_qmltest(Dash/Previews PreviewHeader)
3234add_unity8_qmltest(Dash/Previews PreviewImageGallery)
tests/qmltests/Dash/Previews/tst_PreviewComment.qmlnew file+84
@@ -0,0 +1,84 @@
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.0
18+import QtTest 1.0
19+import Ubuntu.Components 1.1
20+import "../../../../qml/Dash/Previews"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+ color: Theme.palette.selected.background
28+
29+ property var comment: {
30+ "author": "Claire Thompson",
31+ "subtitle": "28/04/2015 3:48pm",
32+ "comment": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
33+ "source": "../../graphics/avatars/amanda@12.png"
34+ }
35+
36+ property var commentNoImage: {
37+ "author": "Claire Thompson",
38+ "subtitle": "28/04/2015 3:48pm",
39+ "comment": "Been quite a while since I really liked one.",
40+ "source": ""
41+ }
42+
43+ property var commentNoSubtitle: {
44+ "author": "Claire Thompson",
45+ "comment": "Been quite a while since I really liked one.",
46+ "source": "../../graphics/avatars/amanda@12.png"
47+ }
48+
49+ PreviewComment {
50+ id: previewComment
51+ anchors.left: parent.left
52+ anchors.right: parent.right
53+ widgetData: comment
54+ widgetId: "previewComment"
55+ }
56+
57+ UT.UnityTestCase {
58+ name: "PreviewCommentTest"
59+ when: windowShown
60+
61+ function init() {
62+ previewComment.widgetData = comment;
63+ }
64+
65+ function test_AnchorsNoAvatar() {
66+ var column = findChild(previewComment, "column");
67+ var avatar = findChild(previewComment, "avatar");
68+
69+ compare(previewComment.widgetData, comment);
70+ compare(avatar.visible, true);
71+ compare(column.anchors.left, avatar.anchors.right);
72+
73+ previewComment.widgetData = commentNoImage;
74+ tryCompare(avatar, "visible", false);
75+ compare(column.anchors.left, previewComment.anchors.left);
76+ }
77+
78+ function test_OptionalSubtitle() {
79+ var subtitle = findChild(previewComment, "subtitle");
80+ previewComment.widgetData = commentNoSubtitle;
81+ tryCompare(subtitle, "visible", false);
82+ }
83+ }
84+}
tests/qmltests/Dash/Previews/tst_PreviewCommentInput.qmlnew file+85
@@ -0,0 +1,85 @@
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.0
18+import QtTest 1.0
19+import Ubuntu.Components 1.1
20+import "../../../../qml/Dash/Previews"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+ color: Theme.palette.selected.background
28+
29+ property string validInputText: "Great!"
30+ property string invalidInputText: ""
31+
32+ property var widgetDataNewLabels: { "submit-label": "TestSubmitLabel" }
33+
34+ PreviewCommentInput {
35+ id: previewCommentInput
36+ anchors.left: parent.left
37+ anchors.right: parent.right
38+ widgetData: widgetDataNewLabels
39+ widgetId: "previewCommentInput"
40+ }
41+
42+ SignalSpy {
43+ id: spy
44+ signalName: "triggered"
45+ }
46+
47+ UT.UnityTestCase {
48+ name: "PreviewCommentInputTest"
49+ when: windowShown
50+
51+ property var commentTextArea: findChild(previewCommentInput, "commentTextArea")
52+ property var submitButton: findChild(previewCommentInput, "submitButton")
53+
54+ function init() {
55+ commentTextArea.text = "";
56+ }
57+
58+ function test_labels() {
59+ previewCommentInput.widgetData = widgetDataNewLabels;
60+ compare(submitButton.text, widgetDataNewLabels["submit-label"]);
61+ }
62+
63+ function test_submit_data() {
64+ return [
65+ {tag: "empty review", inputText: invalidInputText, emitted: false},
66+ {tag: "filled review", inputText: validInputText, emitted: true},
67+ ];
68+ }
69+
70+ function test_submit(data) {
71+ spy.clear();
72+ spy.target = previewCommentInput;
73+
74+ commentTextArea.text = data.inputText;
75+ mouseClick(submitButton);
76+ if (!data.emitted) {
77+ compare(spy.count, 0);
78+ } else {
79+ compare(spy.count, 1);
80+ var args = spy.signalArguments[0];
81+ compare(args[2]["comment"], data.inputText);
82+ }
83+ }
84+ }
85+}
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+2
@@ -77,6 +77,8 @@ Rectangle {
7777 return [
7878 { tag: "Actions", type: "actions", source: "PreviewActions.qml" },
7979 { tag: "Audio", type: "audio", source: "PreviewAudioPlayback.qml" },
80+ { tag: "Comment", type: "comment", source: "PreviewComment.qml" },
81+ { tag: "Comment Input", type: "comment-input", source: "PreviewCommentInput.qml" },
8082 { tag: "Expandable", type: "expandable", source: "PreviewExpandable.qml" },
8183 { tag: "Gallery", type: "gallery", source: "PreviewImageGallery.qml" },
8284 { tag: "Header", type: "header", source: "PreviewHeader.qml" },
tests/qmltests/Launcher/tst_Launcher.qml+210−7
@@ -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
@@ -547,7 +608,7 @@ Item {
547608
548609 // Doing longpress
549610 mousePress(draggedItem);
550− tryCompare(quickListShape, "opacity", 0.96);
611+ tryCompare(quickListShape, "opacity", 0.8);
551612 mouseRelease(draggedItem);
552613
553614 verify(quickList.y >= units.gu(1));
@@ -632,9 +693,31 @@ Item {
632693 tryCompare(quickList, "state", "");
633694 }
634695
696+ function test_quickListMenuOnRMB() {
697+ dragLauncherIntoView();
698+ var clickedItem = findChild(launcher, "launcherDelegate5")
699+ var quickList = findChild(launcher, "quickList")
700+ var quickListShape = findChild(launcher, "quickListShape")
701+ var dndArea = findChild(launcher, "dndArea");
702+
703+ // Initial state
704+ tryCompare(quickListShape, "visible", false)
705+
706+ // Doing RMB click
707+ mouseClick(clickedItem, clickedItem.width / 2, clickedItem.height / 2, Qt.RightButton)
708+ tryCompare(quickListShape, "visible", true)
709+ verify(quickList, "state", "open")
710+ verify(dndArea, "dragging", false)
711+
712+ // Click somewhere in the empty space to dismiss the quicklist
713+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
714+ tryCompare(quickListShape, "visible", false);
715+ verify(quickList, "state", "")
716+ }
717+
635718 function test_revealByHover() {
636719 var panel = findChild(launcher, "launcherPanel");
637− verify(panel != undefined);
720+ verify(!!panel);
638721
639722 revealByHover();
640723 tryCompare(launcher, "state", "visibleTemporary");
@@ -645,5 +728,125 @@ Item {
645728 tryCompare(launcher, "state", "", 1000, "Launcher didn't hide after moving mouse away from it");
646729 waitUntilLauncherDisappears();
647730 }
731+
732+ function test_progressChangeViaModel() {
733+ dragLauncherIntoView();
734+ var item = findChild(launcher, "launcherDelegate0")
735+ verify(item != undefined)
736+ LauncherModel.setProgress(LauncherModel.get(0).appId, -1)
737+ compare(findChild(item, "progressOverlay").visible, false)
738+ LauncherModel.setProgress(LauncherModel.get(0).appId, 20)
739+ compare(findChild(item, "progressOverlay").visible, true)
740+ LauncherModel.setProgress(LauncherModel.get(0).appId, 0)
741+ }
742+
743+ function test_alertPeekingIcon() {
744+ var listView = findChild(launcher, "launcherListView")
745+ verify(listView != undefined)
746+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
747+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
748+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
749+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
750+ }
751+
752+ function test_alertHidingIcon() {
753+ var listView = findChild(launcher, "launcherListView")
754+ verify(listView != undefined)
755+ var appIcon6 = findChild(launcher, "launcherDelegate6")
756+ verify(appIcon6 != undefined)
757+ LauncherModel.setAlerting(LauncherModel.get(6).appId, true)
758+ waitForWiggleToStart(appIcon6)
759+ LauncherModel.setAlerting(LauncherModel.get(6).appId, false)
760+ waitForWiggleToStop(appIcon6)
761+ tryCompare(appIcon6, "x", 0, 1000, "x-value of appId #6 should not be non-zero")
762+ waitForRendering(listView)
763+ }
764+
765+ function test_alertIgnoreFocusedApp() {
766+ LauncherModel.setAlerting(LauncherModel.get(0).appId, true)
767+ compare(LauncherModel.get(0).alerting, false, "Focused app should not have the alert-state set")
768+ }
769+
770+ function test_alertOnlyOnePeekingIcon() {
771+ var listView = findChild(launcher, "launcherListView")
772+ verify(listView != undefined)
773+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
774+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
775+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
776+ tryCompare(listView, "peekingIndex", 3, 1000, "Wrong appId set as peeking-index")
777+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
778+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
779+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
780+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
781+ waitForRendering(listView)
782+ }
783+
784+ function test_alertMultipleApps() {
785+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
786+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
787+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
788+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
789+ compare(LauncherModel.get(1).alerting, true, "Alert-state of appId #1 should not be false")
790+ compare(LauncherModel.get(3).alerting, true, "Alert-state of appId #3 should not be false")
791+ compare(LauncherModel.get(5).alerting, true, "Alert-state of appId #5 should not be false")
792+ compare(LauncherModel.get(7).alerting, true, "Alert-state of appId #7 should not be false")
793+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
794+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
795+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
796+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
797+ compare(LauncherModel.get(1).alerting, false, "Alert-state of appId #1 should not be true")
798+ compare(LauncherModel.get(3).alerting, false, "Alert-state of appId #1 should not be true")
799+ compare(LauncherModel.get(5).alerting, false, "Alert-state of appId #1 should not be true")
800+ compare(LauncherModel.get(7).alerting, false, "Alert-state of appId #1 should not be true")
801+ }
802+
803+ function test_alertMoveIconIntoView() {
804+ dragLauncherIntoView();
805+ var appIcon1 = findChild(launcher, "launcherDelegate1");
806+ var appIcon7 = findChild(launcher, "launcherDelegate7");
807+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
808+ tryCompare(appIcon1, "angle", 0, 1000, "angle of appId #1 should not be non-zero")
809+ waitForWiggleToStart(appIcon1)
810+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
811+ tryCompare(appIcon7, "angle", 0, 1000, "angle of appId #7 should not be non-zero")
812+ waitForWiggleToStart(appIcon7)
813+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
814+ waitForWiggleToStop(appIcon1)
815+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
816+ waitForWiggleToStop(appIcon7)
817+ }
818+
819+ function test_alertWigglePeekDrag() {
820+ var appIcon5 = findChild(launcher, "launcherDelegate5");
821+ var listView = findChild(launcher, "launcherListView")
822+ verify(listView != undefined)
823+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
824+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
825+ waitForWiggleToStart(appIcon5)
826+ tryCompare(appIcon5, "wiggling", true, 1000, "appId #6 should not be still")
827+ dragLauncherIntoView();
828+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
829+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
830+ waitForWiggleToStop(appIcon5)
831+ tryCompare(appIcon5, "wiggling", false, 1000, "appId #1 should not be wiggling")
832+ }
833+
834+ function test_alertViaCountAndCountVisible() {
835+ dragLauncherIntoView();
836+ var appIcon1 = findChild(launcher, "launcherDelegate1")
837+ var oldCount = LauncherModel.get(1).count
838+ LauncherModel.setCount(LauncherModel.get(1).appId, 42)
839+ tryCompare(appIcon1, "wiggling", false, 1000, "appId #1 should be still")
840+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 1)
841+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
842+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
843+ waitForWiggleToStop(appIcon1)
844+ LauncherModel.setCount(LauncherModel.get(1).appId, 4711)
845+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
846+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
847+ waitForWiggleToStop(appIcon1)
848+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 0)
849+ LauncherModel.setCount(LauncherModel.get(1).appId, oldCount)
850+ }
648851 }
649852}
tests/qmltests/Tutorial/tst_Tutorial.qml+31−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013,2014 Canonical, Ltd.
2+ * Copyright (C) 2013,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
@@ -319,6 +319,36 @@ Item {
319319 tryCompare(left, "shown", true); // and we should still be on left
320320 }
321321
322+ function test_launcherNoDragGap() {
323+ // See bug 1454882, where if you dragged the launcher while it was
324+ // visible, you could pull it further than the edge of the screen.
325+
326+ var left = goToPage("tutorialLeft");
327+ var launcher = findChild(shell, "launcher");
328+ var teaseAnimation = findInvisibleChild(left, "teaseAnimation");
329+
330+ // Wait for launcher to be really out there
331+ tryCompareFunction(function() {return launcher.x > teaseAnimation.maxBounce/2}, true);
332+ verify(teaseAnimation.running);
333+
334+ // Start a drag, make sure animation stops
335+ touchFlick(shell, 0, halfHeight, units.gu(4), halfHeight, true, false);
336+ verify(!teaseAnimation.running);
337+ verify(launcher.visibleWidth > 0);
338+ verify(launcher.x > 0);
339+ compare(launcher.x, teaseAnimation.bounce);
340+
341+ // Continue drag, make sure we don't create a gap on the left hand side
342+ touchFlick(shell, units.gu(4), halfHeight, shell.width, halfHeight, false, false);
343+ verify(!teaseAnimation.running);
344+ compare(launcher.visibleWidth, launcher.panelWidth);
345+ compare(launcher.x, 0);
346+
347+ // Finish and make sure we continue animation
348+ touchFlick(shell, shell.width, halfHeight, shell.width, halfHeight, false, true);
349+ tryCompare(teaseAnimation, "running", true);
350+ }
351+
322352 function test_spread() {
323353 // Unfortunately, most of what we want to test of the spread is
324354 // "did it render correctly?" but that's hard to test. So instead,
tests/qmltests/tst_ShellWithPin.qml+1−1
@@ -313,7 +313,7 @@ Item {
313313 tryCompare(panel, "fullscreenMode", false)
314314 tryCompare(indicators, "available", true)
315315 tryCompare(launcher, "available", true)
316− tryCompare(stage, "spreadEnabled", true)
316+ tryCompare(stage, "spreadEnabled", false)
317317 }
318318
319319 function test_emergencyCallCrash() {
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
tools/CMakeLists.txt+1
@@ -1,6 +1,7 @@
11add_executable(${SCOPE_TOOL}
22 scopetool.cpp
33 registry-tracker.cpp
4+ unix-signal-handler.cpp
45 )
56
67qt5_use_modules(${SCOPE_TOOL} Qml Quick)
tools/scopetool.cpp+6
@@ -32,6 +32,7 @@
3232// local
3333#include <paths.h>
3434#include "registry-tracker.h"
35+#include "unix-signal-handler.h"
3536
3637
3738int main(int argc, char *argv[])
@@ -96,6 +97,11 @@ int main(int argc, char *argv[])
9697
9798 view->show();
9899
100+ UnixSignalHandler signal_handler([]{
101+ QGuiApplication::exit(0);
102+ });
103+ signal_handler.setupUnixSignalHandlers();
104+
99105 int result = application->exec();
100106
101107 delete view;
tools/unix-signal-handler.cppnew file+141
@@ -0,0 +1,141 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License along
14+ * with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Marcus Tomlinson <marcus.tomlinson@canonical.com>
17+ */
18+
19+#include "unix-signal-handler.h"
20+
21+#include <QDebug>
22+
23+#include <csignal>
24+#include <sys/socket.h>
25+#include <unistd.h>
26+
27+int UnixSignalHandler::sighupFd[2];
28+int UnixSignalHandler::sigintFd[2];
29+int UnixSignalHandler::sigtermFd[2];
30+
31+UnixSignalHandler::UnixSignalHandler(const std::function<void()>& f, QObject *parent)
32+ : QObject(parent),
33+ m_func(f)
34+{
35+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighupFd))
36+ {
37+ qFatal("Couldn't create HUP socketpair");
38+ }
39+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd))
40+ {
41+ qFatal("Couldn't create INT socketpair");
42+ }
43+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigtermFd))
44+ {
45+ qFatal("Couldn't create TERM socketpair");
46+ }
47+
48+ m_socketNotifierHup = new QSocketNotifier(sighupFd[1], QSocketNotifier::Read, this);
49+ connect(m_socketNotifierHup, &QSocketNotifier::activated, this, &UnixSignalHandler::handleSigHup);
50+ m_socketNotifierInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this);
51+ connect(m_socketNotifierInt, &QSocketNotifier::activated, this, &UnixSignalHandler::handleSigInt);
52+ m_socketNotifierTerm = new QSocketNotifier(sigtermFd[1], QSocketNotifier::Read, this);
53+ connect(m_socketNotifierTerm, &QSocketNotifier::activated, this, &UnixSignalHandler::handleSigTerm);
54+}
55+
56+void UnixSignalHandler::hupSignalHandler(int)
57+{
58+ char a = 1;
59+ ::write(sighupFd[0], &a, sizeof(a));
60+}
61+
62+void UnixSignalHandler::intSignalHandler(int)
63+{
64+ char a = 1;
65+ ::write(sigintFd[0], &a, sizeof(a));
66+}
67+
68+void UnixSignalHandler::termSignalHandler(int)
69+{
70+ char a = 1;
71+ ::write(sigtermFd[0], &a, sizeof(a));
72+}
73+
74+int UnixSignalHandler::setupUnixSignalHandlers()
75+{
76+ struct sigaction sighup, sigint, sigterm;
77+
78+ sighup.sa_handler = UnixSignalHandler::hupSignalHandler;
79+ sigemptyset(&sighup.sa_mask);
80+ sighup.sa_flags = 0;
81+ sighup.sa_flags |= SA_RESTART;
82+
83+ if (sigaction(SIGHUP, &sighup, 0) > 0)
84+ {
85+ return 1;
86+ }
87+
88+ sigint.sa_handler = UnixSignalHandler::intSignalHandler;
89+ sigemptyset(&sigint.sa_mask);
90+ sigint.sa_flags = 0;
91+ sigint.sa_flags |= SA_RESTART;
92+
93+ if (sigaction(SIGINT, &sigint, 0) > 0)
94+ {
95+ return 2;
96+ }
97+
98+ sigterm.sa_handler = UnixSignalHandler::termSignalHandler;
99+ sigemptyset(&sigterm.sa_mask);
100+ sigterm.sa_flags |= SA_RESTART;
101+
102+ if (sigaction(SIGTERM, &sigterm, 0) > 0)
103+ {
104+ return 3;
105+ }
106+
107+ return 0;
108+}
109+
110+void UnixSignalHandler::handleSigHup()
111+{
112+ m_socketNotifierHup->setEnabled(false);
113+ char tmp;
114+ ::read(sighupFd[1], &tmp, sizeof(tmp));
115+
116+ m_func();
117+
118+ m_socketNotifierHup->setEnabled(true);
119+}
120+
121+void UnixSignalHandler::handleSigInt()
122+{
123+ m_socketNotifierInt->setEnabled(false);
124+ char tmp;
125+ ::read(sigintFd[1], &tmp, sizeof(tmp));
126+
127+ m_func();
128+
129+ m_socketNotifierInt->setEnabled(true);
130+}
131+
132+void UnixSignalHandler::handleSigTerm()
133+{
134+ m_socketNotifierTerm->setEnabled(false);
135+ char tmp;
136+ ::read(sigtermFd[1], &tmp, sizeof(tmp));
137+
138+ m_func();
139+
140+ m_socketNotifierTerm->setEnabled(true);
141+}
tools/unix-signal-handler.hnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License along
14+ * with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Marcus Tomlinson <marcus.tomlinson@canonical.com>
17+ */
18+
19+#pragma once
20+
21+#include <QObject>
22+#include <QSocketNotifier>
23+
24+#include <functional>
25+
26+class UnixSignalHandler: public QObject {
27+Q_OBJECT
28+
29+public:
30+ UnixSignalHandler(const std::function<void()>& f, QObject *parent = 0);
31+ ~UnixSignalHandler() = default;
32+
33+ static int setupUnixSignalHandlers();
34+
35+protected Q_SLOTS:
36+ void handleSigHup();
37+ void handleSigInt();
38+ void handleSigTerm();
39+
40+protected:
41+ static void hupSignalHandler(int unused);
42+ static void intSignalHandler(int unused);
43+ static void termSignalHandler(int unused);
44+
45+ static int sighupFd[2];
46+ static int sigintFd[2];
47+ static int sigtermFd[2];
48+
49+ std::function<void()> m_func;
50+
51+ QSocketNotifier *m_socketNotifierHup;
52+ QSocketNotifier *m_socketNotifierInt;
53+ QSocketNotifier *m_socketNotifierTerm;
54+};