on xenial help sign in
[ Albert Astals ]
3267735Josh Arenson
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+
1unity8 (8.10+15.10.20150713-0ubuntu1) wily; urgency=medium
35unity8 (8.10+15.10.20150713-0ubuntu1) wily; urgency=medium
2
36
3 [ Albert Astals Cid ]
37 [ Albert Astals Cid ]
debian/control+2
@@ -150,6 +150,7 @@ Depends: autopilot-qt5 (>= 1.4),
150 gir1.2-notify-0.7,
150 gir1.2-notify-0.7,
151 libqt5test5,
151 libqt5test5,
152 libqt5widgets5,
152 libqt5widgets5,
153+ ofono-phonesim,
153 python3-autopilot,
154 python3-autopilot,
154 python3-evdev,
155 python3-evdev,
155 python3-fixtures,
156 python3-fixtures,
@@ -160,6 +161,7 @@ Depends: autopilot-qt5 (>= 1.4),
160 unity8 (= ${source:Version}),
161 unity8 (= ${source:Version}),
161 unity8-fake-env (= ${source:Version}),
162 unity8-fake-env (= ${source:Version}),
162 url-dispatcher-tools,
163 url-dispatcher-tools,
164+ xvfb,
163 ${misc:Depends},
165 ${misc:Depends},
164 ${python3:Depends},
166 ${python3:Depends},
165 ${shlibs:Depends},
167 ${shlibs:Depends},
plugins/AccountsService/AccountsService.cpp+31−42
@@ -23,10 +23,9 @@
23#include <QStringList>
23#include <QStringList>
24#include <QDebug>
24#include <QDebug>
25
25
26−AccountsService::AccountsService(QObject* parent)
26+AccountsService::AccountsService(QObject* parent, const QString &user)
27− : QObject(parent),
27+ : QObject(parent),
28 m_service(new AccountsServiceDBusAdaptor(this)),
28 m_service(new AccountsServiceDBusAdaptor(this)),
29− m_user(""),
30 m_demoEdges(false),
29 m_demoEdges(false),
31 m_enableLauncherWhileLocked(false),
30 m_enableLauncherWhileLocked(false),
32 m_enableIndicatorsWhileLocked(false),
31 m_enableIndicatorsWhileLocked(false),
@@ -36,12 +35,10 @@ AccountsService::AccountsService(QObject* parent)
36 m_hereEnabled(false),
35 m_hereEnabled(false),
37 m_hereLicensePath() // null means not set yet
36 m_hereLicensePath() // null means not set yet
38{
37{
39− connect(m_service, SIGNAL(propertiesChanged(const QString &, const QString &, const QStringList &)),
38+ connect(m_service, &AccountsServiceDBusAdaptor::propertiesChanged, this, &AccountsService::onPropertiesChanged);
40− this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
41− connect(m_service, SIGNAL(maybeChanged(const QString &)),
39+ connect(m_service, &AccountsServiceDBusAdaptor::maybeChanged, this, &AccountsService::onMaybeChanged);
42− this, SLOT(maybeChanged(const QString &)));
43
40
44− setUser(qgetenv("USER"));
41+ setUser(!user.isEmpty() ? user : QString::fromUtf8(qgetenv("USER")));
45}
42}
46
43
47QString AccountsService::user() const
44QString AccountsService::user() const
@@ -173,19 +170,18 @@ void AccountsService::updateEnableLauncherWhileLocked(bool async)
173 connect(watcher, &QDBusPendingCallWatcher::finished,
170 connect(watcher, &QDBusPendingCallWatcher::finished,
174 this, [this](QDBusPendingCallWatcher* watcher) {
171 this, [this](QDBusPendingCallWatcher* watcher) {
175
172
176− QDBusPendingReply<QDBusVariant> reply = *watcher;
173+ QDBusPendingReply<QVariant> reply = *watcher;
174+ watcher->deleteLater();
177 if (reply.isError()) {
175 if (reply.isError()) {
178 qWarning() << "Failed to get 'EnableLauncherWhileLocked' property - " << reply.error().message();
176 qWarning() << "Failed to get 'EnableLauncherWhileLocked' property - " << reply.error().message();
179− watcher->deleteLater();
180 return;
177 return;
181 }
178 }
182
179
183− auto enableLauncherWhileLocked = reply.value().variant().toBool();
180+ const bool enableLauncherWhileLocked = reply.value().toBool();
184 if (m_enableLauncherWhileLocked != enableLauncherWhileLocked) {
181 if (m_enableLauncherWhileLocked != enableLauncherWhileLocked) {
185 m_enableLauncherWhileLocked = enableLauncherWhileLocked;
182 m_enableLauncherWhileLocked = enableLauncherWhileLocked;
186 Q_EMIT enableLauncherWhileLockedChanged();
183 Q_EMIT enableLauncherWhileLockedChanged();
187 }
184 }
188− watcher->deleteLater();
189 });
185 });
190 if (!async) {
186 if (!async) {
191 watcher->waitForFinished();
187 watcher->waitForFinished();
@@ -203,19 +199,18 @@ void AccountsService::updateEnableIndicatorsWhileLocked(bool async)
203 connect(watcher, &QDBusPendingCallWatcher::finished,
199 connect(watcher, &QDBusPendingCallWatcher::finished,
204 this, [this](QDBusPendingCallWatcher* watcher) {
200 this, [this](QDBusPendingCallWatcher* watcher) {
205
201
206− QDBusPendingReply<QDBusVariant> reply = *watcher;
202+ QDBusPendingReply<QVariant> reply = *watcher;
203+ watcher->deleteLater();
207 if (reply.isError()) {
204 if (reply.isError()) {
208 qWarning() << "Failed to get 'EnableIndicatorsWhileLocked' property - " << reply.error().message();
205 qWarning() << "Failed to get 'EnableIndicatorsWhileLocked' property - " << reply.error().message();
209− watcher->deleteLater();
210 return;
206 return;
211 }
207 }
212
208
213− auto enableIndicatorsWhileLocked = reply.value().variant().toBool();
209+ const bool enableIndicatorsWhileLocked = reply.value().toBool();
214 if (m_enableIndicatorsWhileLocked != enableIndicatorsWhileLocked) {
210 if (m_enableIndicatorsWhileLocked != enableIndicatorsWhileLocked) {
215 m_enableIndicatorsWhileLocked = enableIndicatorsWhileLocked;
211 m_enableIndicatorsWhileLocked = enableIndicatorsWhileLocked;
216 Q_EMIT enableIndicatorsWhileLockedChanged();
212 Q_EMIT enableIndicatorsWhileLockedChanged();
217 }
213 }
218− watcher->deleteLater();
219 });
214 });
220 if (!async) {
215 if (!async) {
221 watcher->waitForFinished();
216 watcher->waitForFinished();
@@ -233,19 +228,18 @@ void AccountsService::updateBackgroundFile(bool async)
233 connect(watcher, &QDBusPendingCallWatcher::finished,
228 connect(watcher, &QDBusPendingCallWatcher::finished,
234 this, [this](QDBusPendingCallWatcher* watcher) {
229 this, [this](QDBusPendingCallWatcher* watcher) {
235
230
236− QDBusPendingReply<QDBusVariant> reply = *watcher;
231+ QDBusPendingReply<QVariant> reply = *watcher;
232+ watcher->deleteLater();
237 if (reply.isError()) {
233 if (reply.isError()) {
238 qWarning() << "Failed to get 'BackgroundFile' property - " << reply.error().message();
234 qWarning() << "Failed to get 'BackgroundFile' property - " << reply.error().message();
239− watcher->deleteLater();
240 return;
235 return;
241 }
236 }
242
237
243− auto backgroundFile = reply.value().variant().toString();
238+ const QString backgroundFile = reply.value().toString();
244 if (m_backgroundFile != backgroundFile) {
239 if (m_backgroundFile != backgroundFile) {
245 m_backgroundFile = backgroundFile;
240 m_backgroundFile = backgroundFile;
246 Q_EMIT backgroundFileChanged();
241 Q_EMIT backgroundFileChanged();
247 }
242 }
248− watcher->deleteLater();
249 });
243 });
250 if (!async) {
244 if (!async) {
251 watcher->waitForFinished();
245 watcher->waitForFinished();
@@ -263,19 +257,18 @@ void AccountsService::updateStatsWelcomeScreen(bool async)
263 connect(watcher, &QDBusPendingCallWatcher::finished,
257 connect(watcher, &QDBusPendingCallWatcher::finished,
264 this, [this](QDBusPendingCallWatcher* watcher) {
258 this, [this](QDBusPendingCallWatcher* watcher) {
265
259
266− QDBusPendingReply<QDBusVariant> reply = *watcher;
260+ QDBusPendingReply<QVariant> reply = *watcher;
261+ watcher->deleteLater();
267 if (reply.isError()) {
262 if (reply.isError()) {
268 qWarning() << "Failed to get 'StatsWelcomeScreen' property - " << reply.error().message();
263 qWarning() << "Failed to get 'StatsWelcomeScreen' property - " << reply.error().message();
269− watcher->deleteLater();
270 return;
264 return;
271 }
265 }
272
266
273− auto statsWelcomeScreen = reply.value().variant().toBool();
267+ const bool statsWelcomeScreen = reply.value().toBool();
274 if (m_statsWelcomeScreen != statsWelcomeScreen) {
268 if (m_statsWelcomeScreen != statsWelcomeScreen) {
275 m_statsWelcomeScreen = statsWelcomeScreen;
269 m_statsWelcomeScreen = statsWelcomeScreen;
276 Q_EMIT statsWelcomeScreenChanged();
270 Q_EMIT statsWelcomeScreenChanged();
277 }
271 }
278− watcher->deleteLater();
279 });
272 });
280 if (!async) {
273 if (!async) {
281 watcher->waitForFinished();
274 watcher->waitForFinished();
@@ -293,19 +286,18 @@ void AccountsService::updatePasswordDisplayHint(bool async)
293 connect(watcher, &QDBusPendingCallWatcher::finished,
286 connect(watcher, &QDBusPendingCallWatcher::finished,
294 this, [this](QDBusPendingCallWatcher* watcher) {
287 this, [this](QDBusPendingCallWatcher* watcher) {
295
288
296− QDBusPendingReply<QDBusVariant> reply = *watcher;
289+ QDBusPendingReply<QVariant> reply = *watcher;
290+ watcher->deleteLater();
297 if (reply.isError()) {
291 if (reply.isError()) {
298 qWarning() << "Failed to get 'PasswordDisplayHint' property - " << reply.error().message();
292 qWarning() << "Failed to get 'PasswordDisplayHint' property - " << reply.error().message();
299− watcher->deleteLater();
300 return;
293 return;
301 }
294 }
302
295
303− PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)reply.value().variant().toInt();
296+ const PasswordDisplayHint passwordDisplayHint = (PasswordDisplayHint)reply.value().toInt();
304 if (m_passwordDisplayHint != passwordDisplayHint) {
297 if (m_passwordDisplayHint != passwordDisplayHint) {
305 m_passwordDisplayHint = passwordDisplayHint;
298 m_passwordDisplayHint = passwordDisplayHint;
306 Q_EMIT passwordDisplayHintChanged();
299 Q_EMIT passwordDisplayHintChanged();
307 }
300 }
308− watcher->deleteLater();
309 });
301 });
310 if (!async) {
302 if (!async) {
311 watcher->waitForFinished();
303 watcher->waitForFinished();
@@ -323,19 +315,18 @@ void AccountsService::updateFailedLogins(bool async)
323 connect(watcher, &QDBusPendingCallWatcher::finished,
315 connect(watcher, &QDBusPendingCallWatcher::finished,
324 this, [this](QDBusPendingCallWatcher* watcher) {
316 this, [this](QDBusPendingCallWatcher* watcher) {
325
317
326− QDBusPendingReply<QDBusVariant> reply = *watcher;
318+ QDBusPendingReply<QVariant> reply = *watcher;
319+ watcher->deleteLater();
327 if (reply.isError()) {
320 if (reply.isError()) {
328 qWarning() << "Failed to get 'FailedLogins' property - " << reply.error().message();
321 qWarning() << "Failed to get 'FailedLogins' property - " << reply.error().message();
329− watcher->deleteLater();
330 return;
322 return;
331 }
323 }
332
324
333− uint failedLogins = reply.value().variant().toUInt();
325+ const uint failedLogins = reply.value().toUInt();
334 if (m_failedLogins != failedLogins) {
326 if (m_failedLogins != failedLogins) {
335 m_failedLogins = failedLogins;
327 m_failedLogins = failedLogins;
336 Q_EMIT failedLoginsChanged();
328 Q_EMIT failedLoginsChanged();
337 }
329 }
338− watcher->deleteLater();
339 });
330 });
340 if (!async) {
331 if (!async) {
341 watcher->waitForFinished();
332 watcher->waitForFinished();
@@ -353,19 +344,18 @@ void AccountsService::updateHereEnabled(bool async)
353 connect(watcher, &QDBusPendingCallWatcher::finished,
344 connect(watcher, &QDBusPendingCallWatcher::finished,
354 this, [this](QDBusPendingCallWatcher* watcher) {
345 this, [this](QDBusPendingCallWatcher* watcher) {
355
346
356− QDBusPendingReply<QDBusVariant> reply = *watcher;
347+ QDBusPendingReply<QVariant> reply = *watcher;
348+ watcher->deleteLater();
357 if (reply.isError()) {
349 if (reply.isError()) {
358 qWarning() << "Failed to get 'LicenseAccepted' property - " << reply.error().message();
350 qWarning() << "Failed to get 'LicenseAccepted' property - " << reply.error().message();
359− watcher->deleteLater();
360 return;
351 return;
361 }
352 }
362
353
363− auto hereEnabled = reply.value().variant().toBool();
354+ const bool hereEnabled = reply.value().toBool();
364 if (m_hereEnabled != hereEnabled) {
355 if (m_hereEnabled != hereEnabled) {
365 m_hereEnabled = hereEnabled;
356 m_hereEnabled = hereEnabled;
366 Q_EMIT hereEnabledChanged();
357 Q_EMIT hereEnabledChanged();
367 }
358 }
368− watcher->deleteLater();
369 });
359 });
370 if (!async) {
360 if (!async) {
371 watcher->waitForFinished();
361 watcher->waitForFinished();
@@ -383,14 +373,14 @@ void AccountsService::updateHereLicensePath(bool async)
383 connect(watcher, &QDBusPendingCallWatcher::finished,
373 connect(watcher, &QDBusPendingCallWatcher::finished,
384 this, [this](QDBusPendingCallWatcher* watcher) {
374 this, [this](QDBusPendingCallWatcher* watcher) {
385
375
386− QDBusPendingReply<QDBusVariant> reply = *watcher;
376+ QDBusPendingReply<QVariant> reply = *watcher;
377+ watcher->deleteLater();
387 if (reply.isError()) {
378 if (reply.isError()) {
388 qWarning() << "Failed to get 'LicenseBasePath' property - " << reply.error().message();
379 qWarning() << "Failed to get 'LicenseBasePath' property - " << reply.error().message();
389− watcher->deleteLater();
390 return;
380 return;
391 }
381 }
392
382
393− auto hereLicensePath = reply.value().variant().toString();
383+ QString hereLicensePath = reply.value().toString();
394 if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
384 if (hereLicensePath.isEmpty() || !QFile::exists(hereLicensePath))
395 hereLicensePath = "";
385 hereLicensePath = "";
396
386
@@ -398,7 +388,6 @@ void AccountsService::updateHereLicensePath(bool async)
398 m_hereLicensePath = hereLicensePath;
388 m_hereLicensePath = hereLicensePath;
399 Q_EMIT hereLicensePathChanged();
389 Q_EMIT hereLicensePathChanged();
400 }
390 }
401− watcher->deleteLater();
402 });
391 });
403 if (!async) {
392 if (!async) {
404 watcher->waitForFinished();
393 watcher->waitForFinished();
@@ -421,7 +410,7 @@ void AccountsService::setFailedLogins(uint failedLogins)
421 }
410 }
422}
411}
423
412
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)
425{
414{
426 if (m_user != user) {
415 if (m_user != user) {
427 return;
416 return;
@@ -459,7 +448,7 @@ void AccountsService::propertiesChanged(const QString &user, const QString &inte
459 }
448 }
460}
449}
461
450
462−void AccountsService::maybeChanged(const QString &user)
451+void AccountsService::onMaybeChanged(const QString &user)
463{
452{
464 if (m_user != user) {
453 if (m_user != user) {
465 return;
454 return;
plugins/AccountsService/AccountsService.h+4−3
@@ -72,7 +72,8 @@ public:
72 Numeric,
72 Numeric,
73 };
73 };
74
74
75− explicit AccountsService(QObject *parent = 0);
75+ explicit AccountsService(QObject *parent = 0, const QString & user = QString());
76+ ~AccountsService() = default;
76
77
77 QString user() const;
78 QString user() const;
78 void setUser(const QString &user);
79 void setUser(const QString &user);
@@ -103,8 +104,8 @@ Q_SIGNALS:
103 void hereLicensePathChanged();
104 void hereLicensePathChanged();
104
105
105private Q_SLOTS:
106private Q_SLOTS:
106− void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
107+ void onPropertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
107− void maybeChanged(const QString &user);
108+ void onMaybeChanged(const QString &user);
108
109
109private:
110private:
110 void updateDemoEdges(bool async = true);
111 void updateDemoEdges(bool async = true);
plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+13−11
@@ -21,35 +21,35 @@
21#include <QDBusConnectionInterface>
21#include <QDBusConnectionInterface>
22#include <QDBusMessage>
22#include <QDBusMessage>
23#include <QDBusVariant>
23#include <QDBusVariant>
24+#include <QDebug>
24
25
25AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
26AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
26 : QObject(parent),
27 : QObject(parent),
27 m_accountsManager(nullptr),
28 m_accountsManager(nullptr),
28− m_users(),
29 m_ignoreNextChanged(false)
29 m_ignoreNextChanged(false)
30{
30{
31 QDBusConnection connection = QDBusConnection::SM_BUSNAME();
31 QDBusConnection connection = QDBusConnection::SM_BUSNAME();
32 QDBusConnectionInterface *interface = connection.interface();
32 QDBusConnectionInterface *interface = connection.interface();
33 interface->startService("org.freedesktop.Accounts");
33 interface->startService("org.freedesktop.Accounts");
34 m_accountsManager = new QDBusInterface("org.freedesktop.Accounts",
34 m_accountsManager = new QDBusInterface("org.freedesktop.Accounts",
35− "/org/freedesktop/Accounts",
35+ "/org/freedesktop/Accounts",
36− "org.freedesktop.Accounts",
36+ "org.freedesktop.Accounts",
37− connection, this);
37+ connection, this);
38}
38}
39
39
40QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
40QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
41{
41{
42 QDBusInterface *iface = getUserInterface(user);
42 QDBusInterface *iface = getUserInterface(user);
43 if (iface != nullptr && iface->isValid()) {
43 if (iface != nullptr && iface->isValid()) {
44− QDBusMessage answer = iface->call("Get", interface, property);
44+ QDBusReply<QVariant> answer = iface->call("Get", interface, property);
45− if (answer.type() == QDBusMessage::ReplyMessage) {
45+ if (answer.isValid()) {
46− return answer.arguments()[0].value<QDBusVariant>().variant();
46+ return answer;
47 }
47 }
48 }
48 }
49 return QVariant();
49 return QVariant();
50}
50}
51
51
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)
53{
53{
54 QDBusInterface *iface = getUserInterface(user);
54 QDBusInterface *iface = getUserInterface(user);
55 if (iface != nullptr && iface->isValid()) {
55 if (iface != nullptr && iface->isValid()) {
@@ -116,9 +116,9 @@ QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user
116{
116{
117 QDBusInterface *iface = m_users.value(user);
117 QDBusInterface *iface = m_users.value(user);
118 if (iface == nullptr && m_accountsManager->isValid()) {
118 if (iface == nullptr && m_accountsManager->isValid()) {
119− QDBusMessage answer = m_accountsManager->call("FindUserByName", user);
119+ QDBusReply<QDBusObjectPath> answer = m_accountsManager->asyncCall("FindUserByName", user);
120− if (answer.type() == QDBusMessage::ReplyMessage) {
120+ if (answer.isValid()) {
121− QString path = answer.arguments()[0].value<QDBusObjectPath>().path();
121+ const QString path = answer.value().path();
122
122
123 iface = new QDBusInterface("org.freedesktop.Accounts",
123 iface = new QDBusInterface("org.freedesktop.Accounts",
124 path,
124 path,
@@ -148,6 +148,8 @@ QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user
148 SLOT(propertiesChangedSlot(QString, QVariantMap, QStringList)));
148 SLOT(propertiesChangedSlot(QString, QVariantMap, QStringList)));
149
149
150 m_users.insert(user, iface);
150 m_users.insert(user, iface);
151+ } else {
152+ qWarning() << "Couldn't get user interface" << answer.error().name() << answer.error().message();
151 }
153 }
152 }
154 }
153 return iface;
155 return iface;
plugins/AccountsService/AccountsServiceDBusAdaptor.h+3−2
@@ -33,13 +33,14 @@ class AccountsServiceDBusAdaptor: public QObject, public QDBusContext
33
33
34public:
34public:
35 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
35 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
36+ ~AccountsServiceDBusAdaptor() = default;
36
37
37 Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
38 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);
39
40
40 template <typename T>
41 template <typename T>
41 inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
42 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);
43 if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
44 if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
44 return qdbus_cast<T>(variant.value<QDBusArgument>());
45 return qdbus_cast<T>(variant.value<QDBusArgument>());
45 }
46 }
plugins/Lights/Lights.cpp+1−1
@@ -71,7 +71,7 @@ void Lights::setColor(const QColor &color)
71 if (m_color != color) {
71 if (m_color != color) {
72 m_color = color;
72 m_color = color;
73 Q_EMIT colorChanged(m_color);
73 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
75 }
75 }
76}
76}
77
77
plugins/Ubuntu/SystemImage/SystemImage.cpp+7−8
@@ -16,19 +16,18 @@
16
16
17#include "SystemImage.h"
17#include "SystemImage.h"
18#include <QDBusConnection>
18#include <QDBusConnection>
19−#include <QDBusInterface>
19+#include <QDBusPendingCall>
20
20
21SystemImage::SystemImage(QObject *parent)
21SystemImage::SystemImage(QObject *parent)
22− : QObject(parent),
22+ : QObject(parent)
23− m_interface(new QDBusInterface("com.canonical.SystemImage",
24− "/Service",
25− "com.canonical.SystemImage",
26− QDBusConnection::systemBus(),
27− this))
28{
23{
29}
24}
30
25
31void SystemImage::factoryReset()
26void SystemImage::factoryReset()
32{
27{
28+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.SystemImage",
29+ "/Service",
30+ "com.canonical.SystemImage",
33− m_interface->call("FactoryReset");
31+ "FactoryReset");
32+ QDBusConnection::systemBus().asyncCall(msg);
34}
33}
plugins/Ubuntu/SystemImage/SystemImage.h+1−5
@@ -19,8 +19,6 @@
19
19
20#include <QObject>
20#include <QObject>
21
21
22−class QDBusInterface;
23−
24class SystemImage : public QObject
22class SystemImage : public QObject
25{
23{
26 Q_OBJECT
24 Q_OBJECT
@@ -28,11 +26,9 @@ class SystemImage : public QObject
28
26
29public:
27public:
30 explicit SystemImage(QObject *parent = 0);
28 explicit SystemImage(QObject *parent = 0);
29+ ~SystemImage() = default;
31
30
32 Q_INVOKABLE void factoryReset();
31 Q_INVOKABLE void factoryReset();
33−
34−private:
35− QDBusInterface *m_interface;
36};
32};
37
33
38#endif // SYSTEMIMAGE_H
34#endif // SYSTEMIMAGE_H
plugins/Unity/Connectivity/Connectivity.cpp+5−5
@@ -16,7 +16,6 @@
16
16
17#include "Connectivity.h"
17#include "Connectivity.h"
18#include <QDBusConnection>
18#include <QDBusConnection>
19−#include <QDBusInterface>
20#include <QDBusPendingCall>
19#include <QDBusPendingCall>
21
20
22Connectivity::Connectivity(QObject *parent)
21Connectivity::Connectivity(QObject *parent)
@@ -26,8 +25,9 @@ Connectivity::Connectivity(QObject *parent)
26
25
27void Connectivity::unlockAllModems()
26void Connectivity::unlockAllModems()
28{
27{
29− QDBusInterface iface("com.ubuntu.connectivity1",
28+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.ubuntu.connectivity1",
30− "/com/ubuntu/connectivity1/Private",
29+ "/com/ubuntu/connectivity1/Private",
31− "com.ubuntu.connectivity1.Private");
30+ "com.ubuntu.connectivity1.Private",
32− iface.asyncCall("UnlockAllModems");
31+ "UnlockAllModems");
32+ QDBusConnection::sessionBus().asyncCall(msg);
33}
33}
plugins/Unity/Connectivity/Connectivity.h+1
@@ -26,6 +26,7 @@ class Connectivity : public QObject
26
26
27public:
27public:
28 explicit Connectivity(QObject *parent = 0);
28 explicit Connectivity(QObject *parent = 0);
29+ ~Connectivity() = default;
29
30
30 Q_INVOKABLE void unlockAllModems();
31 Q_INVOKABLE void unlockAllModems();
31};
32};
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h+1−1
@@ -36,4 +36,4 @@ public Q_SLOTS:
36
36
37};
37};
38
38
39−#endif // DBUSUNITYSESSIONSERVICE_H
39+#endif // DBUSDASHCOMMUNICATORSERVICE_H
plugins/Unity/DashCommunicator/plugin.cpp+1
@@ -25,6 +25,7 @@
25
25
26void DashCommunicatorPlugin::registerTypes(const char *uri)
26void DashCommunicatorPlugin::registerTypes(const char *uri)
27{
27{
28+ // @uri Unity.DashCommunicator
28 Q_ASSERT(uri == QStringLiteral("Unity.DashCommunicator"));
29 Q_ASSERT(uri == QStringLiteral("Unity.DashCommunicator"));
29 qmlRegisterType<DashCommunicatorService>(uri, 0, 1, "DashCommunicatorService");
30 qmlRegisterType<DashCommunicatorService>(uri, 0, 1, "DashCommunicatorService");
30 qmlRegisterType<DashCommunicator>(uri, 0, 1, "DashCommunicator");
31 qmlRegisterType<DashCommunicator>(uri, 0, 1, "DashCommunicator");
plugins/Unity/Launcher/dbusinterface.cpp−4
@@ -32,10 +32,6 @@ DBusInterface::DBusInterface(LauncherModel *parent):
32{
32{
33}
33}
34
34
35−DBusInterface::~DBusInterface()
36−{
37−}
38−
39QString DBusInterface::introspect(const QString &path) const
35QString DBusInterface::introspect(const QString &path) const
40{
36{
41 /* This case we should just list the nodes */
37 /* This case we should just list the nodes */
plugins/Unity/Launcher/dbusinterface.h+1−1
@@ -27,7 +27,7 @@ class DBusInterface: public UnityDBusVirtualObject
27 Q_OBJECT
27 Q_OBJECT
28public:
28public:
29 DBusInterface(LauncherModel *parent);
29 DBusInterface(LauncherModel *parent);
30− ~DBusInterface();
30+ ~DBusInterface() = default;
31
31
32 // QDBusVirtualObject implementaition
32 // QDBusVirtualObject implementaition
33 QString introspect (const QString &path) const override;
33 QString introspect (const QString &path) const override;
plugins/Unity/Session/dbusunitysessionservice.cpp+47−55
@@ -18,77 +18,69 @@
18#include "dbusunitysessionservice.h"
18#include "dbusunitysessionservice.h"
19
19
20// Qt
20// Qt
21−#include <QDBusConnection>
21+#include <QDBusPendingCall>
22−#include <QDBusInterface>
23
22
24DBusUnitySessionService::DBusUnitySessionService()
23DBusUnitySessionService::DBusUnitySessionService()
25 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
24 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
26{
25{
27}
26}
28
27
29−DBusUnitySessionService::~DBusUnitySessionService()
30−{
31−}
32−
33void DBusUnitySessionService::Logout()
28void DBusUnitySessionService::Logout()
34{
29{
30+ // TODO ask the apps to quit and then emit the signal
35− Q_EMIT logoutReady();
31+ Q_EMIT logoutReady();
36}
32}
37
33
38void DBusUnitySessionService::EndSession()
34void DBusUnitySessionService::EndSession()
39{
35{
40− QDBusConnection connection = QDBusConnection::sessionBus();
36+ const QDBusMessage msg = QDBusMessage::createMethodCall("com.ubuntu.Upstart",
41− QDBusInterface iface1 ("com.ubuntu.Upstart",
42− "/com/ubuntu/Upstart",
37+ "/com/ubuntu/Upstart",
43− "com.ubuntu.Upstart0_6",
38+ "com.ubuntu.Upstart0_6",
44− connection);
45−
46− iface1.call("EndSession");
39+ "EndSession");
40+ QDBusConnection::sessionBus().asyncCall(msg);
47}
41}
48
42
49void DBusUnitySessionService::RequestLogout()
43void DBusUnitySessionService::RequestLogout()
50{
44{
51− Q_EMIT logoutRequested(false);
45+ Q_EMIT logoutRequested(false);
52}
46}
53
47
54void DBusUnitySessionService::Reboot()
48void DBusUnitySessionService::Reboot()
55{
49{
56− QDBusConnection connection = QDBusConnection::systemBus();
50+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
57− QDBusInterface iface1 ("org.freedesktop.login1",
58− "/org/freedesktop/login1",
51+ "/org/freedesktop/login1",
59− "org.freedesktop.login1.Manager",
52+ "org.freedesktop.login1.Manager",
60− connection);
61−
62− iface1.call("Reboot", false);
53+ "Reboot");
54+ msg << false;
55+ QDBusConnection::systemBus().asyncCall(msg);
63}
56}
64
57
65void DBusUnitySessionService::RequestReboot()
58void DBusUnitySessionService::RequestReboot()
66{
59{
67− Q_EMIT rebootRequested(false);
60+ Q_EMIT rebootRequested(false);
68}
61}
69
62
70void DBusUnitySessionService::Shutdown()
63void DBusUnitySessionService::Shutdown()
71{
64{
72− QDBusConnection connection = QDBusConnection::systemBus();
65+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
73− QDBusInterface iface1 ("org.freedesktop.login1",
74− "/org/freedesktop/login1",
66+ "/org/freedesktop/login1",
75− "org.freedesktop.login1.Manager",
67+ "org.freedesktop.login1.Manager",
76− connection);
77−
78− iface1.call("PowerOff", false);
68+ "PowerOff");
69+ msg << false;
70+ QDBusConnection::systemBus().asyncCall(msg);
79}
71}
80
72
81void DBusUnitySessionService::RequestShutdown()
73void DBusUnitySessionService::RequestShutdown()
82{
74{
83− Q_EMIT shutdownRequested(false);
75+ Q_EMIT shutdownRequested(false);
84}
76}
85
77
86enum class Action : unsigned
78enum class Action : unsigned
87{
79{
88− LOGOUT = 0,
80+ LOGOUT = 0,
89− SHUTDOWN,
81+ SHUTDOWN,
90− REBOOT,
82+ REBOOT,
91− NONE
83+ NONE
92};
84};
93
85
94DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
86DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
@@ -96,38 +88,38 @@ DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
96{
88{
97}
89}
98
90
99−DBusGnomeSessionManagerWrapper::~DBusGnomeSessionManagerWrapper()
91+void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
100{
92{
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);
101}
98}
102
99
103void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
100void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
104{
101{
105− Q_UNUSED(arg_1);
102+ Q_UNUSED(arg_1);
106− Q_UNUSED(max_wait);
103+ Q_UNUSED(max_wait);
107− Q_UNUSED(inhibitors);
104+ 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);
114
105
115− Action action = (Action)type;
106+ const Action action = (Action)type;
116
107
117− switch (action)
108+ switch (action)
118− {
109+ {
119 case Action::LOGOUT:
110 case Action::LOGOUT:
120− iface1.call("RequestLogout");
111+ performAsyncCall("RequestLogout");
121− break;
112+ break;
122
113
123 case Action::REBOOT:
114 case Action::REBOOT:
124− iface1.call("RequestShutdown");
115+ performAsyncCall("RequestReboot");
125− break;
116+ break;
126
117
127 case Action::SHUTDOWN:
118 case Action::SHUTDOWN:
119+ performAsyncCall("RequestShutdown");
128− break;
120+ break;
129
121
130 default:
122 default:
131− break;
123+ break;
132− }
124+ }
133}
125}
plugins/Unity/Session/dbusunitysessionservice.h+11−8
@@ -38,7 +38,7 @@ class DBusUnitySessionService : public UnityDBusObject
38
38
39public:
39public:
40 DBusUnitySessionService();
40 DBusUnitySessionService();
41− ~DBusUnitySessionService();
41+ ~DBusUnitySessionService() = default;
42
42
43 // For use in QML. Javascript doesn't accept functions beginning with capital letters
43 // For use in QML. Javascript doesn't accept functions beginning with capital letters
44 Q_INVOKABLE void logout() { Logout(); }
44 Q_INVOKABLE void logout() { Logout(); }
@@ -90,7 +90,7 @@ public Q_SLOTS:
90 /**
90 /**
91 * Logout the system.
91 * Logout the system.
92 *
92 *
93− * This method directly logout the system without user's confirmation.
93+ * This method directly logs out the system without user's confirmation.
94 * Ordinary applications should avoid calling this method. Please call
94 * Ordinary applications should avoid calling this method. Please call
95 * RequestLogout() to ask the user to decide logout or not.
95 * RequestLogout() to ask the user to decide logout or not.
96 */
96 */
@@ -99,7 +99,7 @@ public Q_SLOTS:
99 /**
99 /**
100 * Reboot the system.
100 * Reboot the system.
101 *
101 *
102− * This method directly reboot the system without user's confirmation.
102+ * This method directly reboots the system without user's confirmation.
103 * Ordinary applications should avoid calling this method. Please call
103 * Ordinary applications should avoid calling this method. Please call
104 * RequestReboot() to ask the user to decide reboot or not.
104 * RequestReboot() to ask the user to decide reboot or not.
105 */
105 */
@@ -108,7 +108,7 @@ public Q_SLOTS:
108 /**
108 /**
109 * Shutdown the system.
109 * Shutdown the system.
110 *
110 *
111− * This method directly shutdown the system without user's confirmation.
111+ * This method directly shuts down the system without user's confirmation.
112 * Ordinary applications should avoid calling this method. Please call
112 * Ordinary applications should avoid calling this method. Please call
113 * RequestShutdown() to ask the user to decide shutdown or not.
113 * RequestShutdown() to ask the user to decide shutdown or not.
114 */
114 */
@@ -117,7 +117,7 @@ public Q_SLOTS:
117 /**
117 /**
118 * Issue a logout request.
118 * Issue a logout request.
119 *
119 *
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
121 * which indicates if there's any inhibitors. The shell should receive
121 * which indicates if there's any inhibitors. The shell should receive
122 * this signal and display a dialog to ask the user to confirm the logout
122 * this signal and display a dialog to ask the user to confirm the logout
123 * action. If confirmed, shell can call Logout() method to logout.
123 * action. If confirmed, shell can call Logout() method to logout.
@@ -127,7 +127,7 @@ public Q_SLOTS:
127 /**
127 /**
128 * Issue a reboot request.
128 * Issue a reboot request.
129 *
129 *
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
131 * which indicates if there's any inhibitors. The shell should receive
131 * which indicates if there's any inhibitors. The shell should receive
132 * this signal and display a dialog to ask the user to confirm the reboot
132 * this signal and display a dialog to ask the user to confirm the reboot
133 * action. If confirmed, shell can call Reboot() method to reboot
133 * action. If confirmed, shell can call Reboot() method to reboot
@@ -137,7 +137,7 @@ public Q_SLOTS:
137 /**
137 /**
138 * Issue a shutdown request.
138 * Issue a shutdown request.
139 *
139 *
140− * This method emit the shutdownRequested signal to the shell with a
140+ * This method emits the shutdownRequested signal to the shell with a
141 * boolean which indicates if there's any inhibitors.
141 * boolean which indicates if there's any inhibitors.
142 * The shell should receive
142 * The shell should receive
143 * this signal and display a dialog to ask the user to confirm the reboot
143 * this signal and display a dialog to ask the user to confirm the reboot
@@ -161,9 +161,12 @@ class DBusGnomeSessionManagerWrapper : public UnityDBusObject
161
161
162public:
162public:
163 DBusGnomeSessionManagerWrapper();
163 DBusGnomeSessionManagerWrapper();
164− ~DBusGnomeSessionManagerWrapper();
164+ ~DBusGnomeSessionManagerWrapper() = default;
165
165
166public Q_SLOTS:
166public Q_SLOTS:
167 Q_SCRIPTABLE void Open(const unsigned int type, const unsigned int arg_1, const unsigned int max_wait, const QList<QDBusObjectPath> &inhibitors);
167 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);
168};
171};
169#endif // DBUSUNITYSESSIONSERVICE_H
172#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Wizard/System.cpp+13−9
@@ -16,7 +16,9 @@
16
16
17#include "System.h"
17#include "System.h"
18
18
19−#include <QDBusInterface>
19+#include <QDBusPendingCall>
20+#include <QDBusMessage>
21+#include <QDBusConnection>
20#include <QDBusMetaType>
22#include <QDBusMetaType>
21#include <QDir>
23#include <QDir>
22#include <QFile>
24#include <QFile>
@@ -65,21 +67,23 @@ void System::setWizardEnabled(bool enabled)
65void System::setSessionVariable(const QString &variable, const QString &value)
67void System::setSessionVariable(const QString &variable, const QString &value)
66{
68{
67 // We need to update both upstart's and DBus's environment
69 // We need to update both upstart's and DBus's environment
68− QProcess::execute(QString("initctl set-env --global %1=%2").arg(variable, value));
70+ QProcess::startDetached(QStringLiteral("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());
74
71
75 QMap<QString,QString> valueMap;
72 QMap<QString,QString> valueMap;
76 valueMap.insert(variable, value);
73 valueMap.insert(variable, value);
74+
75+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DBus",
76+ "/org/freedesktop/DBus",
77+ "org.freedesktop.DBus",
77− iface.call("UpdateActivationEnvironment", QVariant::fromValue(valueMap));
78+ "UpdateActivationEnvironment");
79+
80+ msg << QVariant::fromValue(valueMap);
81+ QDBusConnection::sessionBus().asyncCall(msg);
78}
82}
79
83
80void System::updateSessionLanguage(const QString &locale)
84void System::updateSessionLanguage(const QString &locale)
81{
85{
82− QString language = locale.split(".")[0];
86+ const QString language = locale.split(".")[0];
83
87
84 setSessionVariable("LANGUAGE", language);
88 setSessionVariable("LANGUAGE", language);
85 setSessionVariable("LANG", locale);
89 setSessionVariable("LANG", locale);
po/ar.po+1−1
@@ -16,7 +16,7 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
17"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
18"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
18"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2015-07-18 05:34+0000\n"
19+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
20"X-Generator: Launchpad (build 17628)\n"
20"X-Generator: Launchpad (build 17628)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
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/sw.po+1−1
@@ -15,7 +15,7 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-18 05:34+0000\n"
18+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
19"X-Generator: Launchpad (build 17628)\n"
19"X-Generator: Launchpad (build 17628)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
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 {
50 switch (widgetType) {
50 switch (widgetType) {
51 case "actions": return "PreviewActions.qml";
51 case "actions": return "PreviewActions.qml";
52 case "audio": return "PreviewAudioPlayback.qml";
52 case "audio": return "PreviewAudioPlayback.qml";
53+ case "comment": return "PreviewComment.qml";
54+ case "comment-input": return "PreviewCommentInput.qml";
53 case "expandable": return "PreviewExpandable.qml";
55 case "expandable": return "PreviewExpandable.qml";
54 case "gallery": return "PreviewImageGallery.qml";
56 case "gallery": return "PreviewImageGallery.qml";
55 case "header": return "PreviewHeader.qml";
57 case "header": return "PreviewHeader.qml";
qml/Launcher/LauncherPanel.qml+4−3
@@ -521,7 +521,7 @@ Rectangle {
521 Image {
521 Image {
522 anchors {
522 anchors {
523 left: parent.left
523 left: parent.left
524− leftMargin: (quickList.item.width - units.gu(1)) / 2 - width / 2
524+ leftMargin: quickList.item ? (quickList.item.width - units.gu(1)) / 2 - width / 2 : 0
525 verticalCenter: parent.verticalCenter
525 verticalCenter: parent.verticalCenter
526 verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
526 verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
527 }
527 }
@@ -564,9 +564,10 @@ Rectangle {
564
564
565 // internal
565 // internal
566 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
566 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
567− property int offset: itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
567+ property int offset: item ? itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
568 -(item.height/2) - height - units.gu(.5) :
568 -(item.height/2) - height - units.gu(.5) :
569− (item.height/2) + units.gu(.5)
569+ (item.height/2) + units.gu(.5) :
570+ 0
570
571
571 Column {
572 Column {
572 id: quickListColumn
573 id: quickListColumn
qml/Notifications/Notifications.qml+1−1
@@ -33,7 +33,7 @@ ListView {
33 id: snapDecisionProxyModel
33 id: snapDecisionProxyModel
34 objectName: "snapDecisionProxyModel"
34 objectName: "snapDecisionProxyModel"
35
35
36− model: notificationList.model
36+ model: notificationList.model ? notificationList.model : null
37 filterRole: UnityNotifications.ModelInterface != undefined ? UnityNotifications.ModelInterface.RoleType : 0
37 filterRole: UnityNotifications.ModelInterface != undefined ? UnityNotifications.ModelInterface.RoleType : 0
38 filterRegExp: RegExp(UnityNotifications.Notification.SnapDecision)
38 filterRegExp: RegExp(UnityNotifications.Notification.SnapDecision)
39 }
39 }
qml/Panel/IndicatorItem.qml+1−1
@@ -234,7 +234,7 @@ IndicatorDelegate {
234 ]
234 ]
235 }
235 }
236
236
237− onRootActionStateChanged: {
237+ rootActionState.onUpdated: {
238 if (rootActionState == undefined) {
238 if (rootActionState == undefined) {
239 title = "";
239 title = "";
240 leftLabel = "";
240 leftLabel = "";
qml/Panel/Indicators/IndicatorBase.qml−1
@@ -43,6 +43,5 @@ Item {
43 ModelActionRootState {
43 ModelActionRootState {
44 id: rootAction
44 id: rootAction
45 menu: menuModel ? menuModel : null
45 menu: menuModel ? menuModel : null
46− onUpdated: indicatorItem.rootActionStateChanged()
47 }
46 }
48}
47}
qml/Tutorial/TutorialLeft.qml+16−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -46,24 +46,34 @@ TutorialPage {
46
46
47 SequentialAnimation {
47 SequentialAnimation {
48 id: teaseAnimation
48 id: teaseAnimation
49+ objectName: "teaseAnimation"
49 paused: running && root.paused
50 paused: running && root.paused
50 running: !slider.active && root.launcher.visibleWidth === 0 && root.shown
51 running: !slider.active && root.launcher.visibleWidth === 0 && root.shown
51 loops: Animation.Infinite
52 loops: Animation.Infinite
53+ property real bounce: 0
54+ readonly property real maxBounce: units.gu(2)
52
55
53 UbuntuNumberAnimation {
56 UbuntuNumberAnimation {
54− target: root.launcher
57+ target: teaseAnimation
55− property: "x"
58+ property: "bounce"
56− to: units.gu(2)
59+ to: teaseAnimation.maxBounce
57 duration: UbuntuAnimation.SleepyDuration
60 duration: UbuntuAnimation.SleepyDuration
58 }
61 }
59 UbuntuNumberAnimation {
62 UbuntuNumberAnimation {
60− target: root.launcher
63+ target: teaseAnimation
61− property: "x"
64+ property: "bounce"
62 to: 0
65 to: 0
63 duration: UbuntuAnimation.SleepyDuration
66 duration: UbuntuAnimation.SleepyDuration
64 }
67 }
65 }
68 }
66
69
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+
67 Timer {
77 Timer {
68 id: finishTimer
78 id: finishTimer
69 interval: 1
79 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 {
41 signal activated
41 signal activated
42
42
43 property string actionName: \"" + actionName + "\"
43 property string actionName: \"" + actionName + "\"
44− property bool valid: ActionData.data[actionName] != undefined ? ActionData.data[actionName].valid : false
44+ property bool valid: ActionData.data.hasOwnProperty(actionName) ? ActionData.data[actionName].valid : false
45− property var state: ActionData.data[actionName] != undefined ? ActionData.data[actionName].state : undefined
45+ property var state: ActionData.data.hasOwnProperty(actionName) ? ActionData.data[actionName].state : undefined
46
46
47 function activate() {
47 function activate() {
48 activated();
48 activated();
tests/mocks/QMenuModel/actiondata.h+3−1
@@ -31,7 +31,9 @@ class Q_DECL_EXPORT ActionData : public QObject
31public:
31public:
32 ActionData(QObject* parent = 0)
32 ActionData(QObject* parent = 0)
33 : QObject(parent)
33 : QObject(parent)
34+ , m_data(QVariantMap())
34− {}
35+ {
36+ }
35
37
36 QVariant data() const { return m_data; }
38 QVariant data() const { return m_data; }
37 void setData(const QVariant& data)
39 void setData(const QVariant& data)
tests/plugins/AccountsService/client.cpp+10−20
@@ -94,8 +94,7 @@ private Q_SLOTS:
94
94
95 void testGetSetService()
95 void testGetSetService()
96 {
96 {
97− AccountsService session;
97+ AccountsService session(this, QTest::currentTestFunction());
98− session.setUser(QTest::currentTestFunction());
99
98
100 QCOMPARE(session.demoEdges(), false);
99 QCOMPARE(session.demoEdges(), false);
101 session.setDemoEdges(true);
100 session.setDemoEdges(true);
@@ -112,8 +111,7 @@ private Q_SLOTS:
112
111
113 void testAsynchornousChangeForDemoEdges()
112 void testAsynchornousChangeForDemoEdges()
114 {
113 {
115− AccountsService session;
114+ AccountsService session(this, QTest::currentTestFunction());
116− session.setUser(QTest::currentTestFunction());
117
115
118 QCOMPARE(session.demoEdges(), false);
116 QCOMPARE(session.demoEdges(), false);
119 ASSERT_DBUS_CALL(m_userInterface->call("Set",
117 ASSERT_DBUS_CALL(m_userInterface->call("Set",
@@ -125,8 +123,7 @@ private Q_SLOTS:
125
123
126 void testAsynchornousChangeForFailedLogins()
124 void testAsynchornousChangeForFailedLogins()
127 {
125 {
128− AccountsService session;
126+ AccountsService session(this, QTest::currentTestFunction());
129− session.setUser(QTest::currentTestFunction());
130
127
131 QCOMPARE(session.failedLogins(), (uint)0);
128 QCOMPARE(session.failedLogins(), (uint)0);
132 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
129 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -138,8 +135,7 @@ private Q_SLOTS:
138
135
139 void testAsynchornousChangeForStatsWelcomeScreen()
136 void testAsynchornousChangeForStatsWelcomeScreen()
140 {
137 {
141− AccountsService session;
138+ AccountsService session(this, QTest::currentTestFunction());
142− session.setUser(QTest::currentTestFunction());
143
139
144 QCOMPARE(session.statsWelcomeScreen(), true);
140 QCOMPARE(session.statsWelcomeScreen(), true);
145 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
141 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -151,8 +147,7 @@ private Q_SLOTS:
151
147
152 void testAsynchornousChangeForStatsEnableLauncherWhileLocked()
148 void testAsynchornousChangeForStatsEnableLauncherWhileLocked()
153 {
149 {
154− AccountsService session;
150+ AccountsService session(this, QTest::currentTestFunction());
155− session.setUser(QTest::currentTestFunction());
156
151
157 QCOMPARE(session.enableLauncherWhileLocked(), true);
152 QCOMPARE(session.enableLauncherWhileLocked(), true);
158 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
153 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -164,8 +159,7 @@ private Q_SLOTS:
164
159
165 void testAsynchornousChangeForStatsEnableIndicatorsWhileLocked()
160 void testAsynchornousChangeForStatsEnableIndicatorsWhileLocked()
166 {
161 {
167− AccountsService session;
162+ AccountsService session(this, QTest::currentTestFunction());
168− session.setUser(QTest::currentTestFunction());
169
163
170 QCOMPARE(session.enableIndicatorsWhileLocked(), true);
164 QCOMPARE(session.enableIndicatorsWhileLocked(), true);
171 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
165 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -177,8 +171,7 @@ private Q_SLOTS:
177
171
178 void testAsynchornousChangeForStatsPasswordDisplayHint()
172 void testAsynchornousChangeForStatsPasswordDisplayHint()
179 {
173 {
180− AccountsService session;
174+ AccountsService session(this, QTest::currentTestFunction());
181− session.setUser(QTest::currentTestFunction());
182
175
183 QCOMPARE(session.passwordDisplayHint(), AccountsService::Keyboard);
176 QCOMPARE(session.passwordDisplayHint(), AccountsService::Keyboard);
184 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
177 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -190,8 +183,7 @@ private Q_SLOTS:
190
183
191 void testAsynchornousChangeForStatsLicenseAccepted()
184 void testAsynchornousChangeForStatsLicenseAccepted()
192 {
185 {
193− AccountsService session;
186+ AccountsService session(this, QTest::currentTestFunction());
194− session.setUser(QTest::currentTestFunction());
195
187
196 QCOMPARE(session.hereEnabled(), false);
188 QCOMPARE(session.hereEnabled(), false);
197 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
189 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -203,8 +195,7 @@ private Q_SLOTS:
203
195
204 void testAsynchornousChangeForLicenseBasePath()
196 void testAsynchornousChangeForLicenseBasePath()
205 {
197 {
206− AccountsService session;
198+ AccountsService session(this, QTest::currentTestFunction());
207− session.setUser(QTest::currentTestFunction());
208
199
209 QCOMPARE(session.hereLicensePath(), QString());
200 QCOMPARE(session.hereLicensePath(), QString());
210 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
201 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
@@ -216,8 +207,7 @@ private Q_SLOTS:
216
207
217 void testAsynchornousChangeForStatsBackgroundFile()
208 void testAsynchornousChangeForStatsBackgroundFile()
218 {
209 {
219− AccountsService session;
210+ AccountsService session(this, QTest::currentTestFunction());
220− session.setUser(QTest::currentTestFunction());
221
211
222 QCOMPARE(session.backgroundFile(), QString());
212 QCOMPARE(session.backgroundFile(), QString());
223 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
213 ASSERT_DBUS_CALL(m_userInterface->asyncCall("Set",
tests/plugins/IntegratedLightDM/CMakeLists.txt+6
@@ -9,6 +9,9 @@ add_executable(GreeterDBusTestExec
9 dbus.cpp
9 dbus.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/IntegratedLightDM/Greeter.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/IntegratedLightDM/Greeter.cpp
11 )
11 )
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)
12qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test)
15qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test)
13target_link_libraries(GreeterDBusTestExec
16target_link_libraries(GreeterDBusTestExec
14 -L${CMAKE_BINARY_DIR}/tests/mocks/IntegratedLightDM/liblightdm
17 -L${CMAKE_BINARY_DIR}/tests/mocks/IntegratedLightDM/liblightdm
@@ -44,6 +47,9 @@ add_executable(GreeterUsersModelTestExec
44 ${CMAKE_SOURCE_DIR}/plugins/IntegratedLightDM/UsersModel.cpp
47 ${CMAKE_SOURCE_DIR}/plugins/IntegratedLightDM/UsersModel.cpp
45 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
48 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
46 )
49 )
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)
47qt5_use_modules(GreeterUsersModelTestExec Core Test)
53qt5_use_modules(GreeterUsersModelTestExec Core Test)
48target_link_libraries(GreeterUsersModelTestExec
54target_link_libraries(GreeterUsersModelTestExec
49 -L${CMAKE_BINARY_DIR}/tests/mocks/IntegratedLightDM/liblightdm
55 -L${CMAKE_BINARY_DIR}/tests/mocks/IntegratedLightDM/liblightdm
tests/plugins/Unity/Session/sessionbackendtest.cpp+27−40
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -15,6 +15,7 @@
15 *
15 *
16 * Authors:
16 * Authors:
17 * Ying-Chun Liu (PaulLiu) <paul.liu@canonical.com>
17 * Ying-Chun Liu (PaulLiu) <paul.liu@canonical.com>
18+ * Lukáš Tinkl <lukas.tinkl@canonical.com>
18 */
19 */
19
20
20#include <QtTest>
21#include <QtTest>
@@ -49,17 +50,22 @@ private Q_SLOTS:
49
50
50 void testUnitySessionLogoutRequested_data() {
51 void testUnitySessionLogoutRequested_data() {
51 QTest::addColumn<QString>("method");
52 QTest::addColumn<QString>("method");
53+ QTest::addColumn<QString>("signal");
52
54
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)";
54 }
58 }
55
59
56 void testUnitySessionLogoutRequested() {
60 void testUnitySessionLogoutRequested() {
57 QFETCH(QString, method);
61 QFETCH(QString, method);
62+ QFETCH(QString, signal);
58
63
59 DBusUnitySessionService dbusUnitySessionService;
64 DBusUnitySessionService dbusUnitySessionService;
60 QCoreApplication::processEvents(); // to let the service register on DBus
65 QCoreApplication::processEvents(); // to let the service register on DBus
61
66
67+ // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
62− QSignalSpy spy(&dbusUnitySessionService, SIGNAL(logoutRequested(bool)));
68+ QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
63
69
64 QDBusReply<void> reply = dbusUnitySession->call(method);
70 QDBusReply<void> reply = dbusUnitySession->call(method);
65 QCOMPARE(reply.isValid(), true);
71 QCOMPARE(reply.isValid(), true);
@@ -67,51 +73,34 @@ private Q_SLOTS:
67 QCOMPARE(spy.count(), 1);
73 QCOMPARE(spy.count(), 1);
68 }
74 }
69
75
70− void testGnomeSessionWrapperLogoutRequested() {
76+ void testGnomeSessionWrapper_data() {
71− DBusUnitySessionService dbusUnitySessionService;
77+ QTest::addColumn<uint>("method");
72− QCoreApplication::processEvents(); // to let the service register on DBus
78+ QTest::addColumn<QString>("signal");
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)));
77
79
78− DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
80+ QTest::newRow("Logout") << (uint)Action::LOGOUT << "logoutRequested(bool)";
79− QCoreApplication::processEvents(); // to let the service register on DBus
81+ QTest::newRow("Shutdown") << (unsigned)Action::SHUTDOWN << "shutdownRequested(bool)";
80−
82+ QTest::newRow("Reboot") << (unsigned)Action::REBOOT << "rebootRequested(bool)";
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);
98 }
83 }
99
84
100− void testGnomeSessionWrapperShutdownRequested() {
85+ void testGnomeSessionWrapper() {
86+ QFETCH(uint, method);
87+ QFETCH(QString, signal);
88+
101 DBusUnitySessionService dbusUnitySessionService;
89 DBusUnitySessionService dbusUnitySessionService;
102 QCoreApplication::processEvents(); // to let the service register on DBus
90 QCoreApplication::processEvents(); // to let the service register on DBus
103
91
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
105 // as proof we are actually calling the actual method.
93 // as proof we are actually calling the actual method.
94+ // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
106− QSignalSpy spy(&dbusUnitySessionService, SIGNAL(shutdownRequested(bool)));
95+ QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
107
96
108 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
97 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
109 QCoreApplication::processEvents(); // to let the service register on DBus
98 QCoreApplication::processEvents(); // to let the service register on DBus
110
99
111− QDBusInterface dbusGnomeSessionWrapper ("com.canonical.Unity",
100+ QDBusInterface dbusGnomeSessionWrapper("com.canonical.Unity",
112− "/org/gnome/SessionManager/EndSessionDialog",
101+ "/org/gnome/SessionManager/EndSessionDialog",
113− "org.gnome.SessionManager.EndSessionDialog",
102+ "org.gnome.SessionManager.EndSessionDialog",
114− QDBusConnection::sessionBus());
103+ QDBusConnection::sessionBus());
115
104
116 QCOMPARE(dbusGnomeSessionWrapper.isValid(), true);
105 QCOMPARE(dbusGnomeSessionWrapper.isValid(), true);
117
106
@@ -120,9 +109,7 @@ private Q_SLOTS:
120 QVariant inhibitors;
109 QVariant inhibitors;
121 inhibitors.setValue(var);
110 inhibitors.setValue(var);
122
111
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);
126 QCOMPARE(reply.isValid(), true);
113 QCOMPARE(reply.isValid(), true);
127
114
128 // Make sure we see the signal being emitted.
115 // Make sure we see the signal being emitted.
tests/qmltests/CMakeLists.txt+2
@@ -27,6 +27,8 @@ add_unity8_qmltest(Dash PreviewListView)
27add_unity8_qmltest(Dash/Previews Preview)
27add_unity8_qmltest(Dash/Previews Preview)
28add_unity8_qmltest(Dash/Previews PreviewActions)
28add_unity8_qmltest(Dash/Previews PreviewActions)
29add_unity8_qmltest(Dash/Previews PreviewAudioPlayback)
29add_unity8_qmltest(Dash/Previews PreviewAudioPlayback)
30+add_unity8_qmltest(Dash/Previews PreviewComment)
31+add_unity8_qmltest(Dash/Previews PreviewCommentInput)
30add_unity8_qmltest(Dash/Previews PreviewExpandable)
32add_unity8_qmltest(Dash/Previews PreviewExpandable)
31add_unity8_qmltest(Dash/Previews PreviewHeader)
33add_unity8_qmltest(Dash/Previews PreviewHeader)
32add_unity8_qmltest(Dash/Previews PreviewImageGallery)
34add_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 {
77 return [
77 return [
78 { tag: "Actions", type: "actions", source: "PreviewActions.qml" },
78 { tag: "Actions", type: "actions", source: "PreviewActions.qml" },
79 { tag: "Audio", type: "audio", source: "PreviewAudioPlayback.qml" },
79 { 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" },
80 { tag: "Expandable", type: "expandable", source: "PreviewExpandable.qml" },
82 { tag: "Expandable", type: "expandable", source: "PreviewExpandable.qml" },
81 { tag: "Gallery", type: "gallery", source: "PreviewImageGallery.qml" },
83 { tag: "Gallery", type: "gallery", source: "PreviewImageGallery.qml" },
82 { tag: "Header", type: "header", source: "PreviewHeader.qml" },
84 { tag: "Header", type: "header", source: "PreviewHeader.qml" },
tests/qmltests/Tutorial/tst_Tutorial.qml+31−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013,2014 Canonical, Ltd.
2+ * Copyright (C) 2013,2014,2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -320,6 +320,36 @@ Item {
320 tryCompare(left, "shown", true); // and we should still be on left
320 tryCompare(left, "shown", true); // and we should still be on left
321 }
321 }
322
322
323+ function test_launcherNoDragGap() {
324+ // See bug 1454882, where if you dragged the launcher while it was
325+ // visible, you could pull it further than the edge of the screen.
326+
327+ var left = goToPage("tutorialLeft");
328+ var launcher = findChild(shell, "launcher");
329+ var teaseAnimation = findInvisibleChild(left, "teaseAnimation");
330+
331+ // Wait for launcher to be really out there
332+ tryCompareFunction(function() {return launcher.x > teaseAnimation.maxBounce/2}, true);
333+ verify(teaseAnimation.running);
334+
335+ // Start a drag, make sure animation stops
336+ touchFlick(shell, 0, halfHeight, units.gu(4), halfHeight, true, false);
337+ verify(!teaseAnimation.running);
338+ verify(launcher.visibleWidth > 0);
339+ verify(launcher.x > 0);
340+ compare(launcher.x, teaseAnimation.bounce);
341+
342+ // Continue drag, make sure we don't create a gap on the left hand side
343+ touchFlick(shell, units.gu(4), halfHeight, shell.width, halfHeight, false, false);
344+ verify(!teaseAnimation.running);
345+ compare(launcher.visibleWidth, launcher.panelWidth);
346+ compare(launcher.x, 0);
347+
348+ // Finish and make sure we continue animation
349+ touchFlick(shell, shell.width, halfHeight, shell.width, halfHeight, false, true);
350+ tryCompare(teaseAnimation, "running", true);
351+ }
352+
323 function test_spread() {
353 function test_spread() {
324 // Unfortunately, most of what we want to test of the spread is
354 // Unfortunately, most of what we want to test of the spread is
325 // "did it render correctly?" but that's hard to test. So instead,
355 // "did it render correctly?" but that's hard to test. So instead,
tests/qmltests/tst_ShellWithPin.qml+1−1
@@ -313,7 +313,7 @@ Item {
313 tryCompare(panel, "fullscreenMode", false)
313 tryCompare(panel, "fullscreenMode", false)
314 tryCompare(indicators, "available", true)
314 tryCompare(indicators, "available", true)
315 tryCompare(launcher, "available", true)
315 tryCompare(launcher, "available", true)
316− tryCompare(stage, "spreadEnabled", true)
316+ tryCompare(stage, "spreadEnabled", false)
317 }
317 }
318
318
319 function test_emergencyCallCrash() {
319 function test_emergencyCallCrash() {
tools/CMakeLists.txt+1
@@ -1,6 +1,7 @@
1add_executable(${SCOPE_TOOL}
1add_executable(${SCOPE_TOOL}
2 scopetool.cpp
2 scopetool.cpp
3 registry-tracker.cpp
3 registry-tracker.cpp
4+ unix-signal-handler.cpp
4 )
5 )
5
6
6qt5_use_modules(${SCOPE_TOOL} Qml Quick)
7qt5_use_modules(${SCOPE_TOOL} Qml Quick)
tools/scopetool.cpp+6
@@ -32,6 +32,7 @@
32// local
32// local
33#include <paths.h>
33#include <paths.h>
34#include "registry-tracker.h"
34#include "registry-tracker.h"
35+#include "unix-signal-handler.h"
35
36
36
37
37int main(int argc, char *argv[])
38int main(int argc, char *argv[])
@@ -96,6 +97,11 @@ int main(int argc, char *argv[])
96
97
97 view->show();
98 view->show();
98
99
100+ UnixSignalHandler signal_handler([]{
101+ QGuiApplication::exit(0);
102+ });
103+ signal_handler.setupUnixSignalHandlers();
104+
99 int result = application->exec();
105 int result = application->exec();
100
106
101 delete view;
107 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+};