on xenial help sign in
rebash on top of lp:~lukas-kde/unity8/closeAppsFromQuicklist
6ae5fdahandsome_feng
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+5−3
@@ -30,7 +30,7 @@ Build-Depends: cmake,
30 libqt5xmlpatterns5-dev,
30 libqt5xmlpatterns5-dev,
31 libsystemsettings-dev,
31 libsystemsettings-dev,
32 libudev-dev,
32 libudev-dev,
33− libunity-api-dev (>= 7.97),
33+ libunity-api-dev (>= 7.98),
34 libusermetricsoutput1-dev,
34 libusermetricsoutput1-dev,
35 libxcb1-dev,
35 libxcb1-dev,
36 pkg-config,
36 pkg-config,
@@ -101,7 +101,7 @@ Depends: gsettings-desktop-schemas,
101 qtdeclarative5-ubuntu-telephony0.1,
101 qtdeclarative5-ubuntu-telephony0.1,
102 qtdeclarative5-ubuntu-web-plugin,
102 qtdeclarative5-ubuntu-web-plugin,
103 ubuntu-system-settings,
103 ubuntu-system-settings,
104− unity-launcher-impl-4,
104+ unity-launcher-impl-7,
105 unity8-common (= ${source:Version}),
105 unity8-common (= ${source:Version}),
106 unity8-private (= ${binary:Version}),
106 unity8-private (= ${binary:Version}),
107 unity8-private | unity-launcher-impl,
107 unity8-private | unity-launcher-impl,
@@ -148,6 +148,7 @@ Depends: autopilot-qt5 (>= 1.4),
148 gir1.2-notify-0.7,
148 gir1.2-notify-0.7,
149 libqt5test5,
149 libqt5test5,
150 libqt5widgets5,
150 libqt5widgets5,
151+ ofono-phonesim,
151 python3-autopilot,
152 python3-autopilot,
152 python3-evdev,
153 python3-evdev,
153 python3-fixtures,
154 python3-fixtures,
@@ -158,6 +159,7 @@ Depends: autopilot-qt5 (>= 1.4),
158 unity8 (= ${source:Version}),
159 unity8 (= ${source:Version}),
159 unity8-fake-env (= ${source:Version}),
160 unity8-fake-env (= ${source:Version}),
160 url-dispatcher-tools,
161 url-dispatcher-tools,
162+ xvfb,
161 ${misc:Depends},
163 ${misc:Depends},
162 ${python3:Depends},
164 ${python3:Depends},
163 ${shlibs:Depends},
165 ${shlibs:Depends},
@@ -189,7 +191,7 @@ Depends: accountsservice-ubuntu-schemas (>= 0.0.3),
189 ${misc:Depends},
191 ${misc:Depends},
190 ${shlibs:Depends},
192 ${shlibs:Depends},
191Provides: unity-launcher-impl,
193Provides: unity-launcher-impl,
192− unity-launcher-impl-4,
194+ unity-launcher-impl-7,
193Description: Unity 8 private libs
195Description: Unity 8 private libs
194 The Unity 8 shell is the primary user interface for Ubuntu devices.
196 The Unity 8 shell is the primary user interface for Ubuntu devices.
195 .
197 .
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/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
2pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
2pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4
4
plugins/Greeter/Unity/Launcher/launcheritem.cpp+14
@@ -31,6 +31,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
31 m_count(0),
31 m_count(0),
32 m_countVisible(false),
32 m_countVisible(false),
33 m_focused(false),
33 m_focused(false),
34+ m_alerting(false),
34 m_quickList(new QuickListModel(this))
35 m_quickList(new QuickListModel(this))
35{
36{
36 QuickListEntry nameAction;
37 QuickListEntry nameAction;
@@ -165,6 +166,19 @@ void LauncherItem::setFocused(bool focused)
165 }
166 }
166}
167}
167
168
169+bool LauncherItem::alerting() const
170+{
171+ return m_alerting;
172+}
173+
174+void LauncherItem::setAlerting(bool alerting)
175+{
176+ if (m_alerting != alerting) {
177+ m_alerting = alerting;
178+ Q_EMIT alertingChanged(alerting);
179+ }
180+}
181+
168unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
182unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
169{
183{
170 return m_quickList;
184 return m_quickList;
plugins/Greeter/Unity/Launcher/launcheritem.h+4
@@ -41,6 +41,7 @@ public:
41 int count() const override;
41 int count() const override;
42 bool countVisible() const override;
42 bool countVisible() const override;
43 bool focused() const override;
43 bool focused() const override;
44+ bool alerting() const override;
44
45
45 unity::shell::launcher::QuickListModelInterface *quickList() const override;
46 unity::shell::launcher::QuickListModelInterface *quickList() const override;
46
47
@@ -54,6 +55,8 @@ private:
54 void setCount(int count);
55 void setCount(int count);
55 void setCountVisible(bool countVisible);
56 void setCountVisible(bool countVisible);
56 void setFocused(bool focused);
57 void setFocused(bool focused);
58+ void setAlerting(bool alerting);
59+
57
60
58private:
61private:
59 QString m_appId;
62 QString m_appId;
@@ -66,6 +69,7 @@ private:
66 int m_count;
69 int m_count;
67 bool m_countVisible;
70 bool m_countVisible;
68 bool m_focused;
71 bool m_focused;
72+ bool m_alerting;
69 QuickListModel *m_quickList;
73 QuickListModel *m_quickList;
70
74
71 friend class LauncherModel;
75 friend class LauncherModel;
plugins/Greeter/Unity/Launcher/launchermodelas.cpp+28−12
@@ -67,11 +67,20 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
67 return item->progress();
67 return item->progress();
68 case RoleFocused:
68 case RoleFocused:
69 return item->focused();
69 return item->focused();
70+ case RoleRunning:
71+ return item->running();
70 }
72 }
71
73
72 return QVariant();
74 return QVariant();
73}
75}
74
76
77+void LauncherModel::setAlerting(const QString &appId, bool alerting)
78+{
79+ Q_UNUSED(appId)
80+ Q_UNUSED(alerting)
81+ qWarning() << "This is a read only implementation. Cannot set alert-state of items.";
82+}
83+
75unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
84unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
76{
85{
77 if (index < 0 || index >= m_list.count()) {
86 if (index < 0 || index >= m_list.count()) {
@@ -200,16 +209,20 @@ void LauncherModel::refresh()
200 Q_FOREACH (LauncherItem* item, m_list) {
209 Q_FOREACH (LauncherItem* item, m_list) {
201 bool found = false;
210 bool found = false;
202 Q_FOREACH(const QVariant &asItem, items) {
211 Q_FOREACH(const QVariant &asItem, items) {
212+ QVariantMap cachedMap = asItem.toMap();
203− if (asItem.toMap().value("id").toString() == item->appId()) {
213+ if (cachedMap.value("id").toString() == item->appId()) {
204 // Only keep and update it if we either show unpinned or it is pinned
214 // Only keep and update it if we either show unpinned or it is pinned
205− if (!m_onlyPinned || asItem.toMap().value("pinned").toBool()) {
215+ if (!m_onlyPinned || cachedMap.value("pinned").toBool()) {
206 found = true;
216 found = true;
207− item->setName(asItem.toMap().value("name").toString());
217+ item->setName(cachedMap.value("name").toString());
208− item->setIcon(asItem.toMap().value("icon").toString());
218+ item->setIcon(cachedMap.value("icon").toString());
209− item->setCount(asItem.toMap().value("count").toInt());
219+ item->setCount(cachedMap.value("count").toInt());
210− item->setCountVisible(asItem.toMap().value("countVisible").toBool());
220+ item->setCountVisible(cachedMap.value("countVisible").toBool());
221+ item->setProgress(cachedMap.value("progress").toInt());
222+ item->setRunning(cachedMap.value("running").toBool());
223+
211 int idx = m_list.indexOf(item);
224 int idx = m_list.indexOf(item);
212− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
225+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleCount, RoleCountVisible, RoleRunning, RoleProgress});
213 }
226 }
214 break;
227 break;
215 }
228 }
@@ -247,14 +260,17 @@ void LauncherModel::refresh()
247 }
260 }
248
261
249 if (itemIndex == -1) {
262 if (itemIndex == -1) {
263+ QVariantMap cachedMap = entry.toMap();
250 // Need to add it. Just add it into the addedIndex to keep same ordering as the list in AS.
264 // Need to add it. Just add it into the addedIndex to keep same ordering as the list in AS.
251− LauncherItem *item = new LauncherItem(entry.toMap().value("id").toString(),
265+ LauncherItem *item = new LauncherItem(cachedMap.value("id").toString(),
252− entry.toMap().value("name").toString(),
266+ cachedMap.value("name").toString(),
253− entry.toMap().value("icon").toString(),
267+ cachedMap.value("icon").toString(),
254 this);
268 this);
255 item->setPinned(true);
269 item->setPinned(true);
256− item->setCount(entry.toMap().value("count").toInt());
270+ item->setCount(cachedMap.value("count").toInt());
257− item->setCountVisible(entry.toMap().value("countVisible").toBool());
271+ item->setCountVisible(cachedMap.value("countVisible").toBool());
272+ item->setProgress(cachedMap.value("progress").toInt());
273+ item->setRunning(cachedMap.value("running").toBool());
258 beginInsertRows(QModelIndex(), newPosition, newPosition);
274 beginInsertRows(QModelIndex(), newPosition, newPosition);
259 m_list.insert(newPosition, item);
275 m_list.insert(newPosition, item);
260 endInsertRows();
276 endInsertRows();
plugins/Greeter/Unity/Launcher/launchermodelas.h+1
@@ -41,6 +41,7 @@ public:
41
41
42 QVariant data(const QModelIndex &index, int role) const override;
42 QVariant data(const QModelIndex &index, int role) const override;
43
43
44+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
44 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
45 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
45 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
46 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
46 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
47 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
plugins/Lights/Lights.cpp+1−1
@@ -71,7 +71,7 @@ void Lights::setColor(const QColor &color)
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/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
2pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
2pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4
4
plugins/Unity/Launcher/asadapter.cpp+4−2
@@ -34,12 +34,12 @@ ASAdapter::~ASAdapter()
34 m_accounts->deleteLater();
34 m_accounts->deleteLater();
35}
35}
36
36
37−void ASAdapter::syncItems(QList<LauncherItem *> m_list)
37+void ASAdapter::syncItems(const QList<LauncherItem*> &list)
38{
38{
39 if (m_accounts && !m_user.isEmpty()) {
39 if (m_accounts && !m_user.isEmpty()) {
40 QList<QVariantMap> items;
40 QList<QVariantMap> items;
41
41
42− Q_FOREACH(LauncherItem *item, m_list) {
42+ Q_FOREACH(LauncherItem *item, list) {
43 items << itemToVariant(item);
43 items << itemToVariant(item);
44 }
44 }
45
45
@@ -56,5 +56,7 @@ QVariantMap ASAdapter::itemToVariant(LauncherItem *item) const
56 details.insert("count", item->count());
56 details.insert("count", item->count());
57 details.insert("countVisible", item->countVisible());
57 details.insert("countVisible", item->countVisible());
58 details.insert("pinned", item->pinned());
58 details.insert("pinned", item->pinned());
59+ details.insert("running", item->running());
60+ details.insert("progress", item->progress());
59 return details;
61 return details;
60}
62}
plugins/Unity/Launcher/asadapter.h+1−4
@@ -21,7 +21,6 @@
21
21
22class LauncherItem;
22class LauncherItem;
23class AccountsServiceDBusAdaptor;
23class AccountsServiceDBusAdaptor;
24−class QDBusInterface;
25
24
26class ASAdapter
25class ASAdapter
27{
26{
@@ -29,7 +28,7 @@ public:
29 ASAdapter();
28 ASAdapter();
30 ~ASAdapter();
29 ~ASAdapter();
31
30
32− void syncItems(QList<LauncherItem*> m_list);
31+ void syncItems(const QList<LauncherItem*> &list);
33
32
34private:
33private:
35 QVariantMap itemToVariant(LauncherItem *item) const;
34 QVariantMap itemToVariant(LauncherItem *item) const;
@@ -38,8 +37,6 @@ private:
38 AccountsServiceDBusAdaptor *m_accounts;
37 AccountsServiceDBusAdaptor *m_accounts;
39 QString m_user;
38 QString m_user;
40
39
41− QDBusInterface *m_userInterface;
42−
43 friend class LauncherModelTest;
40 friend class LauncherModelTest;
44};
41};
45
42
plugins/Unity/Launcher/dbusinterface.cpp+39−17
@@ -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 */
@@ -66,6 +62,8 @@ QString DBusInterface::introspect(const QString &path) const
66 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
62 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
67 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
63 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
68 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
64 "<property name=\"countVisible\" type=\"b\" access=\"readwrite\" />"
65+ "<property name=\"progress\" type=\"i\" access=\"readwrite\" />"
66+ "<method name=\"Alert\" />"
69 "</interface>";
67 "</interface>";
70 return nodeiface;
68 return nodeiface;
71}
69}
@@ -126,6 +124,20 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
126 return false;
124 return false;
127 }
125 }
128
126
127+ /* Break down the path to just the app id */
128+ bool validpath = true;
129+ QString pathtemp = message.path();
130+ if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
131+ validpath = false;
132+ }
133+ pathtemp.remove("/com/canonical/Unity/Launcher/");
134+ if (pathtemp.indexOf('/') >= 0) {
135+ validpath = false;
136+ }
137+
138+ /* Find ourselves an appid */
139+ QString appid = decodeAppId(pathtemp);
140+
129 // First handle methods of the Launcher interface
141 // First handle methods of the Launcher interface
130 if (message.interface() == "com.canonical.Unity.Launcher") {
142 if (message.interface() == "com.canonical.Unity.Launcher") {
131 if (message.member() == "Refresh") {
143 if (message.member() == "Refresh") {
@@ -133,6 +145,13 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
133 Q_EMIT refreshCalled();
145 Q_EMIT refreshCalled();
134 return connection.send(reply);
146 return connection.send(reply);
135 }
147 }
148+ } else if (message.interface() == "com.canonical.Unity.Launcher.Item") {
149+ // Handle methods of the Launcher-Item interface
150+ if (message.member() == "Alert" && validpath) {
151+ QDBusMessage reply = message.createReply();
152+ Q_EMIT alertCalled(appid);
153+ return connection.send(reply);
154+ }
136 }
155 }
137
156
138 // Now handle dynamic properties (for launcher emblems)
157 // Now handle dynamic properties (for launcher emblems)
@@ -144,50 +163,53 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
144 return false;
163 return false;
145 }
164 }
146
165
147− /* Break down the path to just the app id */
166+ if (!validpath) {
148− QString pathtemp = message.path();
149− if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
150− return false;
151− }
152− pathtemp.remove("/com/canonical/Unity/Launcher/");
153− if (pathtemp.indexOf('/') >= 0) {
154 return false;
167 return false;
155 }
168 }
156
169
157− /* Find ourselves an appid */
158− QString appid = decodeAppId(pathtemp);
159 int index = m_launcherModel->findApplication(appid);
170 int index = m_launcherModel->findApplication(appid);
160 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
171 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
161
172
162 QVariantList retval;
173 QVariantList retval;
163 if (message.member() == "Get") {
174 if (message.member() == "Get") {
175+ QString cachedString = message.arguments()[1].toString();
164 if (!item) {
176 if (!item) {
165 return false;
177 return false;
166 }
178 }
167− if (message.arguments()[1].toString() == "count") {
179+ if (cachedString == "count") {
168 retval.append(QVariant::fromValue(QDBusVariant(item->count())));
180 retval.append(QVariant::fromValue(QDBusVariant(item->count())));
169− } else if (message.arguments()[1].toString() == "countVisible") {
181+ } else if (cachedString == "countVisible") {
170 retval.append(QVariant::fromValue(QDBusVariant(item->countVisible())));
182 retval.append(QVariant::fromValue(QDBusVariant(item->countVisible())));
183+ } else if (cachedString == "progress") {
184+ retval.append(QVariant::fromValue(QDBusVariant(item->progress())));
171 }
185 }
172 } else if (message.member() == "Set") {
186 } else if (message.member() == "Set") {
173− if (message.arguments()[1].toString() == "count") {
187+ QString cachedString = message.arguments()[1].toString();
188+ if (cachedString == "count") {
174 int newCount = message.arguments()[2].value<QDBusVariant>().variant().toInt();
189 int newCount = message.arguments()[2].value<QDBusVariant>().variant().toInt();
175 if (!item || newCount != item->count()) {
190 if (!item || newCount != item->count()) {
176 Q_EMIT countChanged(appid, newCount);
191 Q_EMIT countChanged(appid, newCount);
177 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "count", QVariant(newCount));
192 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "count", QVariant(newCount));
178 }
193 }
179− } else if (message.arguments()[1].toString() == "countVisible") {
194+ } else if (cachedString == "countVisible") {
180 bool newVisible = message.arguments()[2].value<QDBusVariant>().variant().toBool();
195 bool newVisible = message.arguments()[2].value<QDBusVariant>().variant().toBool();
181 if (!item || newVisible != item->countVisible()) {
196 if (!item || newVisible != item->countVisible()) {
182 Q_EMIT countVisibleChanged(appid, newVisible);
197 Q_EMIT countVisibleChanged(appid, newVisible);
183 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "countVisible", newVisible);
198 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "countVisible", newVisible);
184 }
199 }
200+ } else if (cachedString == "progress") {
201+ int newProgress = message.arguments()[2].value<QDBusVariant>().variant().toInt();
202+ if (!item || newProgress != item->progress()) {
203+ Q_EMIT progressChanged(appid, newProgress);
204+ notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "progress", QVariant(newProgress));
205+ }
185 }
206 }
186 } else if (message.member() == "GetAll") {
207 } else if (message.member() == "GetAll") {
187 if (item) {
208 if (item) {
188 QVariantMap all;
209 QVariantMap all;
189 all.insert("count", item->count());
210 all.insert("count", item->count());
190 all.insert("countVisible", item->countVisible());
211 all.insert("countVisible", item->countVisible());
212+ all.insert("progress", item->progress());
191 retval.append(all);
213 retval.append(all);
192 }
214 }
193 } else {
215 } else {
plugins/Unity/Launcher/dbusinterface.h+3−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;
@@ -36,7 +36,9 @@ public:
36Q_SIGNALS:
36Q_SIGNALS:
37 void countChanged(const QString &appId, int count);
37 void countChanged(const QString &appId, int count);
38 void countVisibleChanged(const QString &appId, bool countVisible);
38 void countVisibleChanged(const QString &appId, bool countVisible);
39+ void progressChanged(const QString &appId, int progress);
39 void refreshCalled();
40 void refreshCalled();
41+ void alertCalled(const QString &appId);
40
42
41private:
43private:
42 static QString decodeAppId(const QString& path);
44 static QString decodeAppId(const QString& path);
plugins/Unity/Launcher/gsettings.cpp+1−1
@@ -68,7 +68,7 @@ void GSettings::setStoredApplications(const QStringList &storedApplications)
68void GSettings::onSettingsChanged(const QString &key)
68void GSettings::onSettingsChanged(const QString &key)
69{
69{
70 if (key == "items") {
70 if (key == "items") {
71− QStringList cachedItems = m_gSettings->get("items").toStringList();
71+ const QStringList cachedItems = m_gSettings->get("items").toStringList();
72 if (m_cachedItems != cachedItems) {
72 if (m_cachedItems != cachedItems) {
73 m_cachedItems = cachedItems;
73 m_cachedItems = cachedItems;
74 Q_EMIT changed();
74 Q_EMIT changed();
plugins/Unity/Launcher/launcheritem.cpp+33
@@ -34,16 +34,22 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
34 m_count(0),
34 m_count(0),
35 m_countVisible(false),
35 m_countVisible(false),
36 m_focused(false),
36 m_focused(false),
37+ m_alerting(false),
37 m_quickList(new QuickListModel(this))
38 m_quickList(new QuickListModel(this))
38{
39{
39 QuickListEntry nameAction;
40 QuickListEntry nameAction;
40 nameAction.setActionId("launch_item");
41 nameAction.setActionId("launch_item");
41 nameAction.setText(m_name);
42 nameAction.setText(m_name);
42 m_quickList->appendAction(nameAction);
43 m_quickList->appendAction(nameAction);
44+
43 QuickListEntry pinningAction;
45 QuickListEntry pinningAction;
44 pinningAction.setActionId("pin_item");
46 pinningAction.setActionId("pin_item");
45 pinningAction.setText(gettext("Pin shortcut"));
47 pinningAction.setText(gettext("Pin shortcut"));
46 m_quickList->appendAction(pinningAction);
48 m_quickList->appendAction(pinningAction);
49+
50+ m_quitAction.setActionId("stop_item");
51+ m_quitAction.setIcon("application-exit");
52+ m_quitAction.setText(gettext("Quit"));
47}
53}
48
54
49QString LauncherItem::appId() const
55QString LauncherItem::appId() const
@@ -110,6 +116,11 @@ void LauncherItem::setRunning(bool running)
110{
116{
111 if (m_running != running) {
117 if (m_running != running) {
112 m_running = running;
118 m_running = running;
119+ if (m_running) { // add the quit action
120+ m_quickList->appendAction(m_quitAction);
121+ } else { // remove the quit action
122+ m_quickList->removeAction(m_quitAction);
123+ }
113 Q_EMIT runningChanged(running);
124 Q_EMIT runningChanged(running);
114 }
125 }
115}
126}
@@ -150,6 +161,9 @@ void LauncherItem::setCount(int count)
150 if (m_count != count) {
161 if (m_count != count) {
151 m_count = count;
162 m_count = count;
152 Q_EMIT countChanged(count);
163 Q_EMIT countChanged(count);
164+ if (m_countVisible) {
165+ setAlerting(true);
166+ }
153 }
167 }
154}
168}
155
169
@@ -163,6 +177,9 @@ void LauncherItem::setCountVisible(bool countVisible)
163 if (m_countVisible != countVisible) {
177 if (m_countVisible != countVisible) {
164 m_countVisible = countVisible;
178 m_countVisible = countVisible;
165 Q_EMIT countVisibleChanged(countVisible);
179 Q_EMIT countVisibleChanged(countVisible);
180+ if (countVisible) {
181+ setAlerting(true);
182+ }
166 }
183 }
167}
184}
168
185
@@ -175,10 +192,26 @@ void LauncherItem::setFocused(bool focused)
175{
192{
176 if (m_focused != focused) {
193 if (m_focused != focused) {
177 m_focused = focused;
194 m_focused = focused;
195+ if (focused) {
196+ setAlerting(false);
197+ }
178 Q_EMIT focusedChanged(focused);
198 Q_EMIT focusedChanged(focused);
179 }
199 }
180}
200}
181
201
202+bool LauncherItem::alerting() const
203+{
204+ return m_alerting;
205+}
206+
207+void LauncherItem::setAlerting(bool alerting)
208+{
209+ if (m_alerting != alerting) {
210+ m_alerting = alerting;
211+ Q_EMIT alertingChanged(alerting);
212+ }
213+}
214+
182unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
215unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
183{
216{
184 return m_quickList;
217 return m_quickList;
plugins/Unity/Launcher/launcheritem.h+4
@@ -44,6 +44,7 @@ public:
44 int count() const override;
44 int count() const override;
45 bool countVisible() const override;
45 bool countVisible() const override;
46 bool focused() const override;
46 bool focused() const override;
47+ bool alerting() const override;
47
48
48 unity::shell::launcher::QuickListModelInterface *quickList() const override;
49 unity::shell::launcher::QuickListModelInterface *quickList() const override;
49
50
@@ -57,6 +58,7 @@ private:
57 void setCount(int count);
58 void setCount(int count);
58 void setCountVisible(bool countVisible);
59 void setCountVisible(bool countVisible);
59 void setFocused(bool focused);
60 void setFocused(bool focused);
61+ void setAlerting(bool alerting);
60
62
61private:
63private:
62 QString m_appId;
64 QString m_appId;
@@ -69,7 +71,9 @@ private:
69 int m_count;
71 int m_count;
70 bool m_countVisible;
72 bool m_countVisible;
71 bool m_focused;
73 bool m_focused;
74+ bool m_alerting;
72 QuickListModel *m_quickList;
75 QuickListModel *m_quickList;
76+ QuickListEntry m_quitAction;
73
77
74 friend class LauncherModel;
78 friend class LauncherModel;
75};
79};
plugins/Unity/Launcher/launchermodel.cpp+78−38
@@ -36,11 +36,13 @@ LauncherModel::LauncherModel(QObject *parent):
36 m_settings(new GSettings(this)),
36 m_settings(new GSettings(this)),
37 m_dbusIface(new DBusInterface(this)),
37 m_dbusIface(new DBusInterface(this)),
38 m_asAdapter(new ASAdapter()),
38 m_asAdapter(new ASAdapter()),
39− m_appManager(0)
39+ m_appManager(nullptr)
40{
40{
41 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
41 connect(m_dbusIface, &DBusInterface::countChanged, this, &LauncherModel::countChanged);
42 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
42 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
43+ connect(m_dbusIface, &DBusInterface::progressChanged, this, &LauncherModel::progressChanged);
43 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
44 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
45+ connect(m_dbusIface, &DBusInterface::alertCalled, this, &LauncherModel::alert);
44
46
45 connect(m_settings, &GSettings::changed, this, &LauncherModel::refresh);
47 connect(m_settings, &GSettings::changed, this, &LauncherModel::refresh);
46
48
@@ -74,8 +76,6 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
74 return item->icon();
76 return item->icon();
75 case RolePinned:
77 case RolePinned:
76 return item->pinned();
78 return item->pinned();
77− case RoleRunning:
78− return item->running();
79 case RoleCount:
79 case RoleCount:
80 return item->count();
80 return item->count();
81 case RoleCountVisible:
81 case RoleCountVisible:
@@ -84,11 +84,30 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
84 return item->progress();
84 return item->progress();
85 case RoleFocused:
85 case RoleFocused:
86 return item->focused();
86 return item->focused();
87+ case RoleAlerting:
88+ return item->alerting();
89+ case RoleRunning:
90+ return item->running();
91+ default:
92+ qWarning() << Q_FUNC_INFO << "missing role, implement me";
93+ return QVariant();
87 }
94 }
88
95
89 return QVariant();
96 return QVariant();
90}
97}
91
98
99+void LauncherModel::setAlerting(const QString &appId, bool alerting) {
100+ int index = findApplication(appId);
101+ if (index >= 0) {
102+ QModelIndex modelIndex = this->index(index);
103+ LauncherItem *item = m_list.at(index);
104+ if (!item->focused()) {
105+ item->setAlerting(alerting);
106+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
107+ }
108+ }
109+}
110+
92unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
111unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
93{
112{
94 if (index < 0 || index >= m_list.count()) {
113 if (index < 0 || index >= m_list.count()) {
@@ -136,7 +155,7 @@ void LauncherModel::pin(const QString &appId, int index)
136 if (index == -1 || index == currentIndex) {
155 if (index == -1 || index == currentIndex) {
137 m_list.at(currentIndex)->setPinned(true);
156 m_list.at(currentIndex)->setPinned(true);
138 QModelIndex modelIndex = this->index(currentIndex);
157 QModelIndex modelIndex = this->index(currentIndex);
139− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
158+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
140 } else {
159 } else {
141 move(currentIndex, index);
160 move(currentIndex, index);
142 // move() will store the list to the backend itself, so just exit at this point.
161 // move() will store the list to the backend itself, so just exit at this point.
@@ -149,7 +168,7 @@ void LauncherModel::pin(const QString &appId, int index)
149
168
150 DesktopFileHandler desktopFile(appId);
169 DesktopFileHandler desktopFile(appId);
151 if (!desktopFile.isValid()) {
170 if (!desktopFile.isValid()) {
152− qWarning() << "Can't pin this application, there is no .destkop file available.";
171+ qWarning() << "Can't pin this application, there is no .desktop file available.";
153 return;
172 return;
154 }
173 }
155
174
@@ -174,7 +193,7 @@ void LauncherModel::requestRemove(const QString &appId)
174
193
175void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
194void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
176{
195{
177− int index = findApplication(appId);
196+ const int index = findApplication(appId);
178 if (index < 0) {
197 if (index < 0) {
179 return;
198 return;
180 }
199 }
@@ -182,7 +201,7 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
182 LauncherItem *item = m_list.at(index);
201 LauncherItem *item = m_list.at(index);
183 QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
202 QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
184 if (model) {
203 if (model) {
185− QString actionId = model->get(actionIndex).actionId();
204+ const QString actionId = model->get(actionIndex).actionId();
186
205
187 // Check if this is one of the launcher actions we handle ourselves
206 // Check if this is one of the launcher actions we handle ourselves
188 if (actionId == "pin_item") {
207 if (actionId == "pin_item") {
@@ -193,7 +212,10 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
193 }
212 }
194 } else if (actionId == "launch_item") {
213 } else if (actionId == "launch_item") {
195 QDesktopServices::openUrl(getUrlForAppId(appId));
214 QDesktopServices::openUrl(getUrlForAppId(appId));
196−
215+ } else if (actionId == "stop_item") { // Quit
216+ if (m_appManager) {
217+ m_appManager->stopApplication(appId);
218+ }
197 // Nope, we don't know this action, let the backend forward it to the application
219 // Nope, we don't know this action, let the backend forward it to the application
198 } else {
220 } else {
199 // TODO: forward quicklist action to app, possibly via m_dbusIface
221 // TODO: forward quicklist action to app, possibly via m_dbusIface
@@ -291,7 +313,7 @@ void LauncherModel::storeAppList()
291
313
292void LauncherModel::unpin(const QString &appId)
314void LauncherModel::unpin(const QString &appId)
293{
315{
294− int index = findApplication(appId);
316+ const int index = findApplication(appId);
295 if (index < 0) {
317 if (index < 0) {
296 return;
318 return;
297 }
319 }
@@ -300,7 +322,7 @@ void LauncherModel::unpin(const QString &appId)
300 if (m_list.at(index)->pinned()) {
322 if (m_list.at(index)->pinned()) {
301 m_list.at(index)->setPinned(false);
323 m_list.at(index)->setPinned(false);
302 QModelIndex modelIndex = this->index(index);
324 QModelIndex modelIndex = this->index(index);
303− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
325+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
304 }
326 }
305 } else {
327 } else {
306 beginRemoveRows(QModelIndex(), index, index);
328 beginRemoveRows(QModelIndex(), index, index);
@@ -322,22 +344,25 @@ int LauncherModel::findApplication(const QString &appId)
322
344
323void LauncherModel::progressChanged(const QString &appId, int progress)
345void LauncherModel::progressChanged(const QString &appId, int progress)
324{
346{
325− int idx = findApplication(appId);
347+ const int idx = findApplication(appId);
326 if (idx >= 0) {
348 if (idx >= 0) {
327 LauncherItem *item = m_list.at(idx);
349 LauncherItem *item = m_list.at(idx);
328 item->setProgress(progress);
350 item->setProgress(progress);
329− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
351+ Q_EMIT dataChanged(index(idx), index(idx), {RoleProgress});
330 }
352 }
331}
353}
332
354
333void LauncherModel::countChanged(const QString &appId, int count)
355void LauncherModel::countChanged(const QString &appId, int count)
334{
356{
335− int idx = findApplication(appId);
357+ const int idx = findApplication(appId);
336 if (idx >= 0) {
358 if (idx >= 0) {
337 LauncherItem *item = m_list.at(idx);
359 LauncherItem *item = m_list.at(idx);
338 item->setCount(count);
360 item->setCount(count);
339− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
361+ if (item->countVisible()) {
362+ setAlerting(item->appId(), true);
363+ }
340 m_asAdapter->syncItems(m_list);
364 m_asAdapter->syncItems(m_list);
365+ Q_EMIT dataChanged(index(idx), index(idx), {RoleCount});
341 }
366 }
342}
367}
343
368
@@ -347,7 +372,10 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
347 if (idx >= 0) {
372 if (idx >= 0) {
348 LauncherItem *item = m_list.at(idx);
373 LauncherItem *item = m_list.at(idx);
349 item->setCountVisible(countVisible);
374 item->setCountVisible(countVisible);
375+ if (countVisible) {
376+ setAlerting(item->appId(), true);
377+ }
350− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCountVisible);
378+ Q_EMIT dataChanged(index(idx), index(idx), {RoleCountVisible});
351
379
352 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
380 // If countVisible goes to false, and the item is neither pinned nor recent we can drop it
353 if (!countVisible && !item->pinned() && !item->recent()) {
381 if (!countVisible && !item->pinned() && !item->recent()) {
@@ -389,7 +417,8 @@ void LauncherModel::refresh()
389 item->setName(desktopFile.displayName());
417 item->setName(desktopFile.displayName());
390 item->setIcon(desktopFile.icon());
418 item->setIcon(desktopFile.icon());
391 item->setPinned(item->pinned()); // update pinned text if needed
419 item->setPinned(item->pinned()); // update pinned text if needed
420+ item->setRunning(item->running());
392− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
421+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleRunning});
393 }
422 }
394 }
423 }
395
424
@@ -407,7 +436,7 @@ void LauncherModel::refresh()
407
436
408 // Now walk through settings and see if we need to add something
437 // Now walk through settings and see if we need to add something
409 for (int settingsIndex = 0; settingsIndex < m_settings->storedApplications().count(); ++settingsIndex) {
438 for (int settingsIndex = 0; settingsIndex < m_settings->storedApplications().count(); ++settingsIndex) {
410− QString entry = m_settings->storedApplications().at(settingsIndex);
439+ const QString entry = m_settings->storedApplications().at(settingsIndex);
411 int itemIndex = -1;
440 int itemIndex = -1;
412 for (int i = 0; i < m_list.count(); ++i) {
441 for (int i = 0; i < m_list.count(); ++i) {
413 if (m_list.at(i)->appId() == entry) {
442 if (m_list.at(i)->appId() == entry) {
@@ -455,6 +484,16 @@ void LauncherModel::refresh()
455 m_asAdapter->syncItems(m_list);
484 m_asAdapter->syncItems(m_list);
456}
485}
457
486
487+void LauncherModel::alert(const QString &appId)
488+{
489+ int idx = findApplication(appId);
490+ if (idx >= 0) {
491+ LauncherItem *item = m_list.at(idx);
492+ setAlerting(item->appId(), true);
493+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleAlerting);
494+ }
495+}
496+
458void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
497void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
459{
498{
460 Q_UNUSED(parent);
499 Q_UNUSED(parent);
@@ -470,18 +509,14 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
470 return;
509 return;
471 }
510 }
472
511
473− int itemIndex = findApplication(app->appId());
512+ const int itemIndex = findApplication(app->appId());
474 if (itemIndex != -1) {
513 if (itemIndex != -1) {
475 LauncherItem *item = m_list.at(itemIndex);
514 LauncherItem *item = m_list.at(itemIndex);
476− QVector<int> roles;
477 if (!item->recent()) {
515 if (!item->recent()) {
478 item->setRecent(true);
516 item->setRecent(true);
479− m_asAdapter->syncItems(m_list);
517+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRecent});
480− roles << RoleRecent;
481 }
518 }
482 item->setRunning(true);
519 item->setRunning(true);
483− roles << RoleRunning;
484− Q_EMIT dataChanged(index(itemIndex), index(itemIndex), roles);
485 } else {
520 } else {
486 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
521 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
487 item->setRecent(true);
522 item->setRecent(true);
@@ -491,8 +526,9 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
491 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
526 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
492 m_list.append(item);
527 m_list.append(item);
493 endInsertRows();
528 endInsertRows();
494− m_asAdapter->syncItems(m_list);
495 }
529 }
530+ m_asAdapter->syncItems(m_list);
531+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRunning});
496}
532}
497
533
498void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
534void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
@@ -507,31 +543,35 @@ void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
507 }
543 }
508 }
544 }
509
545
510− if (appIndex > -1) {
546+ if (appIndex < 0) {
547+ qWarning() << Q_FUNC_INFO << "appIndex not found";
548+ return;
549+ }
550+
551+ LauncherItem * item = m_list.at(appIndex);
552+ item->setRunning(false);
553+
511− if (!m_list.at(appIndex)->pinned()) {
554+ if (!item->pinned()) {
512− beginRemoveRows(QModelIndex(), appIndex, appIndex);
555+ beginRemoveRows(QModelIndex(), appIndex, appIndex);
513− m_list.takeAt(appIndex)->deleteLater();
556+ m_list.takeAt(appIndex)->deleteLater();
514− endRemoveRows();
557+ endRemoveRows();
515− m_asAdapter->syncItems(m_list);
558+ m_asAdapter->syncItems(m_list);
516− } else {
517− LauncherItem *item = m_list.at(appIndex);
518− item->setRunning(false);
519− Q_EMIT dataChanged(index(appIndex), index(appIndex), QVector<int>() << RoleRunning);
559+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RolePinned});
520− }
521 }
560 }
561+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RoleRunning});
522}
562}
523
563
524void LauncherModel::focusedAppIdChanged()
564void LauncherModel::focusedAppIdChanged()
525{
565{
526− QString appId = m_appManager->focusedApplicationId();
566+ const QString appId = m_appManager->focusedApplicationId();
527 for (int i = 0; i < m_list.count(); ++i) {
567 for (int i = 0; i < m_list.count(); ++i) {
528 LauncherItem *item = m_list.at(i);
568 LauncherItem *item = m_list.at(i);
529 if (!item->focused() && item->appId() == appId) {
569 if (!item->focused() && item->appId() == appId) {
530 item->setFocused(true);
570 item->setFocused(true);
531− Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
571+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
532 } else if (item->focused() && item->appId() != appId) {
572 } else if (item->focused() && item->appId() != appId) {
533 item->setFocused(false);
573 item->setFocused(false);
534− Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
574+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
535 }
575 }
536 }
576 }
537}
577}
plugins/Unity/Launcher/launchermodel.h+2
@@ -45,6 +45,7 @@ public:
45
45
46 QVariant data(const QModelIndex &index, int role) const override;
46 QVariant data(const QModelIndex &index, int role) const override;
47
47
48+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
48 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
49 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
49 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
50 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
50 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
51 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
@@ -63,6 +64,7 @@ public:
63public Q_SLOTS:
64public Q_SLOTS:
64 void requestRemove(const QString &appId) override;
65 void requestRemove(const QString &appId) override;
65 Q_INVOKABLE void refresh();
66 Q_INVOKABLE void refresh();
67+ Q_INVOKABLE void alert(const QString &appId);
66
68
67private:
69private:
68 void storeAppList();
70 void storeAppList();
plugins/Unity/Launcher/quicklistentry.cpp+6−2
@@ -1,4 +1,4 @@
1−/* Copyright (C) 2013 Canonical, Ltd.
1+/* Copyright (C) 2013, 2015 Canonical, Ltd.
2 *
2 *
3 * Authors:
3 * Authors:
4 * Michael Zanetti <michael.zanetti@canonical.com>
4 * Michael Zanetti <michael.zanetti@canonical.com>
@@ -20,7 +20,6 @@
20
20
21QuickListEntry::QuickListEntry()
21QuickListEntry::QuickListEntry()
22{
22{
23−
24}
23}
25
24
26QString QuickListEntry::actionId() const
25QString QuickListEntry::actionId() const
@@ -57,3 +56,8 @@ bool QuickListEntry::clickable() const
57{
56{
58 return !m_actionId.isEmpty();
57 return !m_actionId.isEmpty();
59}
58}
59+
60+bool QuickListEntry::operator==(const QuickListEntry &other)
61+{
62+ return !other.actionId().isEmpty() && other.actionId() == m_actionId;
63+}
plugins/Unity/Launcher/quicklistentry.h+3−1
@@ -1,4 +1,4 @@
1−/* Copyright (C) 2013 Canonical, Ltd.
1+/* Copyright (C) 2013, 2015 Canonical, Ltd.
2 *
2 *
3 * Authors:
3 * Authors:
4 * Michael Zanetti <michael.zanetti@canonical.com>
4 * Michael Zanetti <michael.zanetti@canonical.com>
@@ -37,6 +37,8 @@ public:
37
37
38 bool clickable() const;
38 bool clickable() const;
39
39
40+ bool operator==(const QuickListEntry & other);
41+
40private:
42private:
41 QString m_actionId;
43 QString m_actionId;
42 QString m_text;
44 QString m_text;
plugins/Unity/Launcher/quicklistmodel.cpp+12−1
@@ -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
@@ -48,6 +48,17 @@ void QuickListModel::updateAction(const QuickListEntry &entry)
48 }
48 }
49}
49}
50
50
51+void QuickListModel::removeAction(const QuickListEntry &entry)
52+{
53+ const int start = m_list.indexOf(entry);
54+ if (start > -1) {
55+ beginRemoveRows(QModelIndex(), start, start);
56+ m_list.removeOne(entry);
57+ Q_EMIT dataChanged(index(start), index(start));
58+ endRemoveRows();
59+ }
60+}
61+
51QuickListEntry QuickListModel::get(int index) const
62QuickListEntry QuickListModel::get(int index) const
52{
63{
53 return m_list.at(index);
64 return m_list.at(index);
plugins/Unity/Launcher/quicklistmodel.h+3−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 201, 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
@@ -46,6 +46,8 @@ public:
46 */
46 */
47 void updateAction(const QuickListEntry &entry);
47 void updateAction(const QuickListEntry &entry);
48
48
49+ void removeAction(const QuickListEntry &entry);
50+
49 QuickListEntry get(int index) const;
51 QuickListEntry get(int index) const;
50
52
51 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
53 int rowCount(const QModelIndex &parent = QModelIndex()) 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+76−61
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-06-22 12:00+0200\n"
10"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-07-14 11:45+0000\n"
11+"PO-Revision-Date: 2015-07-17 12:36+0000\n"
12"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
12"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
13"Language-Team: Arabic <ar@li.org>\n"
13"Language-Team: Arabic <ar@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -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-16 05:44+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
@@ -364,19 +364,19 @@ msgstr "أدخِل الرمز السرّي"
364#: qml/Greeter/NarrowView.qml:118
364#: qml/Greeter/NarrowView.qml:118
365#, qt-format
365#, qt-format
366msgid "Sorry, incorrect %1"
366msgid "Sorry, incorrect %1"
367−msgstr ""
367+msgstr "نأسف، %1 غير صحيحية"
368
368
369#: qml/Greeter/NarrowView.qml:119
369#: qml/Greeter/NarrowView.qml:119
370msgid "Sorry, incorrect passphrase"
370msgid "Sorry, incorrect passphrase"
371−msgstr ""
371+msgstr "نأسف، عبارة السّر غير صحيحية"
372
372
373#: qml/Greeter/NarrowView.qml:120
373#: qml/Greeter/NarrowView.qml:120
374msgid "Please re-enter"
374msgid "Please re-enter"
375−msgstr ""
375+msgstr "برجاء إعادة إدخالها"
376
376
377#: qml/Greeter/NarrowView.qml:121
377#: qml/Greeter/NarrowView.qml:121
378msgid "Sorry, incorrect passcode"
378msgid "Sorry, incorrect passcode"
379−msgstr ""
379+msgstr "نأسف، الرمز السري غير صحيح"
380
380
381#: qml/Notifications/NotificationMenuItemFactory.qml:128
381#: qml/Notifications/NotificationMenuItemFactory.qml:128
382msgid "Show password"
382msgid "Show password"
@@ -384,7 +384,7 @@ msgstr "عرض كلمة السر"
384
384
385#: qml/Panel/ActiveCallHint.qml:79
385#: qml/Panel/ActiveCallHint.qml:79
386msgid "Tap to return to call..."
386msgid "Tap to return to call..."
387−msgstr ""
387+msgstr "انقر للعودة إلى المكالمة..."
388
388
389#: qml/Panel/ActiveCallHint.qml:92
389#: qml/Panel/ActiveCallHint.qml:92
390msgid "Conference"
390msgid "Conference"
@@ -398,52 +398,64 @@ msgstr ""
398#, qt-format
398#, qt-format
399msgid "%1 hour"
399msgid "%1 hour"
400msgid_plural "%1 hours"
400msgid_plural "%1 hours"
401−msgstr[0] ""
401+msgstr[0] "أقل من ساعة (%1)"
402+msgstr[1] "ساعة واحدة (%1)"
403+msgstr[2] "ساعتان (%1)"
404+msgstr[3] "%1 ساعات"
402−msgstr[1] ""
405+msgstr[4] "%1 ساعة"
406+msgstr[5] "%1 ساعة"
403
407
404#: qml/Panel/Indicators/MenuItemFactory.qml:772
408#: qml/Panel/Indicators/MenuItemFactory.qml:772
405#, qt-format
409#, qt-format
406msgid "%1 minute"
410msgid "%1 minute"
407msgid_plural "%1 minutes"
411msgid_plural "%1 minutes"
408−msgstr[0] ""
412+msgstr[0] "أقل من دقيقة (%1)"
413+msgstr[1] "دقيقة واحدة (%1)"
414+msgstr[2] "دقيقتان (%1)"
415+msgstr[3] "%1 دقائق"
409−msgstr[1] ""
416+msgstr[4] "%1 دقيقة"
417+msgstr[5] "%1 دقيقة"
410
418
411#: qml/Panel/Indicators/MenuItemFactory.qml:777
419#: qml/Panel/Indicators/MenuItemFactory.qml:777
412#, qt-format
420#, qt-format
413msgid "%1 second"
421msgid "%1 second"
414msgid_plural "%1 seconds"
422msgid_plural "%1 seconds"
415−msgstr[0] ""
423+msgstr[0] "أقل من ثانية (%1)"
424+msgstr[1] "ثانية واحدة (%1)"
425+msgstr[2] "ثانيتان (%1)"
426+msgstr[3] "%1 ثوان"
416−msgstr[1] ""
427+msgstr[4] "%1 ثانية"
428+msgstr[5] "%1 ثانية"
417
429
418#: qml/Panel/Indicators/MenuItemFactory.qml:780
430#: qml/Panel/Indicators/MenuItemFactory.qml:780
419msgid "0 seconds"
431msgid "0 seconds"
420−msgstr ""
432+msgstr "أقل من ثانية"
421
433
422#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
434#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
423#: qml/Panel/Indicators/MenuItemFactory.qml:782
435#: qml/Panel/Indicators/MenuItemFactory.qml:782
424#, qt-format
436#, qt-format
425msgid "%1 remaining"
437msgid "%1 remaining"
426−msgstr ""
438+msgstr "باق %1"
427
439
428#: qml/Panel/Indicators/MenuItemFactory.qml:788
440#: qml/Panel/Indicators/MenuItemFactory.qml:788
429msgid "In queue…"
441msgid "In queue…"
430−msgstr ""
442+msgstr "في الانتظار..."
431
443
432#: qml/Panel/Indicators/MenuItemFactory.qml:792
444#: qml/Panel/Indicators/MenuItemFactory.qml:792
433msgid "Downloading"
445msgid "Downloading"
434−msgstr ""
446+msgstr "التنزيل"
435
447
436#: qml/Panel/Indicators/MenuItemFactory.qml:794
448#: qml/Panel/Indicators/MenuItemFactory.qml:794
437msgid "Paused, tap to resume"
449msgid "Paused, tap to resume"
438−msgstr ""
450+msgstr "مُلبث، انقر للاستئناف"
439
451
440#: qml/Panel/Indicators/MenuItemFactory.qml:796
452#: qml/Panel/Indicators/MenuItemFactory.qml:796
441msgid "Canceled"
453msgid "Canceled"
442−msgstr ""
454+msgstr "تم إلغائه"
443
455
444#: qml/Panel/Indicators/MenuItemFactory.qml:798
456#: qml/Panel/Indicators/MenuItemFactory.qml:798
445msgid "Finished"
457msgid "Finished"
446−msgstr ""
458+msgstr "انتهى"
447
459
448#: qml/Panel/Indicators/MenuItemFactory.qml:800
460#: qml/Panel/Indicators/MenuItemFactory.qml:800
449msgid "Failed, tap to retry"
461msgid "Failed, tap to retry"
@@ -460,36 +472,36 @@ msgstr ""
460
472
461#: qml/Panel/Indicators/RoamingIndication.qml:27
473#: qml/Panel/Indicators/RoamingIndication.qml:27
462msgid "Roaming"
474msgid "Roaming"
463−msgstr ""
475+msgstr "التجوال"
464
476
465#: qml/Tutorial/TutorialBottomFinish.qml:24
477#: qml/Tutorial/TutorialBottomFinish.qml:24
466msgid "This action does different things for different apps"
478msgid "This action does different things for different apps"
467−msgstr ""
479+msgstr "هذا الإجراء يقوم بأشياء مختلفة على التطبيقات المختلفة"
468
480
469#: qml/Tutorial/TutorialBottomFinish.qml:25
481#: qml/Tutorial/TutorialBottomFinish.qml:25
470msgid "Tap here to finish."
482msgid "Tap here to finish."
471−msgstr ""
483+msgstr "انقر هنا للإنهاء."
472
484
473#: qml/Tutorial/TutorialBottom.qml:28
485#: qml/Tutorial/TutorialBottom.qml:28
474msgid "Open special menus"
486msgid "Open special menus"
475−msgstr ""
487+msgstr "فتح قوائم خاصة"
476
488
477#: qml/Tutorial/TutorialBottom.qml:29
489#: qml/Tutorial/TutorialBottom.qml:29
478msgid "Swipe up from the bottom edge."
490msgid "Swipe up from the bottom edge."
479−msgstr ""
491+msgstr "اسحب لأعلى من الحافة السفلى."
480
492
481#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
493#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
482#: qml/Tutorial/TutorialRight.qml:144
494#: qml/Tutorial/TutorialRight.qml:144
483msgid "Tap here to continue."
495msgid "Tap here to continue."
484−msgstr ""
496+msgstr "انقر هنا للمتابعة."
485
497
486#: qml/Tutorial/TutorialContent.qml:81
498#: qml/Tutorial/TutorialContent.qml:81
487msgid "Click here to finish."
499msgid "Click here to finish."
488−msgstr ""
500+msgstr "انقر هنا للإنهاء."
489
501
490#: qml/Tutorial/TutorialLeftFinish.qml:24
502#: qml/Tutorial/TutorialLeftFinish.qml:24
491msgid "These are the shortcuts to favorite apps"
503msgid "These are the shortcuts to favorite apps"
492−msgstr ""
504+msgstr "هذه اختصارات للتطبيقات المُفضلة"
493
505
494#: qml/Tutorial/TutorialLeft.qml:26
506#: qml/Tutorial/TutorialLeft.qml:26
495msgid "Open the launcher"
507msgid "Open the launcher"
@@ -497,7 +509,7 @@ msgstr ""
497
509
498#: qml/Tutorial/TutorialLeft.qml:27
510#: qml/Tutorial/TutorialLeft.qml:27
499msgid "Short swipe from the left edge."
511msgid "Short swipe from the left edge."
500−msgstr ""
512+msgstr "اسحب سحبة قصيرة من الحافة اليسرى."
501
513
502#: qml/Tutorial/TutorialPage.qml:178
514#: qml/Tutorial/TutorialPage.qml:178
503msgid "You almost got it!"
515msgid "You almost got it!"
@@ -505,7 +517,7 @@ msgstr ""
505
517
506#: qml/Tutorial/TutorialPage.qml:195
518#: qml/Tutorial/TutorialPage.qml:195
507msgid "Try again."
519msgid "Try again."
508−msgstr ""
520+msgstr "حاول مجددًا."
509
521
510#: qml/Tutorial/TutorialRight.qml:31
522#: qml/Tutorial/TutorialRight.qml:31
511msgid "To view open apps"
523msgid "To view open apps"
@@ -513,138 +525,141 @@ msgstr ""
513
525
514#: qml/Tutorial/TutorialRight.qml:32
526#: qml/Tutorial/TutorialRight.qml:32
515msgid "Long swipe from the right edge."
527msgid "Long swipe from the right edge."
516−msgstr ""
528+msgstr "سحبة طويلة من الحافة اليمنى."
517
529
518#: qml/Tutorial/TutorialRight.qml:122
530#: qml/Tutorial/TutorialRight.qml:122
519msgid "View all your running tasks."
531msgid "View all your running tasks."
520−msgstr ""
532+msgstr "عرض جميع المهام النشطة."
521
533
522#: qml/Wizard/Page.qml:89
534#: qml/Wizard/Page.qml:89
523msgctxt "Button: Go back one page in the Wizard"
535msgctxt "Button: Go back one page in the Wizard"
524msgid "Back"
536msgid "Back"
525−msgstr ""
537+msgstr "عودة"
526
538
527#: qml/Wizard/Pages/here-terms.qml:27
539#: qml/Wizard/Pages/here-terms.qml:27
528msgid "Terms & Conditions"
540msgid "Terms & Conditions"
529−msgstr ""
541+msgstr "الشروط والأحكام"
530
542
531#: qml/Wizard/Pages/passwd-confirm.qml:49
543#: qml/Wizard/Pages/passwd-confirm.qml:49
532msgid "Confirm passphrase"
544msgid "Confirm passphrase"
533−msgstr ""
545+msgstr "تأكيد العبارة السرية"
534
546
535#: qml/Wizard/Pages/passwd-confirm.qml:50
547#: qml/Wizard/Pages/passwd-confirm.qml:50
536msgid "Confirm passcode"
548msgid "Confirm passcode"
537−msgstr ""
549+msgstr "تأكيد الرمز السري"
538
550
539#: qml/Wizard/Pages/passwd-confirm.qml:53
551#: qml/Wizard/Pages/passwd-confirm.qml:53
540#: qml/Wizard/Pages/passwd-confirm.qml:54
552#: qml/Wizard/Pages/passwd-confirm.qml:54
541msgid "Please try again."
553msgid "Please try again."
542−msgstr ""
554+msgstr "حاول مجددا."
543
555
544#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
556#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
545#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
557#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
546#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
558#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
547#: qml/Wizard/Pages/60-reporting.qml:50
559#: qml/Wizard/Pages/60-reporting.qml:50
548msgid "Continue"
560msgid "Continue"
549−msgstr ""
561+msgstr "متابعة"
550
562
551#: qml/Wizard/Pages/passwd-set.qml:61
563#: qml/Wizard/Pages/passwd-set.qml:61
552msgid "Choose your passcode"
564msgid "Choose your passcode"
553−msgstr ""
565+msgstr "اختر الرمز السري خاصتك"
554
566
555#: qml/Wizard/Pages/passwd-set.qml:68
567#: qml/Wizard/Pages/passwd-set.qml:68
556msgid "Passphrase must be 4 characters long"
568msgid "Passphrase must be 4 characters long"
557−msgstr ""
569+msgstr "يجب أن تحتوي العبارة السرية على 4 أحرف على الأقل"
558
570
559#: qml/Wizard/Pages/10-welcome.qml:27
571#: qml/Wizard/Pages/10-welcome.qml:27
560msgid "Hi!"
572msgid "Hi!"
561−msgstr ""
573+msgstr "أهلا!"
562
574
563#: qml/Wizard/Pages/10-welcome.qml:44
575#: qml/Wizard/Pages/10-welcome.qml:44
564msgid "Welcome to your Ubuntu phone."
576msgid "Welcome to your Ubuntu phone."
565−msgstr ""
577+msgstr "مرحبا بك في أوبونتو للهواتف."
566
578
567#: qml/Wizard/Pages/10-welcome.qml:52
579#: qml/Wizard/Pages/10-welcome.qml:52
568msgid "Let’s get started."
580msgid "Let’s get started."
569−msgstr ""
581+msgstr "هيا بنا نبدأ."
570
582
571#: qml/Wizard/Pages/20-sim.qml:25
583#: qml/Wizard/Pages/20-sim.qml:25
572msgid "Add a SIM card and restart your device"
584msgid "Add a SIM card and restart your device"
573−msgstr ""
585+msgstr "أدخِل شريحة SIM ثم أعِد تشغيل الجهاز"
574
586
575#: qml/Wizard/Pages/20-sim.qml:59
587#: qml/Wizard/Pages/20-sim.qml:59
576msgid "Without it, you won’t be able to make calls or use text messaging."
588msgid "Without it, you won’t be able to make calls or use text messaging."
577msgstr ""
589msgstr ""
590+"بدونها لن تتمكن من إجراء أي مكالمات هاتفية أو استخدام الرسائل النصية."
578
591
579#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
592#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
580msgid "Skip"
593msgid "Skip"
581−msgstr ""
594+msgstr "تخطي"
582
595
583#: qml/Wizard/Pages/30-passwd-type.qml:39
596#: qml/Wizard/Pages/30-passwd-type.qml:39
584msgid "Lock security"
597msgid "Lock security"
585−msgstr ""
598+msgstr "قِفل الأمان"
586
599
587#: qml/Wizard/Pages/30-passwd-type.qml:74
600#: qml/Wizard/Pages/30-passwd-type.qml:74
588msgid "Please select how you’d like to unlock your phone."
601msgid "Please select how you’d like to unlock your phone."
589−msgstr ""
602+msgstr "رجاءً اختر الطريقة التي ترغب بأن توصد هاتفك بها."
590
603
591#: qml/Wizard/Pages/30-passwd-type.qml:97
604#: qml/Wizard/Pages/30-passwd-type.qml:97
592msgctxt "Label: Type of security method"
605msgctxt "Label: Type of security method"
593msgid "Swipe"
606msgid "Swipe"
594−msgstr ""
607+msgstr "سحبة"
595
608
596#: qml/Wizard/Pages/30-passwd-type.qml:98
609#: qml/Wizard/Pages/30-passwd-type.qml:98
597msgctxt "Label: Description of security method"
610msgctxt "Label: Description of security method"
598msgid "No security"
611msgid "No security"
599−msgstr ""
612+msgstr "بدون أمان"
600
613
601#: qml/Wizard/Pages/30-passwd-type.qml:100
614#: qml/Wizard/Pages/30-passwd-type.qml:100
602msgctxt "Label: Type of security method"
615msgctxt "Label: Type of security method"
603msgid "Passcode"
616msgid "Passcode"
604−msgstr ""
617+msgstr "رمز سري"
605
618
606#: qml/Wizard/Pages/30-passwd-type.qml:101
619#: qml/Wizard/Pages/30-passwd-type.qml:101
607msgctxt "Label: Description of security method"
620msgctxt "Label: Description of security method"
608msgid "4 digits only"
621msgid "4 digits only"
609−msgstr ""
622+msgstr "4 أرقام فقط"
610
623
611#: qml/Wizard/Pages/30-passwd-type.qml:103
624#: qml/Wizard/Pages/30-passwd-type.qml:103
612msgctxt "Label: Type of security method"
625msgctxt "Label: Type of security method"
613msgid "Passphrase"
626msgid "Passphrase"
614−msgstr ""
627+msgstr "عبارة سرية"
615
628
616#: qml/Wizard/Pages/30-passwd-type.qml:104
629#: qml/Wizard/Pages/30-passwd-type.qml:104
617msgctxt "Label: Description of security method"
630msgctxt "Label: Description of security method"
618msgid "Numbers and letters"
631msgid "Numbers and letters"
619−msgstr ""
632+msgstr "أرقام وحروف"
620
633
621#: qml/Wizard/Pages/40-wifi.qml:29
634#: qml/Wizard/Pages/40-wifi.qml:29
622msgid "Connect to Wi‑Fi"
635msgid "Connect to Wi‑Fi"
623−msgstr ""
636+msgstr "اتصل بالواي فاي"
624
637
625#: qml/Wizard/Pages/40-wifi.qml:168
638#: qml/Wizard/Pages/40-wifi.qml:168
626msgid "Available networks…"
639msgid "Available networks…"
627−msgstr ""
640+msgstr "الشبكات المتاحة..."
628
641
629#: qml/Wizard/Pages/40-wifi.qml:169
642#: qml/Wizard/Pages/40-wifi.qml:169
630msgid "No available networks."
643msgid "No available networks."
631−msgstr ""
644+msgstr "لا توجد شبكات متاحة."
632
645
633#: qml/Wizard/Pages/50-location.qml:27
646#: qml/Wizard/Pages/50-location.qml:27
634msgid "Location"
647msgid "Location"
635−msgstr ""
648+msgstr "الموقع"
636
649
637#: qml/Wizard/Pages/50-location.qml:62
650#: qml/Wizard/Pages/50-location.qml:62
638msgid "Let the phone detect your location:"
651msgid "Let the phone detect your location:"
639−msgstr ""
652+msgstr "دع هاتفك يحدد موقعك:"
640
653
641#: qml/Wizard/Pages/50-location.qml:69
654#: qml/Wizard/Pages/50-location.qml:69
642msgid "Using GPS only (less accurate)"
655msgid "Using GPS only (less accurate)"
643−msgstr ""
656+msgstr "باستخدام نظام تحديد المواقع (GPS) فقط (أقل دقّة)"
644
657
645#: qml/Wizard/Pages/50-location.qml:86
658#: qml/Wizard/Pages/50-location.qml:86
646msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
659msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
647msgstr ""
660msgstr ""
661+"باستخدام نظام تحديد المواقع (GPS)، والواي فاي، وبيانات الشبكة الخلوية "
662+"(مستحسن)"
648
663
649#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
664#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
650#: qml/Wizard/Pages/50-location.qml:103
665#: qml/Wizard/Pages/50-location.qml:103
@@ -659,11 +674,11 @@ msgstr ""
659
674
660#: qml/Wizard/Pages/50-location.qml:124
675#: qml/Wizard/Pages/50-location.qml:124
661msgid "You can change your mind later in <b>System Settings</b>."
676msgid "You can change your mind later in <b>System Settings</b>."
662−msgstr ""
677+msgstr "يمكنك تغيير رأيك في وقت لاحق عن طريق <b>إعدادات النظام</b>."
663
678
664#: qml/Wizard/Pages/60-reporting.qml:24
679#: qml/Wizard/Pages/60-reporting.qml:24
665msgid "Improving your experience"
680msgid "Improving your experience"
666−msgstr ""
681+msgstr "تحسين تجربتك"
667
682
668#: qml/Wizard/Pages/60-reporting.qml:36
683#: qml/Wizard/Pages/60-reporting.qml:36
669msgid ""
684msgid ""
po/ga.ponew file+697
@@ -0,0 +1,697 @@
1+# Irish translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11+"PO-Revision-Date: 2015-07-20 16:25+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Irish <ga@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
18+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
19+"X-Generator: Launchpad (build 17628)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr "Focal Faire: "
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
35+#. This format string is used for showing, on a 12-hour clock, times that happen yesterday.
36+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
37+#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
38+#: plugins/Utils/relativetimeformatter.cpp:157
39+msgid "Yesterday %l:%M %p"
40+msgstr ""
41+
42+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
43+#. This format string is used for showing, on a 12-hour clock, times that happened today.
44+#. en_US example: "%l:%M %p" --> "1:00 PM"
45+#: plugins/Utils/relativetimeformatter.cpp:164
46+msgid "%l:%M %p"
47+msgstr ""
48+
49+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
50+#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
51+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
52+#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
53+#: plugins/Utils/relativetimeformatter.cpp:172
54+msgid "Tomorrow %l:%M %p"
55+msgstr ""
56+
57+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
58+#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
59+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
60+#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
61+#: plugins/Utils/relativetimeformatter.cpp:181
62+msgid "%a %l:%M %p"
63+msgstr ""
64+
65+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
66+#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
67+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
68+#. en_US example: "%a %d %b\u2003%l:%M %p" --> "Fri Oct 31 1:00 PM"
69+#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM"
70+#: plugins/Utils/relativetimeformatter.cpp:190
71+msgid "%a %d %b %l:%M %p"
72+msgstr ""
73+
74+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
75+#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
76+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
77+#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
78+#: plugins/Utils/relativetimeformatter.cpp:201
79+msgid "Yesterday %H:%M"
80+msgstr ""
81+
82+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
83+#. This format string is used for showing, on a 24-hour clock, times that happened today.
84+#. en_US example: "%H:%M" --> "13:00"
85+#: plugins/Utils/relativetimeformatter.cpp:208
86+msgid "%H:%M"
87+msgstr ""
88+
89+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
90+#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
91+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
92+#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
93+#: plugins/Utils/relativetimeformatter.cpp:216
94+msgid "Tomorrow %H:%M"
95+msgstr ""
96+
97+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
98+#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
99+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
100+#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
101+#: plugins/Utils/relativetimeformatter.cpp:225
102+msgid "%a %H:%M"
103+msgstr ""
104+
105+#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
106+#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
107+#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
108+#. en_US example: "%a %d %b\u2003%H:%M" --> "Fri Oct 31 13:00"
109+#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
110+#: plugins/Utils/relativetimeformatter.cpp:234
111+msgid "%a %d %b %H:%M"
112+msgstr ""
113+
114+#: qml/Components/DelayedLockscreen.qml:49
115+msgid "Device Locked"
116+msgstr ""
117+
118+#: qml/Components/DelayedLockscreen.qml:64
119+msgid "You have been locked out due to too many failed passphrase attempts."
120+msgstr ""
121+
122+#: qml/Components/DelayedLockscreen.qml:65
123+msgid "You have been locked out due to too many failed passcode attempts."
124+msgstr ""
125+
126+#: qml/Components/DelayedLockscreen.qml:74
127+#, qt-format
128+msgid "Please wait %1 minute and then try again…"
129+msgid_plural "Please wait %1 minutes and then try again…"
130+msgstr[0] ""
131+msgstr[1] ""
132+
133+#: qml/Components/Dialogs.qml:69
134+msgctxt "Title: Lock/Log out dialog"
135+msgid "Log out"
136+msgstr ""
137+
138+#: qml/Components/Dialogs.qml:70
139+msgid "Are you sure you want to log out?"
140+msgstr ""
141+
142+#: qml/Components/Dialogs.qml:72
143+msgctxt "Button: Lock the system"
144+msgid "Lock"
145+msgstr ""
146+
147+#: qml/Components/Dialogs.qml:79
148+msgctxt "Button: Log out from the system"
149+msgid "Log Out"
150+msgstr ""
151+
152+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153+#: qml/Components/Dialogs.qml:173
154+msgid "Cancel"
155+msgstr ""
156+
157+#: qml/Components/Dialogs.qml:98
158+msgctxt "Title: Reboot/Shut down dialog"
159+msgid "Shut down"
160+msgstr ""
161+
162+#: qml/Components/Dialogs.qml:99
163+msgid "Are you sure you want to shut down?"
164+msgstr ""
165+
166+#: qml/Components/Dialogs.qml:101
167+msgctxt "Button: Reboot the system"
168+msgid "Reboot"
169+msgstr ""
170+
171+#: qml/Components/Dialogs.qml:109
172+msgctxt "Button: Shut down the system"
173+msgid "Shut down"
174+msgstr ""
175+
176+#: qml/Components/Dialogs.qml:129
177+msgctxt "Title: Reboot dialog"
178+msgid "Reboot"
179+msgstr ""
180+
181+#: qml/Components/Dialogs.qml:130
182+msgid "Are you sure you want to reboot?"
183+msgstr ""
184+
185+#: qml/Components/Dialogs.qml:132
186+msgid "No"
187+msgstr ""
188+
189+#: qml/Components/Dialogs.qml:138
190+msgid "Yes"
191+msgstr ""
192+
193+#: qml/Components/Dialogs.qml:152
194+msgctxt "Title: Power off/Restart dialog"
195+msgid "Power"
196+msgstr ""
197+
198+#: qml/Components/Dialogs.qml:153
199+msgid ""
200+"Are you sure you would like\n"
201+"to power off?"
202+msgstr ""
203+
204+#: qml/Components/Dialogs.qml:155
205+msgctxt "Button: Power off the system"
206+msgid "Power off"
207+msgstr ""
208+
209+#: qml/Components/Dialogs.qml:164
210+msgctxt "Button: Restart the system"
211+msgid "Restart"
212+msgstr ""
213+
214+#: qml/Components/Lockscreen.qml:245
215+msgid "Return to Call"
216+msgstr ""
217+
218+#: qml/Components/Lockscreen.qml:245
219+msgid "Emergency Call"
220+msgstr ""
221+
222+#: qml/Components/Lockscreen.qml:277
223+msgid "OK"
224+msgstr ""
225+
226+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
227+msgid "See less"
228+msgstr ""
229+
230+#: qml/Dash/GenericScopeView.qml:532
231+msgid "See all"
232+msgstr ""
233+
234+#: qml/Dash/GenericScopeView.qml:594
235+msgctxt "Label: Hint for dash search line edit"
236+msgid "Search"
237+msgstr ""
238+
239+#: qml/Dash/PageHeader.qml:269
240+msgctxt "Button: Open the Ubuntu Store"
241+msgid "Store"
242+msgstr ""
243+
244+#: qml/Dash/PageHeader.qml:276
245+msgctxt "Button: Start a search in the current dash scope"
246+msgid "Search"
247+msgstr ""
248+
249+#: qml/Dash/PageHeader.qml:286
250+msgctxt "Button: Show the current dash scope settings"
251+msgid "Settings"
252+msgstr ""
253+
254+#: qml/Dash/PageHeader.qml:293
255+msgid "Remove from Favorites"
256+msgstr ""
257+
258+#: qml/Dash/PageHeader.qml:293
259+msgid "Add to Favorites"
260+msgstr ""
261+
262+#: qml/Dash/Previews/PreviewActionCombo.qml:35
263+msgid "More..."
264+msgstr ""
265+
266+#: qml/Dash/Previews/PreviewActionCombo.qml:35
267+msgid "Less..."
268+msgstr ""
269+
270+#: qml/Dash/Previews/PreviewRatingInput.qml:84
271+msgid "Rate this"
272+msgstr ""
273+
274+#: qml/Dash/Previews/PreviewRatingInput.qml:129
275+msgid "Add a review"
276+msgstr ""
277+
278+#: qml/Dash/Previews/PreviewRatingInput.qml:169
279+msgid "Send"
280+msgstr ""
281+
282+#: qml/Dash/PullToRefreshScopeStyle.qml:55
283+msgid "Pull to refresh…"
284+msgstr ""
285+
286+#: qml/Dash/PullToRefreshScopeStyle.qml:60
287+msgid "Release to refresh…"
288+msgstr ""
289+
290+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
291+msgid "Enable location data"
292+msgstr ""
293+
294+#: qml/Dash/ScopesList.qml:67
295+msgid "Manage"
296+msgstr ""
297+
298+#: qml/Dash/ScopesList.qml:113
299+msgid "Home"
300+msgstr ""
301+
302+#: qml/Dash/ScopesList.qml:114
303+msgid "Also installed"
304+msgstr ""
305+
306+#: qml/Greeter/CoverPage.qml:107
307+msgid "Unlock"
308+msgstr ""
309+
310+#: qml/Greeter/LoginList.qml:236
311+msgid "Retry"
312+msgstr ""
313+
314+#: qml/Greeter/LoginList.qml:237
315+msgid "Tap to unlock"
316+msgstr ""
317+
318+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
319+msgid "Sorry, incorrect passphrase."
320+msgstr ""
321+
322+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
323+msgid "Sorry, incorrect passcode."
324+msgstr ""
325+
326+#: qml/Greeter/NarrowView.qml:55
327+msgid "This will be your last attempt."
328+msgstr ""
329+
330+#: qml/Greeter/NarrowView.qml:57
331+msgid ""
332+"If passphrase is entered incorrectly, your phone will conduct a factory "
333+"reset and all personal data will be deleted."
334+msgstr ""
335+
336+#: qml/Greeter/NarrowView.qml:58
337+msgid ""
338+"If passcode is entered incorrectly, your phone will conduct a factory reset "
339+"and all personal data will be deleted."
340+msgstr ""
341+
342+#: qml/Greeter/NarrowView.qml:115
343+#, qt-format
344+msgid "Enter %1"
345+msgstr ""
346+
347+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
348+msgid "Enter passphrase"
349+msgstr ""
350+
351+#: qml/Greeter/NarrowView.qml:117
352+msgid "Enter passcode"
353+msgstr ""
354+
355+#: qml/Greeter/NarrowView.qml:118
356+#, qt-format
357+msgid "Sorry, incorrect %1"
358+msgstr ""
359+
360+#: qml/Greeter/NarrowView.qml:119
361+msgid "Sorry, incorrect passphrase"
362+msgstr ""
363+
364+#: qml/Greeter/NarrowView.qml:120
365+msgid "Please re-enter"
366+msgstr ""
367+
368+#: qml/Greeter/NarrowView.qml:121
369+msgid "Sorry, incorrect passcode"
370+msgstr ""
371+
372+#: qml/Notifications/NotificationMenuItemFactory.qml:128
373+msgid "Show password"
374+msgstr ""
375+
376+#: qml/Panel/ActiveCallHint.qml:79
377+msgid "Tap to return to call..."
378+msgstr ""
379+
380+#: qml/Panel/ActiveCallHint.qml:92
381+msgid "Conference"
382+msgstr ""
383+
384+#: qml/Panel/Indicators/MenuItemFactory.qml:639
385+msgid "Nothing is playing"
386+msgstr ""
387+
388+#: qml/Panel/Indicators/MenuItemFactory.qml:768
389+#, qt-format
390+msgid "%1 hour"
391+msgid_plural "%1 hours"
392+msgstr[0] ""
393+msgstr[1] ""
394+
395+#: qml/Panel/Indicators/MenuItemFactory.qml:772
396+#, qt-format
397+msgid "%1 minute"
398+msgid_plural "%1 minutes"
399+msgstr[0] ""
400+msgstr[1] ""
401+
402+#: qml/Panel/Indicators/MenuItemFactory.qml:777
403+#, qt-format
404+msgid "%1 second"
405+msgid_plural "%1 seconds"
406+msgstr[0] ""
407+msgstr[1] ""
408+
409+#: qml/Panel/Indicators/MenuItemFactory.qml:780
410+msgid "0 seconds"
411+msgstr ""
412+
413+#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
414+#: qml/Panel/Indicators/MenuItemFactory.qml:782
415+#, qt-format
416+msgid "%1 remaining"
417+msgstr ""
418+
419+#: qml/Panel/Indicators/MenuItemFactory.qml:788
420+msgid "In queue…"
421+msgstr ""
422+
423+#: qml/Panel/Indicators/MenuItemFactory.qml:792
424+msgid "Downloading"
425+msgstr ""
426+
427+#: qml/Panel/Indicators/MenuItemFactory.qml:794
428+msgid "Paused, tap to resume"
429+msgstr ""
430+
431+#: qml/Panel/Indicators/MenuItemFactory.qml:796
432+msgid "Canceled"
433+msgstr ""
434+
435+#: qml/Panel/Indicators/MenuItemFactory.qml:798
436+msgid "Finished"
437+msgstr ""
438+
439+#: qml/Panel/Indicators/MenuItemFactory.qml:800
440+msgid "Failed, tap to retry"
441+msgstr ""
442+
443+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
444+msgctxt "Label: Hint in message indicator line edit"
445+msgid "Reply"
446+msgstr ""
447+
448+#: qml/Panel/Indicators/ModemInfoItem.qml:105
449+msgid "Unlock SIM"
450+msgstr ""
451+
452+#: qml/Panel/Indicators/RoamingIndication.qml:27
453+msgid "Roaming"
454+msgstr ""
455+
456+#: qml/Tutorial/TutorialBottomFinish.qml:24
457+msgid "This action does different things for different apps"
458+msgstr ""
459+
460+#: qml/Tutorial/TutorialBottomFinish.qml:25
461+msgid "Tap here to finish."
462+msgstr ""
463+
464+#: qml/Tutorial/TutorialBottom.qml:28
465+msgid "Open special menus"
466+msgstr ""
467+
468+#: qml/Tutorial/TutorialBottom.qml:29
469+msgid "Swipe up from the bottom edge."
470+msgstr ""
471+
472+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
473+#: qml/Tutorial/TutorialRight.qml:144
474+msgid "Tap here to continue."
475+msgstr ""
476+
477+#: qml/Tutorial/TutorialContent.qml:81
478+msgid "Click here to finish."
479+msgstr ""
480+
481+#: qml/Tutorial/TutorialLeftFinish.qml:24
482+msgid "These are the shortcuts to favorite apps"
483+msgstr ""
484+
485+#: qml/Tutorial/TutorialLeft.qml:26
486+msgid "Open the launcher"
487+msgstr ""
488+
489+#: qml/Tutorial/TutorialLeft.qml:27
490+msgid "Short swipe from the left edge."
491+msgstr ""
492+
493+#: qml/Tutorial/TutorialPage.qml:178
494+msgid "You almost got it!"
495+msgstr ""
496+
497+#: qml/Tutorial/TutorialPage.qml:195
498+msgid "Try again."
499+msgstr ""
500+
501+#: qml/Tutorial/TutorialRight.qml:31
502+msgid "To view open apps"
503+msgstr ""
504+
505+#: qml/Tutorial/TutorialRight.qml:32
506+msgid "Long swipe from the right edge."
507+msgstr ""
508+
509+#: qml/Tutorial/TutorialRight.qml:122
510+msgid "View all your running tasks."
511+msgstr ""
512+
513+#: qml/Wizard/Page.qml:89
514+msgctxt "Button: Go back one page in the Wizard"
515+msgid "Back"
516+msgstr ""
517+
518+#: qml/Wizard/Pages/here-terms.qml:27
519+msgid "Terms & Conditions"
520+msgstr ""
521+
522+#: qml/Wizard/Pages/passwd-confirm.qml:49
523+msgid "Confirm passphrase"
524+msgstr ""
525+
526+#: qml/Wizard/Pages/passwd-confirm.qml:50
527+msgid "Confirm passcode"
528+msgstr ""
529+
530+#: qml/Wizard/Pages/passwd-confirm.qml:53
531+#: qml/Wizard/Pages/passwd-confirm.qml:54
532+msgid "Please try again."
533+msgstr ""
534+
535+#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
536+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
537+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
538+#: qml/Wizard/Pages/60-reporting.qml:50
539+msgid "Continue"
540+msgstr ""
541+
542+#: qml/Wizard/Pages/passwd-set.qml:61
543+msgid "Choose your passcode"
544+msgstr ""
545+
546+#: qml/Wizard/Pages/passwd-set.qml:68
547+msgid "Passphrase must be 4 characters long"
548+msgstr ""
549+
550+#: qml/Wizard/Pages/10-welcome.qml:27
551+msgid "Hi!"
552+msgstr ""
553+
554+#: qml/Wizard/Pages/10-welcome.qml:44
555+msgid "Welcome to your Ubuntu phone."
556+msgstr ""
557+
558+#: qml/Wizard/Pages/10-welcome.qml:52
559+msgid "Let’s get started."
560+msgstr ""
561+
562+#: qml/Wizard/Pages/20-sim.qml:25
563+msgid "Add a SIM card and restart your device"
564+msgstr ""
565+
566+#: qml/Wizard/Pages/20-sim.qml:59
567+msgid "Without it, you won’t be able to make calls or use text messaging."
568+msgstr ""
569+
570+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
571+msgid "Skip"
572+msgstr ""
573+
574+#: qml/Wizard/Pages/30-passwd-type.qml:39
575+msgid "Lock security"
576+msgstr ""
577+
578+#: qml/Wizard/Pages/30-passwd-type.qml:74
579+msgid "Please select how you’d like to unlock your phone."
580+msgstr ""
581+
582+#: qml/Wizard/Pages/30-passwd-type.qml:97
583+msgctxt "Label: Type of security method"
584+msgid "Swipe"
585+msgstr ""
586+
587+#: qml/Wizard/Pages/30-passwd-type.qml:98
588+msgctxt "Label: Description of security method"
589+msgid "No security"
590+msgstr ""
591+
592+#: qml/Wizard/Pages/30-passwd-type.qml:100
593+msgctxt "Label: Type of security method"
594+msgid "Passcode"
595+msgstr ""
596+
597+#: qml/Wizard/Pages/30-passwd-type.qml:101
598+msgctxt "Label: Description of security method"
599+msgid "4 digits only"
600+msgstr ""
601+
602+#: qml/Wizard/Pages/30-passwd-type.qml:103
603+msgctxt "Label: Type of security method"
604+msgid "Passphrase"
605+msgstr ""
606+
607+#: qml/Wizard/Pages/30-passwd-type.qml:104
608+msgctxt "Label: Description of security method"
609+msgid "Numbers and letters"
610+msgstr ""
611+
612+#: qml/Wizard/Pages/40-wifi.qml:29
613+msgid "Connect to Wi‑Fi"
614+msgstr ""
615+
616+#: qml/Wizard/Pages/40-wifi.qml:168
617+msgid "Available networks…"
618+msgstr ""
619+
620+#: qml/Wizard/Pages/40-wifi.qml:169
621+msgid "No available networks."
622+msgstr ""
623+
624+#: qml/Wizard/Pages/50-location.qml:27
625+msgid "Location"
626+msgstr ""
627+
628+#: qml/Wizard/Pages/50-location.qml:62
629+msgid "Let the phone detect your location:"
630+msgstr ""
631+
632+#: qml/Wizard/Pages/50-location.qml:69
633+msgid "Using GPS only (less accurate)"
634+msgstr ""
635+
636+#: qml/Wizard/Pages/50-location.qml:86
637+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
638+msgstr ""
639+
640+#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
641+#: qml/Wizard/Pages/50-location.qml:103
642+msgid ""
643+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
644+"conditions</a>."
645+msgstr ""
646+
647+#: qml/Wizard/Pages/50-location.qml:112
648+msgid "Not at all"
649+msgstr ""
650+
651+#: qml/Wizard/Pages/50-location.qml:124
652+msgid "You can change your mind later in <b>System Settings</b>."
653+msgstr ""
654+
655+#: qml/Wizard/Pages/60-reporting.qml:24
656+msgid "Improving your experience"
657+msgstr ""
658+
659+#: qml/Wizard/Pages/60-reporting.qml:36
660+msgid ""
661+"Your phone is set up to automatically report errors to Canonical and its "
662+"partners, the makers of the operating system."
663+msgstr ""
664+
665+#: qml/Wizard/Pages/60-reporting.qml:43
666+msgid ""
667+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
668+"Privacy</b>"
669+msgstr ""
670+
671+#: qml/Wizard/Pages/80-finished.qml:24
672+msgid "All done"
673+msgstr ""
674+
675+#: qml/Wizard/Pages/80-finished.qml:39
676+msgid "Nice work!"
677+msgstr ""
678+
679+#: qml/Wizard/Pages/80-finished.qml:46
680+msgid "Your phone is now ready to use."
681+msgstr ""
682+
683+#: qml/Wizard/Pages/80-finished.qml:53
684+msgid "Finish"
685+msgstr ""
686+
687+#. Translators: This is the arrow for "Back" buttons
688+#: qml/Wizard/StackButton.qml:39
689+#, qt-format
690+msgid "〈 %1"
691+msgstr ""
692+
693+#. Translators: This is the arrow for "Forward" buttons
694+#: qml/Wizard/StackButton.qml:42
695+#, qt-format
696+msgid "%1 〉"
697+msgstr ""
po/gd.po+29−23
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-06-22 12:00+0200\n"
10"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-05-01 16:39+0000\n"
11+"PO-Revision-Date: 2015-07-16 10:56+0000\n"
12"Last-Translator: GunChleoc <Unknown>\n"
12"Last-Translator: GunChleoc <Unknown>\n"
13"Language-Team: Fòram na Gàidhlig\n"
13"Language-Team: Fòram na Gàidhlig\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
17"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
18"(n > 2 && n < 20) ? 2 : 3;\n"
18"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-07-04 05:37+0000\n"
19+"X-Launchpad-Export-Date: 2015-07-17 05:37+0000\n"
20−"X-Generator: Launchpad (build 17597)\n"
20+"X-Generator: Launchpad (build 17628)\n"
21"Language: gd\n"
21"Language: gd\n"
22
22
23#: plugins/LightDM/Greeter.cpp:112
23#: plugins/LightDM/Greeter.cpp:112
@@ -39,14 +39,14 @@ msgstr "Neo-phrìnich an ath-ghoirid"
39#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
39#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
40#: plugins/Utils/relativetimeformatter.cpp:157
40#: plugins/Utils/relativetimeformatter.cpp:157
41msgid "Yesterday %l:%M %p"
41msgid "Yesterday %l:%M %p"
42−msgstr ""
42+msgstr "An-dè aig %l:%M %p"
43
43
44#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
44#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
45#. This format string is used for showing, on a 12-hour clock, times that happened today.
45#. This format string is used for showing, on a 12-hour clock, times that happened today.
46#. en_US example: "%l:%M %p" --> "1:00 PM"
46#. en_US example: "%l:%M %p" --> "1:00 PM"
47#: plugins/Utils/relativetimeformatter.cpp:164
47#: plugins/Utils/relativetimeformatter.cpp:164
48msgid "%l:%M %p"
48msgid "%l:%M %p"
49−msgstr ""
49+msgstr "%l:%M %p"
50
50
51#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
51#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
52#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
52#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
@@ -54,7 +54,7 @@ msgstr ""
54#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
54#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
55#: plugins/Utils/relativetimeformatter.cpp:172
55#: plugins/Utils/relativetimeformatter.cpp:172
56msgid "Tomorrow %l:%M %p"
56msgid "Tomorrow %l:%M %p"
57−msgstr ""
57+msgstr "A-màireach aig %l:%M %p"
58
58
59#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
59#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
60#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
60#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
@@ -62,7 +62,7 @@ msgstr ""
62#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
62#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
63#: plugins/Utils/relativetimeformatter.cpp:181
63#: plugins/Utils/relativetimeformatter.cpp:181
64msgid "%a %l:%M %p"
64msgid "%a %l:%M %p"
65−msgstr ""
65+msgstr "%a %l:%M %p"
66
66
67#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
67#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
68#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
68#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
@@ -71,7 +71,7 @@ msgstr ""
71#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM"
71#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM"
72#: plugins/Utils/relativetimeformatter.cpp:190
72#: plugins/Utils/relativetimeformatter.cpp:190
73msgid "%a %d %b %l:%M %p"
73msgid "%a %d %b %l:%M %p"
74−msgstr ""
74+msgstr "%a %b %d %l:%M %p"
75
75
76#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
76#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
77#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
77#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
@@ -79,14 +79,14 @@ msgstr ""
79#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
79#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
80#: plugins/Utils/relativetimeformatter.cpp:201
80#: plugins/Utils/relativetimeformatter.cpp:201
81msgid "Yesterday %H:%M"
81msgid "Yesterday %H:%M"
82−msgstr ""
82+msgstr "An-dè aig %H:%M"
83
83
84#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
84#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
85#. This format string is used for showing, on a 24-hour clock, times that happened today.
85#. This format string is used for showing, on a 24-hour clock, times that happened today.
86#. en_US example: "%H:%M" --> "13:00"
86#. en_US example: "%H:%M" --> "13:00"
87#: plugins/Utils/relativetimeformatter.cpp:208
87#: plugins/Utils/relativetimeformatter.cpp:208
88msgid "%H:%M"
88msgid "%H:%M"
89−msgstr ""
89+msgstr "%H:%M"
90
90
91#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
91#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
92#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
92#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
@@ -94,7 +94,7 @@ msgstr ""
94#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
94#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
95#: plugins/Utils/relativetimeformatter.cpp:216
95#: plugins/Utils/relativetimeformatter.cpp:216
96msgid "Tomorrow %H:%M"
96msgid "Tomorrow %H:%M"
97−msgstr ""
97+msgstr "A-màireach aig %H:%M"
98
98
99#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
99#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
100#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
100#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
@@ -102,7 +102,7 @@ msgstr ""
102#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
102#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
103#: plugins/Utils/relativetimeformatter.cpp:225
103#: plugins/Utils/relativetimeformatter.cpp:225
104msgid "%a %H:%M"
104msgid "%a %H:%M"
105−msgstr ""
105+msgstr "%a %H:%M"
106
106
107#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
107#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
108#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
108#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
@@ -111,7 +111,7 @@ msgstr ""
111#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
111#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
112#: plugins/Utils/relativetimeformatter.cpp:234
112#: plugins/Utils/relativetimeformatter.cpp:234
113msgid "%a %d %b %H:%M"
113msgid "%a %d %b %H:%M"
114−msgstr ""
114+msgstr "%a %b %d %H:%M"
115
115
116#: qml/Components/DelayedLockscreen.qml:49
116#: qml/Components/DelayedLockscreen.qml:49
117msgid "Device Locked"
117msgid "Device Locked"
@@ -405,32 +405,38 @@ msgstr "Chan eil dad 'ga chluich"
405#, qt-format
405#, qt-format
406msgid "%1 hour"
406msgid "%1 hour"
407msgid_plural "%1 hours"
407msgid_plural "%1 hours"
408−msgstr[0] ""
408+msgstr[0] "%1 uair a thìde"
409+msgstr[1] "%1 uair a thìde"
409−msgstr[1] ""
410+msgstr[2] "%1 uairean a thìde"
411+msgstr[3] "%1 uair a thìde"
410
412
411#: qml/Panel/Indicators/MenuItemFactory.qml:772
413#: qml/Panel/Indicators/MenuItemFactory.qml:772
412#, qt-format
414#, qt-format
413msgid "%1 minute"
415msgid "%1 minute"
414msgid_plural "%1 minutes"
416msgid_plural "%1 minutes"
415−msgstr[0] ""
417+msgstr[0] "%1 mhionaid"
418+msgstr[1] "%1 mhionaid"
416−msgstr[1] ""
419+msgstr[2] "%1 mionaidean"
420+msgstr[3] "%1 mionaid"
417
421
418#: qml/Panel/Indicators/MenuItemFactory.qml:777
422#: qml/Panel/Indicators/MenuItemFactory.qml:777
419#, qt-format
423#, qt-format
420msgid "%1 second"
424msgid "%1 second"
421msgid_plural "%1 seconds"
425msgid_plural "%1 seconds"
422−msgstr[0] ""
426+msgstr[0] "%1 diog"
427+msgstr[1] "%1 dhiog"
423−msgstr[1] ""
428+msgstr[2] "%1 diogan"
429+msgstr[3] "%1 diog"
424
430
425#: qml/Panel/Indicators/MenuItemFactory.qml:780
431#: qml/Panel/Indicators/MenuItemFactory.qml:780
426msgid "0 seconds"
432msgid "0 seconds"
427−msgstr ""
433+msgstr "0 diog"
428
434
429#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
435#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
430#: qml/Panel/Indicators/MenuItemFactory.qml:782
436#: qml/Panel/Indicators/MenuItemFactory.qml:782
431#, qt-format
437#, qt-format
432msgid "%1 remaining"
438msgid "%1 remaining"
433−msgstr ""
439+msgstr "%1 air fhàgail"
434
440
435#: qml/Panel/Indicators/MenuItemFactory.qml:788
441#: qml/Panel/Indicators/MenuItemFactory.qml:788
436msgid "In queue…"
442msgid "In queue…"
@@ -492,7 +498,7 @@ msgstr "Thoir gnogag an-seo gus leantainn air adhart."
492
498
493#: qml/Tutorial/TutorialContent.qml:81
499#: qml/Tutorial/TutorialContent.qml:81
494msgid "Click here to finish."
500msgid "Click here to finish."
495−msgstr ""
501+msgstr "Briog an-seo gus crìoch a chur air."
496
502
497#: qml/Tutorial/TutorialLeftFinish.qml:24
503#: qml/Tutorial/TutorialLeftFinish.qml:24
498msgid "These are the shortcuts to favorite apps"
504msgid "These are the shortcuts to favorite apps"
@@ -613,7 +619,7 @@ msgstr "Còd-faire"
613#: qml/Wizard/Pages/30-passwd-type.qml:101
619#: qml/Wizard/Pages/30-passwd-type.qml:101
614msgctxt "Label: Description of security method"
620msgctxt "Label: Description of security method"
615msgid "4 digits only"
621msgid "4 digits only"
616−msgstr ""
622+msgstr "4 àireamhan a-mhàin"
617
623
618#: qml/Wizard/Pages/30-passwd-type.qml:103
624#: qml/Wizard/Pages/30-passwd-type.qml:103
619msgctxt "Label: Type of security method"
625msgctxt "Label: Type of security method"
po/pt.po+10−10
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-06-22 12:00+0200\n"
10"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-05-19 03:42+0000\n"
11+"PO-Revision-Date: 2015-07-16 12:23+0000\n"
12−"Last-Translator: António Miranda <Unknown>\n"
12+"Last-Translator: João Fernandes <Unknown>\n"
13"Language-Team: Portuguese <pt@li.org>\n"
13"Language-Team: Portuguese <pt@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
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-04 05:37+0000\n"
18+"X-Launchpad-Export-Date: 2015-07-17 05:37+0000\n"
19−"X-Generator: Launchpad (build 17597)\n"
19+"X-Generator: Launchpad (build 17628)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -37,7 +37,7 @@ msgstr "Remover atalho"
37#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
37#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
38#: plugins/Utils/relativetimeformatter.cpp:157
38#: plugins/Utils/relativetimeformatter.cpp:157
39msgid "Yesterday %l:%M %p"
39msgid "Yesterday %l:%M %p"
40−msgstr ""
40+msgstr "Ontem %l:%M %p"
41
41
42#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
43#. This format string is used for showing, on a 12-hour clock, times that happened today.
@@ -52,7 +52,7 @@ msgstr ""
52#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
52#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
53#: plugins/Utils/relativetimeformatter.cpp:172
53#: plugins/Utils/relativetimeformatter.cpp:172
54msgid "Tomorrow %l:%M %p"
54msgid "Tomorrow %l:%M %p"
55−msgstr ""
55+msgstr "Amanhã %l:%M %p"
56
56
57#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
58#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
@@ -77,7 +77,7 @@ msgstr ""
77#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
77#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
78#: plugins/Utils/relativetimeformatter.cpp:201
78#: plugins/Utils/relativetimeformatter.cpp:201
79msgid "Yesterday %H:%M"
79msgid "Yesterday %H:%M"
80−msgstr ""
80+msgstr "Ontem %H:%M"
81
81
82#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
83#. This format string is used for showing, on a 24-hour clock, times that happened today.
@@ -92,7 +92,7 @@ msgstr ""
92#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
92#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
93#: plugins/Utils/relativetimeformatter.cpp:216
93#: plugins/Utils/relativetimeformatter.cpp:216
94msgid "Tomorrow %H:%M"
94msgid "Tomorrow %H:%M"
95−msgstr ""
95+msgstr "Amanhã %H:%M"
96
96
97#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
98#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
@@ -399,8 +399,8 @@ msgstr "Nada está a ser tocado"
399#, qt-format
399#, qt-format
400msgid "%1 hour"
400msgid "%1 hour"
401msgid_plural "%1 hours"
401msgid_plural "%1 hours"
402−msgstr[0] ""
402+msgstr[0] "%1 hora"
403−msgstr[1] ""
403+msgstr[1] "%1 horas"
404
404
405#: qml/Panel/Indicators/MenuItemFactory.qml:772
405#: qml/Panel/Indicators/MenuItemFactory.qml:772
406#, qt-format
406#, qt-format
po/sw.po+86−80
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-06-22 12:00+0200\n"
10"POT-Creation-Date: 2015-06-22 12:00+0200\n"
11−"PO-Revision-Date: 2015-07-10 20:22+0000\n"
11+"PO-Revision-Date: 2015-07-17 20:02+0000\n"
12"Last-Translator: Valentine <mwangi_valentino@yahoo.com>\n"
12"Last-Translator: Valentine <mwangi_valentino@yahoo.com>\n"
13"Language-Team: Swahili <sw@li.org>\n"
13"Language-Team: Swahili <sw@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
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-11 05:33+0000\n"
18+"X-Launchpad-Export-Date: 2015-07-21 05:35+0000\n"
19−"X-Generator: Launchpad (build 17620)\n"
19+"X-Generator: Launchpad (build 17628)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -25,11 +25,11 @@ msgstr "Neno siri "
25#: plugins/Unity/Launcher/launcheritem.cpp:45
25#: plugins/Unity/Launcher/launcheritem.cpp:45
26#: plugins/Unity/Launcher/launcheritem.cpp:100
26#: plugins/Unity/Launcher/launcheritem.cpp:100
27msgid "Pin shortcut"
27msgid "Pin shortcut"
28−msgstr ""
28+msgstr "Njia ya mkato ya nambari ya siri"
29
29
30#: plugins/Unity/Launcher/launcheritem.cpp:100
30#: plugins/Unity/Launcher/launcheritem.cpp:100
31msgid "Unpin shortcut"
31msgid "Unpin shortcut"
32−msgstr ""
32+msgstr "Toa njia ya mkato ya nambari ya siri"
33
33
34#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
35#. This format string is used for showing, on a 12-hour clock, times that happen yesterday.
@@ -60,7 +60,7 @@ msgstr "Kesho %l:%M %p"
60#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
60#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
61#: plugins/Utils/relativetimeformatter.cpp:181
61#: plugins/Utils/relativetimeformatter.cpp:181
62msgid "%a %l:%M %p"
62msgid "%a %l:%M %p"
63−msgstr ""
63+msgstr "%a %l:%M %p"
64
64
65#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
66#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
@@ -118,10 +118,12 @@ msgstr "kifaa kimefungwa"
118#: qml/Components/DelayedLockscreen.qml:64
118#: qml/Components/DelayedLockscreen.qml:64
119msgid "You have been locked out due to too many failed passphrase attempts."
119msgid "You have been locked out due to too many failed passphrase attempts."
120msgstr ""
120msgstr ""
121+"Huwezi kulogi kwa sababu umenoa kubonyeza vizuri neno la siri mara nyingi."
121
122
122#: qml/Components/DelayedLockscreen.qml:65
123#: qml/Components/DelayedLockscreen.qml:65
123msgid "You have been locked out due to too many failed passcode attempts."
124msgid "You have been locked out due to too many failed passcode attempts."
124msgstr ""
125msgstr ""
126+"Huwezi kulogi kwa sababu umenoa kubonyeza vizuri neno la siri mara nyingi."
125
127
126#: qml/Components/DelayedLockscreen.qml:74
128#: qml/Components/DelayedLockscreen.qml:74
127#, qt-format
129#, qt-format
@@ -193,7 +195,7 @@ msgstr "Ndio"
193#: qml/Components/Dialogs.qml:152
195#: qml/Components/Dialogs.qml:152
194msgctxt "Title: Power off/Restart dialog"
196msgctxt "Title: Power off/Restart dialog"
195msgid "Power"
197msgid "Power"
196−msgstr ""
198+msgstr "Zima"
197
199
198#: qml/Components/Dialogs.qml:153
200#: qml/Components/Dialogs.qml:153
199msgid ""
201msgid ""
@@ -213,7 +215,7 @@ msgstr "Zima na uwashe"
213
215
214#: qml/Components/Lockscreen.qml:245
216#: qml/Components/Lockscreen.qml:245
215msgid "Return to Call"
217msgid "Return to Call"
216−msgstr ""
218+msgstr "Rudi nyuma upige simu"
217
219
218#: qml/Components/Lockscreen.qml:245
220#: qml/Components/Lockscreen.qml:245
219msgid "Emergency Call"
221msgid "Emergency Call"
@@ -269,11 +271,11 @@ msgstr "Kidogo..."
269
271
270#: qml/Dash/Previews/PreviewRatingInput.qml:84
272#: qml/Dash/Previews/PreviewRatingInput.qml:84
271msgid "Rate this"
273msgid "Rate this"
272−msgstr ""
274+msgstr "Pea kiwango"
273
275
274#: qml/Dash/Previews/PreviewRatingInput.qml:129
276#: qml/Dash/Previews/PreviewRatingInput.qml:129
275msgid "Add a review"
277msgid "Add a review"
276−msgstr ""
278+msgstr "Ongeza pitio"
277
279
278#: qml/Dash/Previews/PreviewRatingInput.qml:169
280#: qml/Dash/Previews/PreviewRatingInput.qml:169
279msgid "Send"
281msgid "Send"
@@ -281,11 +283,11 @@ msgstr "Tuma"
281
283
282#: qml/Dash/PullToRefreshScopeStyle.qml:55
284#: qml/Dash/PullToRefreshScopeStyle.qml:55
283msgid "Pull to refresh…"
285msgid "Pull to refresh…"
284−msgstr ""
286+msgstr "Vuta unawarishe"
285
287
286#: qml/Dash/PullToRefreshScopeStyle.qml:60
288#: qml/Dash/PullToRefreshScopeStyle.qml:60
287msgid "Release to refresh…"
289msgid "Release to refresh…"
288−msgstr ""
290+msgstr "Achilia unawarishe"
289
291
290#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
292#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
291msgid "Enable location data"
293msgid "Enable location data"
@@ -317,27 +319,31 @@ msgstr "Gusa ufungue"
317
319
318#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
320#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
319msgid "Sorry, incorrect passphrase."
321msgid "Sorry, incorrect passphrase."
320−msgstr ""
322+msgstr "Samahani, neno siri si hilo"
321
323
322#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
324#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
323msgid "Sorry, incorrect passcode."
325msgid "Sorry, incorrect passcode."
324−msgstr ""
326+msgstr "Samahani, neno siri si hilo"
325
327
326#: qml/Greeter/NarrowView.qml:55
328#: qml/Greeter/NarrowView.qml:55
327msgid "This will be your last attempt."
329msgid "This will be your last attempt."
328−msgstr ""
330+msgstr "Hii ndiyo nafasi ya mwisho"
329
331
330#: qml/Greeter/NarrowView.qml:57
332#: qml/Greeter/NarrowView.qml:57
331msgid ""
333msgid ""
332"If passphrase is entered incorrectly, your phone will conduct a factory "
334"If passphrase is entered incorrectly, your phone will conduct a factory "
333"reset and all personal data will be deleted."
335"reset and all personal data will be deleted."
334msgstr ""
336msgstr ""
337+"Kama utabonyeza neno la siri lisilofaa, simu yako itajiweka upya na data "
338+"yako yote itafutwa."
335
339
336#: qml/Greeter/NarrowView.qml:58
340#: qml/Greeter/NarrowView.qml:58
337msgid ""
341msgid ""
338"If passcode is entered incorrectly, your phone will conduct a factory reset "
342"If passcode is entered incorrectly, your phone will conduct a factory reset "
339"and all personal data will be deleted."
343"and all personal data will be deleted."
340msgstr ""
344msgstr ""
345+"Kama utabonyeza neno la siri lisilofaa, simu yako itajiweka upya na data "
346+"yako yote itafutwa."
341
347
342#: qml/Greeter/NarrowView.qml:115
348#: qml/Greeter/NarrowView.qml:115
343#, qt-format
349#, qt-format
@@ -350,75 +356,75 @@ msgstr "Bonyeza neno siri"
350
356
351#: qml/Greeter/NarrowView.qml:117
357#: qml/Greeter/NarrowView.qml:117
352msgid "Enter passcode"
358msgid "Enter passcode"
353−msgstr ""
359+msgstr "Bonyeza neno la siri"
354
360
355#: qml/Greeter/NarrowView.qml:118
361#: qml/Greeter/NarrowView.qml:118
356#, qt-format
362#, qt-format
357msgid "Sorry, incorrect %1"
363msgid "Sorry, incorrect %1"
358−msgstr ""
364+msgstr "Samahani, %1 halidhihirishi"
359
365
360#: qml/Greeter/NarrowView.qml:119
366#: qml/Greeter/NarrowView.qml:119
361msgid "Sorry, incorrect passphrase"
367msgid "Sorry, incorrect passphrase"
362−msgstr ""
368+msgstr "Samahani, neno la siri si hilo"
363
369
364#: qml/Greeter/NarrowView.qml:120
370#: qml/Greeter/NarrowView.qml:120
365msgid "Please re-enter"
371msgid "Please re-enter"
366−msgstr ""
372+msgstr "Tafadhali bonyeza tena"
367
373
368#: qml/Greeter/NarrowView.qml:121
374#: qml/Greeter/NarrowView.qml:121
369msgid "Sorry, incorrect passcode"
375msgid "Sorry, incorrect passcode"
370−msgstr ""
376+msgstr "Samahani, neno la siri si hilo"
371
377
372#: qml/Notifications/NotificationMenuItemFactory.qml:128
378#: qml/Notifications/NotificationMenuItemFactory.qml:128
373msgid "Show password"
379msgid "Show password"
374−msgstr ""
380+msgstr "Onyesha neno la siri"
375
381
376#: qml/Panel/ActiveCallHint.qml:79
382#: qml/Panel/ActiveCallHint.qml:79
377msgid "Tap to return to call..."
383msgid "Tap to return to call..."
378−msgstr ""
384+msgstr "Gusa urudi ili upige simu"
379
385
380#: qml/Panel/ActiveCallHint.qml:92
386#: qml/Panel/ActiveCallHint.qml:92
381msgid "Conference"
387msgid "Conference"
382−msgstr ""
388+msgstr "Mkutano"
383
389
384#: qml/Panel/Indicators/MenuItemFactory.qml:639
390#: qml/Panel/Indicators/MenuItemFactory.qml:639
385msgid "Nothing is playing"
391msgid "Nothing is playing"
386−msgstr ""
392+msgstr "Hakuna linalocheza"
387
393
388#: qml/Panel/Indicators/MenuItemFactory.qml:768
394#: qml/Panel/Indicators/MenuItemFactory.qml:768
389#, qt-format
395#, qt-format
390msgid "%1 hour"
396msgid "%1 hour"
391msgid_plural "%1 hours"
397msgid_plural "%1 hours"
392−msgstr[0] ""
398+msgstr[0] "Saa %1"
393−msgstr[1] ""
399+msgstr[1] "Mass %1"
394
400
395#: qml/Panel/Indicators/MenuItemFactory.qml:772
401#: qml/Panel/Indicators/MenuItemFactory.qml:772
396#, qt-format
402#, qt-format
397msgid "%1 minute"
403msgid "%1 minute"
398msgid_plural "%1 minutes"
404msgid_plural "%1 minutes"
399−msgstr[0] ""
405+msgstr[0] "Dakika %1"
400−msgstr[1] ""
406+msgstr[1] "Dakika %1"
401
407
402#: qml/Panel/Indicators/MenuItemFactory.qml:777
408#: qml/Panel/Indicators/MenuItemFactory.qml:777
403#, qt-format
409#, qt-format
404msgid "%1 second"
410msgid "%1 second"
405msgid_plural "%1 seconds"
411msgid_plural "%1 seconds"
406−msgstr[0] ""
412+msgstr[0] "Sekunde %1"
407−msgstr[1] ""
413+msgstr[1] "Sekunde %1"
408
414
409#: qml/Panel/Indicators/MenuItemFactory.qml:780
415#: qml/Panel/Indicators/MenuItemFactory.qml:780
410msgid "0 seconds"
416msgid "0 seconds"
411−msgstr ""
417+msgstr "sekunde 0"
412
418
413#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
419#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
414#: qml/Panel/Indicators/MenuItemFactory.qml:782
420#: qml/Panel/Indicators/MenuItemFactory.qml:782
415#, qt-format
421#, qt-format
416msgid "%1 remaining"
422msgid "%1 remaining"
417−msgstr ""
423+msgstr "%1 imebaki"
418
424
419#: qml/Panel/Indicators/MenuItemFactory.qml:788
425#: qml/Panel/Indicators/MenuItemFactory.qml:788
420msgid "In queue…"
426msgid "In queue…"
421−msgstr ""
427+msgstr "Kwenye foleni"
422
428
423#: qml/Panel/Indicators/MenuItemFactory.qml:792
429#: qml/Panel/Indicators/MenuItemFactory.qml:792
424msgid "Downloading"
430msgid "Downloading"
@@ -426,204 +432,204 @@ msgstr ""
426
432
427#: qml/Panel/Indicators/MenuItemFactory.qml:794
433#: qml/Panel/Indicators/MenuItemFactory.qml:794
428msgid "Paused, tap to resume"
434msgid "Paused, tap to resume"
429−msgstr ""
435+msgstr "Imepumzishwa, gusa uendelee"
430
436
431#: qml/Panel/Indicators/MenuItemFactory.qml:796
437#: qml/Panel/Indicators/MenuItemFactory.qml:796
432msgid "Canceled"
438msgid "Canceled"
433−msgstr ""
439+msgstr "Imeghairishwa"
434
440
435#: qml/Panel/Indicators/MenuItemFactory.qml:798
441#: qml/Panel/Indicators/MenuItemFactory.qml:798
436msgid "Finished"
442msgid "Finished"
437−msgstr ""
443+msgstr "Imemaliza"
438
444
439#: qml/Panel/Indicators/MenuItemFactory.qml:800
445#: qml/Panel/Indicators/MenuItemFactory.qml:800
440msgid "Failed, tap to retry"
446msgid "Failed, tap to retry"
441−msgstr ""
447+msgstr "Imefeli, gusa urudie"
442
448
443#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
449#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
444msgctxt "Label: Hint in message indicator line edit"
450msgctxt "Label: Hint in message indicator line edit"
445msgid "Reply"
451msgid "Reply"
446−msgstr ""
452+msgstr "Jibu"
447
453
448#: qml/Panel/Indicators/ModemInfoItem.qml:105
454#: qml/Panel/Indicators/ModemInfoItem.qml:105
449msgid "Unlock SIM"
455msgid "Unlock SIM"
450−msgstr ""
456+msgstr "Fungua SIM"
451
457
452#: qml/Panel/Indicators/RoamingIndication.qml:27
458#: qml/Panel/Indicators/RoamingIndication.qml:27
453msgid "Roaming"
459msgid "Roaming"
454−msgstr ""
460+msgstr "Uzururaji"
455
461
456#: qml/Tutorial/TutorialBottomFinish.qml:24
462#: qml/Tutorial/TutorialBottomFinish.qml:24
457msgid "This action does different things for different apps"
463msgid "This action does different things for different apps"
458−msgstr ""
464+msgstr "Hatua hii inafanya vitu tofauti kwenye programu tofauti"
459
465
460#: qml/Tutorial/TutorialBottomFinish.qml:25
466#: qml/Tutorial/TutorialBottomFinish.qml:25
461msgid "Tap here to finish."
467msgid "Tap here to finish."
462−msgstr ""
468+msgstr "Gusa hapa umalize"
463
469
464#: qml/Tutorial/TutorialBottom.qml:28
470#: qml/Tutorial/TutorialBottom.qml:28
465msgid "Open special menus"
471msgid "Open special menus"
466−msgstr ""
472+msgstr "Fungua orodha maalum"
467
473
468#: qml/Tutorial/TutorialBottom.qml:29
474#: qml/Tutorial/TutorialBottom.qml:29
469msgid "Swipe up from the bottom edge."
475msgid "Swipe up from the bottom edge."
470−msgstr ""
476+msgstr "Vuta juu kutoka chini"
471
477
472#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
478#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
473#: qml/Tutorial/TutorialRight.qml:144
479#: qml/Tutorial/TutorialRight.qml:144
474msgid "Tap here to continue."
480msgid "Tap here to continue."
475−msgstr ""
481+msgstr "Gusa hapa uendelee."
476
482
477#: qml/Tutorial/TutorialContent.qml:81
483#: qml/Tutorial/TutorialContent.qml:81
478msgid "Click here to finish."
484msgid "Click here to finish."
479−msgstr ""
485+msgstr "Click hapa umalize."
480
486
481#: qml/Tutorial/TutorialLeftFinish.qml:24
487#: qml/Tutorial/TutorialLeftFinish.qml:24
482msgid "These are the shortcuts to favorite apps"
488msgid "These are the shortcuts to favorite apps"
483−msgstr ""
489+msgstr "Hizi ni njia za mkato za programu ulizopenda"
484
490
485#: qml/Tutorial/TutorialLeft.qml:26
491#: qml/Tutorial/TutorialLeft.qml:26
486msgid "Open the launcher"
492msgid "Open the launcher"
487−msgstr ""
493+msgstr "Fungua launcher"
488
494
489#: qml/Tutorial/TutorialLeft.qml:27
495#: qml/Tutorial/TutorialLeft.qml:27
490msgid "Short swipe from the left edge."
496msgid "Short swipe from the left edge."
491−msgstr ""
497+msgstr "Vuta kidogo kutoka kushoto."
492
498
493#: qml/Tutorial/TutorialPage.qml:178
499#: qml/Tutorial/TutorialPage.qml:178
494msgid "You almost got it!"
500msgid "You almost got it!"
495−msgstr ""
501+msgstr "Karibu upate!"
496
502
497#: qml/Tutorial/TutorialPage.qml:195
503#: qml/Tutorial/TutorialPage.qml:195
498msgid "Try again."
504msgid "Try again."
499−msgstr ""
505+msgstr "Jaribu tena."
500
506
501#: qml/Tutorial/TutorialRight.qml:31
507#: qml/Tutorial/TutorialRight.qml:31
502msgid "To view open apps"
508msgid "To view open apps"
503−msgstr ""
509+msgstr "Kuona fungua programu"
504
510
505#: qml/Tutorial/TutorialRight.qml:32
511#: qml/Tutorial/TutorialRight.qml:32
506msgid "Long swipe from the right edge."
512msgid "Long swipe from the right edge."
507−msgstr ""
513+msgstr "Vuta kabisa kutoka kulia."
508
514
509#: qml/Tutorial/TutorialRight.qml:122
515#: qml/Tutorial/TutorialRight.qml:122
510msgid "View all your running tasks."
516msgid "View all your running tasks."
511−msgstr ""
517+msgstr "Ona kazi zote zinazoendelea."
512
518
513#: qml/Wizard/Page.qml:89
519#: qml/Wizard/Page.qml:89
514msgctxt "Button: Go back one page in the Wizard"
520msgctxt "Button: Go back one page in the Wizard"
515msgid "Back"
521msgid "Back"
516−msgstr ""
522+msgstr "Nyuma"
517
523
518#: qml/Wizard/Pages/here-terms.qml:27
524#: qml/Wizard/Pages/here-terms.qml:27
519msgid "Terms & Conditions"
525msgid "Terms & Conditions"
520−msgstr ""
526+msgstr "Suala na Masharti"
521
527
522#: qml/Wizard/Pages/passwd-confirm.qml:49
528#: qml/Wizard/Pages/passwd-confirm.qml:49
523msgid "Confirm passphrase"
529msgid "Confirm passphrase"
524−msgstr ""
530+msgstr "Thibitisha neno la siri"
525
531
526#: qml/Wizard/Pages/passwd-confirm.qml:50
532#: qml/Wizard/Pages/passwd-confirm.qml:50
527msgid "Confirm passcode"
533msgid "Confirm passcode"
528−msgstr ""
534+msgstr "Thibitisha neno la siri"
529
535
530#: qml/Wizard/Pages/passwd-confirm.qml:53
536#: qml/Wizard/Pages/passwd-confirm.qml:53
531#: qml/Wizard/Pages/passwd-confirm.qml:54
537#: qml/Wizard/Pages/passwd-confirm.qml:54
532msgid "Please try again."
538msgid "Please try again."
533−msgstr ""
539+msgstr "Jaribu tena."
534
540
535#: qml/Wizard/Pages/passwd-confirm.qml:82 qml/Wizard/Pages/passwd-set.qml:90
541#: 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
542#: 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
543#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
538#: qml/Wizard/Pages/60-reporting.qml:50
544#: qml/Wizard/Pages/60-reporting.qml:50
539msgid "Continue"
545msgid "Continue"
540−msgstr ""
546+msgstr "Endelea"
541
547
542#: qml/Wizard/Pages/passwd-set.qml:61
548#: qml/Wizard/Pages/passwd-set.qml:61
543msgid "Choose your passcode"
549msgid "Choose your passcode"
544−msgstr ""
550+msgstr "Chagua neno lako la siri"
545
551
546#: qml/Wizard/Pages/passwd-set.qml:68
552#: qml/Wizard/Pages/passwd-set.qml:68
547msgid "Passphrase must be 4 characters long"
553msgid "Passphrase must be 4 characters long"
548−msgstr ""
554+msgstr "Neno la siri ni lazima liwe na urefu wa herufi 4"
549
555
550#: qml/Wizard/Pages/10-welcome.qml:27
556#: qml/Wizard/Pages/10-welcome.qml:27
551msgid "Hi!"
557msgid "Hi!"
552−msgstr ""
558+msgstr "Jambo!"
553
559
554#: qml/Wizard/Pages/10-welcome.qml:44
560#: qml/Wizard/Pages/10-welcome.qml:44
555msgid "Welcome to your Ubuntu phone."
561msgid "Welcome to your Ubuntu phone."
556−msgstr ""
562+msgstr "Karibu kwenye simu yako ya Ubuntu."
557
563
558#: qml/Wizard/Pages/10-welcome.qml:52
564#: qml/Wizard/Pages/10-welcome.qml:52
559msgid "Let’s get started."
565msgid "Let’s get started."
560−msgstr ""
566+msgstr "Tuanze pamoja."
561
567
562#: qml/Wizard/Pages/20-sim.qml:25
568#: qml/Wizard/Pages/20-sim.qml:25
563msgid "Add a SIM card and restart your device"
569msgid "Add a SIM card and restart your device"
564−msgstr ""
570+msgstr "Ongeza kadi ya SIM halafu uanzishe upya kifaa chako"
565
571
566#: qml/Wizard/Pages/20-sim.qml:59
572#: qml/Wizard/Pages/20-sim.qml:59
567msgid "Without it, you won’t be able to make calls or use text messaging."
573msgid "Without it, you won’t be able to make calls or use text messaging."
568−msgstr ""
574+msgstr "Bila hiyo, hutaweza kupiga simu ama kutuma teksti."
569
575
570#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
576#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
571msgid "Skip"
577msgid "Skip"
572−msgstr ""
578+msgstr "Ruka"
573
579
574#: qml/Wizard/Pages/30-passwd-type.qml:39
580#: qml/Wizard/Pages/30-passwd-type.qml:39
575msgid "Lock security"
581msgid "Lock security"
576−msgstr ""
582+msgstr "Funga usalama"
577
583
578#: qml/Wizard/Pages/30-passwd-type.qml:74
584#: qml/Wizard/Pages/30-passwd-type.qml:74
579msgid "Please select how you’d like to unlock your phone."
585msgid "Please select how you’d like to unlock your phone."
580−msgstr ""
586+msgstr "Tafadhali chagua vile ungependa kufunga simu."
581
587
582#: qml/Wizard/Pages/30-passwd-type.qml:97
588#: qml/Wizard/Pages/30-passwd-type.qml:97
583msgctxt "Label: Type of security method"
589msgctxt "Label: Type of security method"
584msgid "Swipe"
590msgid "Swipe"
585−msgstr ""
591+msgstr "Vuta"
586
592
587#: qml/Wizard/Pages/30-passwd-type.qml:98
593#: qml/Wizard/Pages/30-passwd-type.qml:98
588msgctxt "Label: Description of security method"
594msgctxt "Label: Description of security method"
589msgid "No security"
595msgid "No security"
590−msgstr ""
596+msgstr "Hakuna usalama"
591
597
592#: qml/Wizard/Pages/30-passwd-type.qml:100
598#: qml/Wizard/Pages/30-passwd-type.qml:100
593msgctxt "Label: Type of security method"
599msgctxt "Label: Type of security method"
594msgid "Passcode"
600msgid "Passcode"
595−msgstr ""
601+msgstr "Neno la siri"
596
602
597#: qml/Wizard/Pages/30-passwd-type.qml:101
603#: qml/Wizard/Pages/30-passwd-type.qml:101
598msgctxt "Label: Description of security method"
604msgctxt "Label: Description of security method"
599msgid "4 digits only"
605msgid "4 digits only"
600−msgstr ""
606+msgstr "Herufi nne peke yake"
601
607
602#: qml/Wizard/Pages/30-passwd-type.qml:103
608#: qml/Wizard/Pages/30-passwd-type.qml:103
603msgctxt "Label: Type of security method"
609msgctxt "Label: Type of security method"
604msgid "Passphrase"
610msgid "Passphrase"
605−msgstr ""
611+msgstr "Kaulisiri"
606
612
607#: qml/Wizard/Pages/30-passwd-type.qml:104
613#: qml/Wizard/Pages/30-passwd-type.qml:104
608msgctxt "Label: Description of security method"
614msgctxt "Label: Description of security method"
609msgid "Numbers and letters"
615msgid "Numbers and letters"
610−msgstr ""
616+msgstr "Nambari na herufi"
611
617
612#: qml/Wizard/Pages/40-wifi.qml:29
618#: qml/Wizard/Pages/40-wifi.qml:29
613msgid "Connect to Wi‑Fi"
619msgid "Connect to Wi‑Fi"
614−msgstr ""
620+msgstr "Tumia Wi-fi"
615
621
616#: qml/Wizard/Pages/40-wifi.qml:168
622#: qml/Wizard/Pages/40-wifi.qml:168
617msgid "Available networks…"
623msgid "Available networks…"
618−msgstr ""
624+msgstr "Mitandao inayopatikana"
619
625
620#: qml/Wizard/Pages/40-wifi.qml:169
626#: qml/Wizard/Pages/40-wifi.qml:169
621msgid "No available networks."
627msgid "No available networks."
622−msgstr ""
628+msgstr "Hakuna mitandao inayopatikana"
623
629
624#: qml/Wizard/Pages/50-location.qml:27
630#: qml/Wizard/Pages/50-location.qml:27
625msgid "Location"
631msgid "Location"
626−msgstr ""
632+msgstr "Mahali"
627
633
628#: qml/Wizard/Pages/50-location.qml:62
634#: qml/Wizard/Pages/50-location.qml:62
629msgid "Let the phone detect your location:"
635msgid "Let the phone detect your location:"
qml/Dash/Previews/PreviewComment.qmlnew file+83
@@ -0,0 +1,83 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 1.2
19+
20+/*! \brief Preview widget for comments.
21+
22+ This widget shows an (optional) avatar contained in widgetData["source"]
23+ along with a label that comes from widgetData["author"],
24+ a (optional) subtitle from widgetData["subtitle"] and the comment widgetData["comment"].
25+*/
26+
27+PreviewWidget {
28+ id: root
29+ implicitHeight: Math.max(avatar.height, column.implicitHeight)
30+
31+ UbuntuShape {
32+ id: avatar
33+ objectName: "avatar"
34+ anchors {
35+ left: parent.left
36+ top: parent.top
37+ }
38+ width: units.gu(6)
39+ height: width
40+ source: Image {
41+ source: widgetData["source"]
42+ }
43+ radius: "medium"
44+ opacity: source.status === Image.Ready ? 1 : 0
45+ visible: widgetData["source"] !== ""
46+ }
47+
48+ Column {
49+ id: column
50+ objectName: "column"
51+ anchors {
52+ left: avatar.visible ? avatar.right : parent.left
53+ right: parent.right
54+ top: parent.top
55+ topMargin: units.gu(0.5)
56+ leftMargin: avatar.visible ? units.gu(1) : 0
57+ }
58+ spacing: units.gu(0.24)
59+
60+ Label {
61+ width: parent.width
62+ text: widgetData["author"] || ""
63+ fontSize: "small"
64+ maximumLineCount: 1
65+ elide: Text.ElideRight
66+ }
67+ Label {
68+ objectName: "subtitle"
69+ width: parent.width
70+ visible: text !== ""
71+ text: widgetData["subtitle"] || ""
72+ fontSize: "xx-small"
73+ maximumLineCount: 1
74+ elide: Text.ElideRight
75+ }
76+ Label {
77+ width: parent.width
78+ text: widgetData["comment"] || ""
79+ fontSize: "small"
80+ wrapMode: Text.Wrap
81+ }
82+ }
83+}
qml/Dash/Previews/PreviewCommentInput.qmlnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 1.1
19+import "../../Components"
20+
21+/*! \brief Preview widget for commenting.
22+
23+ The widget can show a field to enter a comment.
24+ It is possible to customise the submit button's label by setting widgetData["submit-label"]
25+ The successeful submit emits triggered(widgetId, "commented", data),
26+ with data being {"comment": comment}.
27+*/
28+
29+PreviewWidget {
30+ id: root
31+ implicitHeight: Math.max(commentTextArea.implicitHeight, submitButton.implicitHeight)
32+
33+ function submit() {
34+ var data = { "comment": commentTextArea.text };
35+ triggered(root.widgetId, "commented", data);
36+ }
37+
38+ TextArea {
39+ id: commentTextArea
40+ objectName: "commentTextArea"
41+ anchors {
42+ top: parent.top
43+ left: parent.left
44+ right: submitButton.left
45+ rightMargin: units.gu(1)
46+ }
47+ autoSize: true
48+ }
49+
50+ Button {
51+ id: submitButton
52+ objectName: "submitButton"
53+
54+ readonly property bool readyToSubmit: commentTextArea.text.trim().length > 0
55+
56+ anchors {
57+ top: parent.top
58+ right: parent.right
59+ }
60+ color: readyToSubmit ? Theme.palette.selected.base : Theme.palette.normal.base
61+ text: widgetData["submit-label"] || i18n.tr("Send")
62+ onClicked: {
63+ if (readyToSubmit) root.submit()
64+ }
65+ }
66+}
qml/Dash/Previews/PreviewWidgetFactory.qml+2
@@ -50,6 +50,8 @@ Loader {
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/LauncherDelegate.qml+100−15
@@ -15,7 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19
19
20Item {
20Item {
21 id: root
21 id: root
@@ -28,6 +28,8 @@ Item {
28 property bool itemFocused: false
28 property bool itemFocused: false
29 property real maxAngle: 0
29 property real maxAngle: 0
30 property bool inverted: false
30 property bool inverted: false
31+ property bool alerting: false
32+ readonly property alias wiggling: wiggleAnim.running
31
33
32 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
34 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
33 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
35 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
@@ -40,6 +42,82 @@ Item {
40 property real offset: 0
42 property real offset: 0
41 property real itemOpacity: 1
43 property real itemOpacity: 1
42 property real brightness: 0
44 property real brightness: 0
45+ property double maxWiggleAngle: 5.0
46+
47+ QtObject {
48+ id: priv
49+
50+ readonly property int wiggleDuration: UbuntuAnimation.SnapDuration
51+ property real wiggleAngle: 0
52+ }
53+
54+ SequentialAnimation {
55+ id: wiggleAnim
56+
57+ running: alerting
58+ loops: 1
59+ alwaysRunToEnd: true
60+
61+ NumberAnimation {
62+ target: priv
63+ property: "wiggleAngle"
64+ from: 0
65+ to: maxWiggleAngle
66+ duration: priv.wiggleDuration
67+ easing.type: Easing.InQuad
68+ }
69+
70+ NumberAnimation {
71+ target: priv
72+ property: "wiggleAngle"
73+ from: maxWiggleAngle
74+ to: -maxWiggleAngle
75+ duration: priv.wiggleDuration
76+ easing.type: Easing.InOutQuad
77+ }
78+
79+ NumberAnimation {
80+ target: priv
81+ property: "wiggleAngle"
82+ from: -maxWiggleAngle
83+ to: maxWiggleAngle
84+ duration: priv.wiggleDuration
85+ easing.type: Easing.InOutQuad
86+ }
87+
88+ NumberAnimation {
89+ target: priv
90+ property: "wiggleAngle"
91+ from: maxWiggleAngle
92+ to: -maxWiggleAngle
93+ duration: priv.wiggleDuration
94+ easing.type: Easing.InOutQuad
95+ }
96+
97+ NumberAnimation {
98+ target: priv
99+ property: "wiggleAngle"
100+ from: -maxWiggleAngle
101+ to: maxWiggleAngle
102+ duration: priv.wiggleDuration
103+ easing.type: Easing.InOutQuad
104+ }
105+
106+ NumberAnimation {
107+ target: priv
108+ property: "wiggleAngle"
109+ from: maxWiggleAngle
110+ to: 0
111+ duration: priv.wiggleDuration
112+ easing.type: Easing.OutQuad
113+ }
114+
115+ UbuntuNumberAnimation {
116+ target: root
117+ property: "alerting"
118+ to: 0
119+ }
120+ }
43
121
44 Item {
122 Item {
45 id: iconItem
123 id: iconItem
@@ -76,7 +154,7 @@ Item {
76 objectName: "countEmblem"
154 objectName: "countEmblem"
77 anchors {
155 anchors {
78 right: parent.right
156 right: parent.right
79− top: parent.top
157+ bottom: parent.bottom
80 margins: units.dp(3)
158 margins: units.dp(3)
81 }
159 }
82 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
160 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
@@ -101,23 +179,21 @@ Item {
101 }
179 }
102 }
180 }
103
181
104− BorderImage {
182+ UbuntuShape {
105 id: progressOverlay
183 id: progressOverlay
106 objectName: "progressOverlay"
184 objectName: "progressOverlay"
185+
107 anchors {
186 anchors {
108 left: iconItem.left
187 left: iconItem.left
109 right: iconItem.right
188 right: iconItem.right
110− bottom: iconItem.bottom
189+ verticalCenter: parent.verticalCenter
111− leftMargin: units.gu(1)
190+ leftMargin: units.gu(1.5)
112− rightMargin: units.gu(1)
191+ rightMargin: units.gu(1.5)
113− bottomMargin: units.gu(1)
114 }
192 }
115− height: units.gu(1.5)
193+ height: units.gu(1)
116 visible: root.progress > -1
194 visible: root.progress > -1
117− source: "graphics/progressbar-trough.sci"
195+ color: UbuntuColors.darkGrey
118−
196+ borderSource: "none"
119− // For fill calculation we need to remove the 2 units of border defined in .sci file
120− property int adjustedWidth: width - units.gu(2)
121
197
122 Item {
198 Item {
123 anchors {
199 anchors {
@@ -125,17 +201,18 @@ Item {
125 top: parent.top
201 top: parent.top
126 bottom: parent.bottom
202 bottom: parent.bottom
127 }
203 }
128− width: Math.min(100, root.progress) / 100 * parent.adjustedWidth + units.gu(1)
204+ width: Math.min(100, root.progress) / 100 * parent.width
129 clip: true
205 clip: true
130
206
131− BorderImage {
207+ UbuntuShape {
132 anchors {
208 anchors {
133 left: parent.left
209 left: parent.left
134 top: parent.top
210 top: parent.top
135 bottom: parent.bottom
211 bottom: parent.bottom
136 }
212 }
213+ color: "white"
214+ borderSource: "none"
137 width: progressOverlay.width
215 width: progressOverlay.width
138− source: "graphics/progressbar-fill.sci"
139 }
216 }
140 }
217 }
141 }
218 }
@@ -180,6 +257,14 @@ Item {
180 }
257 }
181
258
182 transform: [
259 transform: [
260+ // The rotation about the icon's center/z-axis for the wiggle
261+ // needs to happen here too, because there's no other way to
262+ // align the wiggle with the icon-folding otherwise
263+ Rotation {
264+ axis { x: 0; y: 0; z: 1 }
265+ origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
266+ angle: priv.wiggleAngle
267+ },
183 // Rotating 3 times at top/bottom because that increases the perspective.
268 // Rotating 3 times at top/bottom because that increases the perspective.
184 // This is a hack, but as QML does not support real 3D coordinates
269 // This is a hack, but as QML does not support real 3D coordinates
185 // getting a higher perspective can only be done by a hack. This is the most
270 // getting a higher perspective can only be done by a hack. This is the most
qml/Launcher/LauncherPanel.qml+93−13
@@ -15,7 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.3
17import QtQuick 2.3
18−import Ubuntu.Components 1.1
18+import Ubuntu.Components 1.2
19import Ubuntu.Components.ListItems 1.0 as ListItems
19import Ubuntu.Components.ListItems 1.0 as ListItems
20import Unity.Launcher 0.1
20import Unity.Launcher 0.1
21import Ubuntu.Components.Popups 0.1
21import Ubuntu.Components.Popups 0.1
@@ -86,6 +86,7 @@ Rectangle {
86 height: parent.height - dashItem.height - parent.spacing*2
86 height: parent.height - dashItem.height - parent.spacing*2
87
87
88 Item {
88 Item {
89+ id: launcherListViewItem
89 anchors.fill: parent
90 anchors.fill: parent
90 clip: true
91 clip: true
91
92
@@ -109,12 +110,21 @@ Rectangle {
109 preferredHighlightBegin: (height - itemHeight) / 2
110 preferredHighlightBegin: (height - itemHeight) / 2
110 preferredHighlightEnd: (height + itemHeight) / 2
111 preferredHighlightEnd: (height + itemHeight) / 2
111
112
113+ // for the single peeking icon, when alert-state is set on delegate
114+ property int peekingIndex: -1
115+
112 // The size of the area the ListView is extended to make sure items are not
116 // The size of the area the ListView is extended to make sure items are not
113 // destroyed when dragging them outside the list. This needs to be at least
117 // destroyed when dragging them outside the list. This needs to be at least
114 // itemHeight to prevent folded items from disappearing and DragArea limits
118 // itemHeight to prevent folded items from disappearing and DragArea limits
115 // need to be smaller than this size to avoid breakage.
119 // need to be smaller than this size to avoid breakage.
116 property int extensionSize: 0
120 property int extensionSize: 0
117
121
122+ // These properties are needed to facilitate implicitly unfolding the
123+ // launcher to the peeking icon, if it is not already fully flat
124+ // and in view for the user
125+ property real moveToIndexYFrom
126+ property real moveToIndexYTo
127+
118 // Setting extensionSize after the list has been populated because it has
128 // Setting extensionSize after the list has been populated because it has
119 // the potential to mess up with the intial positioning in combination
129 // the potential to mess up with the intial positioning in combination
120 // with snapping to the center of the list. This catches all the cases
130 // with snapping to the center of the list. This catches all the cases
@@ -128,6 +138,14 @@ Rectangle {
128 flick(0, clickFlickSpeed)
138 flick(0, clickFlickSpeed)
129 }
139 }
130
140
141+ UbuntuNumberAnimation {
142+ id: moveToIndexAnimation
143+ target: launcherListView
144+ property: "contentY"
145+ from: launcherListView.moveToIndexYFrom
146+ to: launcherListView.moveToIndexYTo
147+ }
148+
131 // The height of the area where icons start getting folded
149 // The height of the area where icons start getting folded
132 property int foldingStartHeight: units.gu(6.5)
150 property int foldingStartHeight: units.gu(6.5)
133 // The height of the area where the items reach the final folding angle
151 // The height of the area where the items reach the final folding angle
@@ -181,10 +199,63 @@ Rectangle {
181 itemRunning: model.running
199 itemRunning: model.running
182 itemFocused: model.focused
200 itemFocused: model.focused
183 inverted: root.inverted
201 inverted: root.inverted
202+ alerting: model.alerting
184 z: -Math.abs(offset)
203 z: -Math.abs(offset)
185 maxAngle: 55
204 maxAngle: 55
186 property bool dragging: false
205 property bool dragging: false
187
206
207+ SequentialAnimation {
208+ id: peekingAnimation
209+
210+ // revealing
211+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 1 : 0 }
212+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 0 }
213+
214+ UbuntuNumberAnimation {
215+ target: launcherDelegate
216+ alwaysRunToEnd: true
217+ loops: 1
218+ properties: "x"
219+ to: (units.gu(.5) + launcherListView.width * .5) * (root.inverted ? -1 : 1)
220+ duration: UbuntuAnimation.BriskDuration
221+ }
222+
223+ // hiding
224+ UbuntuNumberAnimation {
225+ target: launcherDelegate
226+ alwaysRunToEnd: true
227+ loops: 1
228+ properties: "x"
229+ to: 0
230+ duration: UbuntuAnimation.BriskDuration
231+ }
232+
233+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 1 }
234+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 0 : 1 }
235+ }
236+
237+ onAlertingChanged: {
238+ if(alerting) {
239+ if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
240+ launcherListView.moveToIndexYFrom = launcherListView.contentY
241+ launcherListView.positionViewAtIndex(index, ListView.Center)
242+ launcherListView.moveToIndexYTo = launcherListView.contentY
243+ moveToIndexAnimation.start()
244+ if (!dragging && launcher.state !== "visible") {
245+ peekingAnimation.start()
246+ }
247+ }
248+
249+ if (launcherListView.peekingIndex === -1) {
250+ launcherListView.peekingIndex = index
251+ }
252+ } else {
253+ if (launcherListView.peekingIndex === index) {
254+ launcherListView.peekingIndex = -1
255+ }
256+ }
257+ }
258+
188 ThinDivider {
259 ThinDivider {
189 id: dropIndicator
260 id: dropIndicator
190 objectName: "dropIndicator"
261 objectName: "dropIndicator"
@@ -287,6 +358,7 @@ Rectangle {
287 MouseArea {
358 MouseArea {
288 id: dndArea
359 id: dndArea
289 objectName: "dndArea"
360 objectName: "dndArea"
361+ acceptedButtons: Qt.LeftButton | Qt.RightButton
290 anchors {
362 anchors {
291 fill: parent
363 fill: parent
292 topMargin: launcherListView.topMargin
364 topMargin: launcherListView.topMargin
@@ -298,7 +370,7 @@ Rectangle {
298 property int draggedIndex: -1
370 property int draggedIndex: -1
299 property var selectedItem
371 property var selectedItem
300 property bool preDragging: false
372 property bool preDragging: false
301− property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging
373+ property bool dragging: !!selectedItem && selectedItem.dragging
302 property bool postDragging: false
374 property bool postDragging: false
303 property int startX
375 property int startX
304 property int startY
376 property int startY
@@ -316,6 +388,15 @@ Rectangle {
316 return;
388 return;
317 }
389 }
318
390
391+ if (mouse.button & Qt.RightButton) { // context menu
392+ // Opening QuickList
393+ quickList.item = clickedItem;
394+ quickList.model = launcherListView.model.get(index).quickList;
395+ quickList.appId = launcherListView.model.get(index).appId;
396+ quickList.state = "open";
397+ return
398+ }
399+
319 // First/last item do the scrolling at more than 12 degrees
400 // First/last item do the scrolling at more than 12 degrees
320 if (index == 0 || index == launcherListView.count - 1) {
401 if (index == 0 || index == launcherListView.count - 1) {
321 if (clickedItem.angle > 12) {
402 if (clickedItem.angle > 12) {
@@ -509,7 +590,7 @@ Rectangle {
509 id: quickListShape
590 id: quickListShape
510 objectName: "quickListShape"
591 objectName: "quickListShape"
511 anchors.fill: quickList
592 anchors.fill: quickList
512− opacity: quickList.state === "open" ? 0.96 : 0
593+ opacity: quickList.state === "open" ? 0.8 : 0
513 visible: opacity > 0
594 visible: opacity > 0
514 rotation: root.rotation
595 rotation: root.rotation
515
596
@@ -521,15 +602,15 @@ Rectangle {
521
602
522 Image {
603 Image {
523 anchors {
604 anchors {
524− left: parent.left
605+ right: parent.left
525− leftMargin: (quickList.item.width - units.gu(1)) / 2 - width / 2
606+ rightMargin: -units.dp(4)
526 verticalCenter: parent.verticalCenter
607 verticalCenter: parent.verticalCenter
527− verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
608+ verticalCenterOffset: -quickList.offset * (root.inverted ? -1 : 1)
528 }
609 }
529 height: units.gu(1)
610 height: units.gu(1)
530 width: units.gu(2)
611 width: units.gu(2)
531 source: "graphics/quicklist_tooltip.png"
612 source: "graphics/quicklist_tooltip.png"
532− rotation: (quickList.offset > 0 ? 0 : 180) + (root.inverted ? 0 : 180)
613+ rotation: 90
533 }
614 }
534
615
535 InverseMouseArea {
616 InverseMouseArea {
@@ -552,11 +633,11 @@ Rectangle {
552 height: quickListColumn.height
633 height: quickListColumn.height
553 visible: quickListShape.visible
634 visible: quickListShape.visible
554 anchors {
635 anchors {
555− left: root.inverted ? undefined : parent.left
636+ left: root.inverted ? undefined : parent.right
556− right: root.inverted ? parent.right : undefined
637+ right: root.inverted ? parent.left : undefined
557 margins: units.gu(1)
638 margins: units.gu(1)
558 }
639 }
559− y: itemCenter + offset
640+ y: itemCenter - (height / 2) + offset
560 rotation: root.rotation
641 rotation: root.rotation
561
642
562 property var model
643 property var model
@@ -565,9 +646,8 @@ Rectangle {
565
646
566 // internal
647 // internal
567 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
648 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
568− property int offset: itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
649+ property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
569− -(item.height/2) - height - units.gu(.5) :
570− (item.height/2) + units.gu(.5)
650+ itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
571
651
572 Column {
652 Column {
573 id: quickListColumn
653 id: quickListColumn
qml/Launcher/graphics/progressbar-fill@30.pngdeleted

Binary file not shown.

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

Binary file not shown.

qml/Launcher/graphics/progressbar-trough@30.scideleted−5
@@ -1,5 +0,0 @@
1−border.left: 36
2−border.top: 16
3−border.bottom: 26
4−border.right: 36
5−source: progressbar-trough@30.png
qml/Notifications/Notifications.qml+1−1
@@ -33,7 +33,7 @@ ListView {
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/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
2
2
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+22
@@ -37,9 +37,13 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
37 m_count(0),
37 m_count(0),
38 m_countVisible(false),
38 m_countVisible(false),
39 m_focused(false),
39 m_focused(false),
40+ m_alerting(false),
40 m_quickList(new MockQuickListModel(this))
41 m_quickList(new MockQuickListModel(this))
41{
42{
43+}
42
44
45+MockLauncherItem::~MockLauncherItem()
46+{
43}
47}
44
48
45QString MockLauncherItem::appId() const
49QString MockLauncherItem::appId() const
@@ -129,6 +133,9 @@ void MockLauncherItem::setCount(int count)
129 {
133 {
130 m_count = count;
134 m_count = count;
131 Q_EMIT countChanged(count);
135 Q_EMIT countChanged(count);
136+ if (m_countVisible) {
137+ setAlerting(true);
138+ }
132 }
139 }
133}
140}
134
141
@@ -142,6 +149,9 @@ void MockLauncherItem::setCountVisible(bool countVisible)
142 if (m_countVisible != countVisible) {
149 if (m_countVisible != countVisible) {
143 m_countVisible = countVisible;
150 m_countVisible = countVisible;
144 Q_EMIT countVisibleChanged(countVisible);
151 Q_EMIT countVisibleChanged(countVisible);
152+ if (countVisible) {
153+ setAlerting(true);
154+ }
145 }
155 }
146}
156}
147
157
@@ -159,6 +169,18 @@ void MockLauncherItem::setFocused(bool focused)
159 }
169 }
160}
170}
161
171
172+bool MockLauncherItem::alerting() const
173+{
174+ return m_alerting;
175+}
176+
177+void MockLauncherItem::setAlerting(bool alerting)
178+{
179+ if (m_alerting != alerting) {
180+ m_alerting = alerting;
181+ Q_EMIT alertingChanged(alerting);
182+ }
183+}
162
184
163unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
185unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
164{
186{
tests/mocks/Unity/Launcher/MockLauncherItem.h+4
@@ -31,6 +31,7 @@ class MockLauncherItem: public LauncherItemInterface
31 Q_OBJECT
31 Q_OBJECT
32public:
32public:
33 MockLauncherItem(const QString &appId, const QString& desktopFile, const QString& name, const QString& icon, QObject* parent = 0);
33 MockLauncherItem(const QString &appId, const QString& desktopFile, const QString& name, const QString& icon, QObject* parent = 0);
34+ ~MockLauncherItem();
34
35
35 QString appId() const override;
36 QString appId() const override;
36 QString desktopFile() const;
37 QString desktopFile() const;
@@ -44,6 +45,7 @@ public:
44 int count() const override;
45 int count() const override;
45 bool countVisible() const override;
46 bool countVisible() const override;
46 bool focused() const override;
47 bool focused() const override;
48+ bool alerting() const override;
47
49
48 unity::shell::launcher::QuickListModelInterface *quickList() const override;
50 unity::shell::launcher::QuickListModelInterface *quickList() const override;
49
51
@@ -55,6 +57,7 @@ private:
55 void setCount(int count);
57 void setCount(int count);
56 void setCountVisible(bool countVisible);
58 void setCountVisible(bool countVisible);
57 void setFocused(bool focused);
59 void setFocused(bool focused);
60+ void setAlerting(bool alerting);
58
61
59 QString m_appId;
62 QString m_appId;
60 QString m_desktopFile;
63 QString m_desktopFile;
@@ -67,6 +70,7 @@ private:
67 int m_count;
70 int m_count;
68 bool m_countVisible;
71 bool m_countVisible;
69 bool m_focused;
72 bool m_focused;
73+ bool m_alerting;
70 MockQuickListModel *m_quickList;
74 MockQuickListModel *m_quickList;
71
75
72 friend class MockLauncherModel;
76 friend class MockLauncherModel;
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+54−3
@@ -26,7 +26,6 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
26 item->setProgress(0);
26 item->setProgress(0);
27 item->setPinned(true);
27 item->setPinned(true);
28 item->setFocused(true);
28 item->setFocused(true);
29− item->setRunning(true);
30 m_list.append(item);
29 m_list.append(item);
31 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
30 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
32 item->setProgress(10);
31 item->setProgress(10);
@@ -35,6 +34,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
35 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
34 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
36 item->setProgress(50);
35 item->setProgress(50);
37 item->setCountVisible(true);
36 item->setCountVisible(true);
37+ item->setAlerting(false);
38 m_list.append(item);
38 m_list.append(item);
39 item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
39 item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
40 m_list.append(item);
40 m_list.append(item);
@@ -44,26 +44,31 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
44 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
44 item = new MockLauncherItem("webbrowser-app", "/usr/share/applications/webbrowser-app.desktop", "Browser", "browser", this);
45 item->setCount(1);
45 item->setCount(1);
46 item->setCountVisible(true);
46 item->setCountVisible(true);
47+ item->setRunning(true);
48+ item->setAlerting(false);
47 m_list.append(item);
49 m_list.append(item);
48 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
50 item = new MockLauncherItem("twitter-webapp", "/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter", this);
49 item->setCount(12);
51 item->setCount(12);
50 item->setCountVisible(true);
52 item->setCountVisible(true);
53+ item->setAlerting(false);
51 item->setPinned(true);
54 item->setPinned(true);
52 m_list.append(item);
55 m_list.append(item);
53 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
56 item = new MockLauncherItem("gmail-webapp", "/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail", this);
54 item->setCount(123);
57 item->setCount(123);
55 item->setCountVisible(true);
58 item->setCountVisible(true);
59+ item->setAlerting(false);
56 m_list.append(item);
60 m_list.append(item);
57 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
61 item = new MockLauncherItem("ubuntu-weather-app", "/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather", this);
58 item->setCount(1234567890);
62 item->setCount(1234567890);
59 item->setCountVisible(true);
63 item->setCountVisible(true);
64+ item->setAlerting(false);
60 item->setPinned(true);
65 item->setPinned(true);
61− item->setRunning(true);
62 m_list.append(item);
66 m_list.append(item);
63 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
67 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
64 item->setProgress(50);
68 item->setProgress(50);
65 item->setCount(5);
69 item->setCount(5);
66 item->setCountVisible(true);
70 item->setCountVisible(true);
71+ item->setAlerting(false);
67 item->setFocused(true);
72 item->setFocused(true);
68 item->setPinned(true);
73 item->setPinned(true);
69 m_list.append(item);
74 m_list.append(item);
@@ -107,6 +112,8 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
107 return item->countVisible();
112 return item->countVisible();
108 case RoleFocused:
113 case RoleFocused:
109 return item->focused();
114 return item->focused();
115+ case RoleAlerting:
116+ return item->alerting();
110 }
117 }
111
118
112 return QVariant();
119 return QVariant();
@@ -121,6 +128,18 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
121 return m_list.at(index);
128 return m_list.at(index);
122}
129}
123
130
131+void MockLauncherModel::setAlerting(const QString &appId, bool alerting) {
132+ int index = findApp(appId);
133+ if (index >= 0) {
134+ QModelIndex modelIndex = this->index(index);
135+ MockLauncherItem *item = m_list.at(index);
136+ if (!item->focused()) {
137+ item->setAlerting(alerting);
138+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
139+ }
140+ }
141+}
142+
124void MockLauncherModel::move(int oldIndex, int newIndex)
143void MockLauncherModel::move(int oldIndex, int newIndex)
125{
144{
126 // Make sure its not moved outside the lists
145 // Make sure its not moved outside the lists
@@ -176,7 +195,9 @@ void MockLauncherModel::requestRemove(const QString &appId)
176 int index = findApp(appId);
195 int index = findApp(appId);
177 if (index >= 0) {
196 if (index >= 0) {
178 beginRemoveRows(QModelIndex(), index, 0);
197 beginRemoveRows(QModelIndex(), index, 0);
179− m_list.takeAt(index)->deleteLater();
198+ MockLauncherItem * item = m_list.takeAt(index);
199+ item->setRunning(false);
200+ item->deleteLater();
180 endRemoveRows();
201 endRemoveRows();
181 }
202 }
182}
203}
@@ -197,6 +218,16 @@ int MockLauncherModel::findApp(const QString &appId)
197 return -1;
218 return -1;
198}
219}
199
220
221+void MockLauncherModel::setProgress(const QString &appId, int progress)
222+{
223+ int index = findApp(appId);
224+ if (index >= 0) {
225+ m_list.at(index)->setProgress(progress);
226+ QModelIndex modelIndex = this->index(index);
227+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleProgress);
228+ }
229+}
230+
200void MockLauncherModel::setUser(const QString &username)
231void MockLauncherModel::setUser(const QString &username)
201{
232{
202 Q_UNUSED(username)
233 Q_UNUSED(username)
@@ -228,6 +259,26 @@ void MockLauncherModel::emitHint()
228 Q_EMIT hint();
259 Q_EMIT hint();
229}
260}
230
261
262+void MockLauncherModel::setCount(const QString &appId, int count)
263+{
264+ int index = findApp(appId);
265+ if (index >= 0) {
266+ m_list.at(index)->setCount(count);
267+ QModelIndex modelIndex = this->index(index);
268+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleCount);
269+ }
270+}
271+
272+void MockLauncherModel::setCountVisible(const QString &appId, bool countVisible)
273+{
274+ int index = findApp(appId);
275+ if (index >= 0) {
276+ m_list.at(index)->setCountVisible(countVisible);
277+ QModelIndex modelIndex = this->index(index);
278+ Q_EMIT dataChanged(modelIndex, modelIndex);
279+ }
280+}
281+
231unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
282unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
232{
283{
233 return nullptr;
284 return nullptr;
tests/mocks/Unity/Launcher/MockLauncherModel.h+4
@@ -38,6 +38,7 @@ public:
38
38
39 QVariant data(const QModelIndex& index, int role) const override;
39 QVariant data(const QModelIndex& index, int role) const override;
40
40
41+ Q_INVOKABLE void setAlerting(const QString &appId, bool alerting) override;
41 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const override;
42 Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const override;
42 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
43 Q_INVOKABLE void move(int oldIndex, int newIndex) override;
43 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
44 Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
@@ -54,6 +55,9 @@ public:
54
55
55 // For testing
56 // For testing
56 Q_INVOKABLE void emitHint();
57 Q_INVOKABLE void emitHint();
58+ Q_INVOKABLE void setProgress(const QString &appId, int progress);
59+ Q_INVOKABLE void setCount(const QString &appId, int count);
60+ Q_INVOKABLE void setCountVisible(const QString &appId, bool countVisible);
57
61
58Q_SIGNALS:
62Q_SIGNALS:
59 void quickListTriggered(const QString &appId, int index);
63 void quickListTriggered(const QString &appId, int index);
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/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
2pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
2pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
3
3
4include_directories(
4include_directories(
tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+3
@@ -43,6 +43,7 @@ private Q_SLOTS:
43 item.insert("name", "Item 1");
43 item.insert("name", "Item 1");
44 item.insert("icon", "fake.svg");
44 item.insert("icon", "fake.svg");
45 item.insert("count", 0);
45 item.insert("count", 0);
46+ item.insert("progress", 0);
46 item.insert("countVisible", false);
47 item.insert("countVisible", false);
47 item.insert("pinned", true);
48 item.insert("pinned", true);
48 list.append(item);
49 list.append(item);
@@ -67,6 +68,7 @@ private Q_SLOTS:
67 inSync &= model->get(i)->name() == list.at(i).value("name").toString();
68 inSync &= model->get(i)->name() == list.at(i).value("name").toString();
68 inSync &= model->get(i)->icon() == list.at(i).value("icon").toString();
69 inSync &= model->get(i)->icon() == list.at(i).value("icon").toString();
69 inSync &= model->get(i)->count() == list.at(i).value("count").toInt();
70 inSync &= model->get(i)->count() == list.at(i).value("count").toInt();
71+ inSync &= model->get(i)->progress() == list.at(i).value("progress").toInt();
70 inSync &= model->get(i)->countVisible() == list.at(i).value("countVisible").toBool();
72 inSync &= model->get(i)->countVisible() == list.at(i).value("countVisible").toBool();
71 }
73 }
72 return inSync;
74 return inSync;
@@ -111,6 +113,7 @@ private Q_SLOTS:
111 newEntry.insert("name", "New app");
113 newEntry.insert("name", "New app");
112 newEntry.insert("icon", "some-icon.svg");
114 newEntry.insert("icon", "some-icon.svg");
113 newEntry.insert("count", 0);
115 newEntry.insert("count", 0);
116+ newEntry.insert("progress", 42);
114 newEntry.insert("countVisible", false);
117 newEntry.insert("countVisible", false);
115 newEntry.insert("pinned", true);
118 newEntry.insert("pinned", true);
116 newList.append(newEntry);
119 newList.append(newEntry);
tests/plugins/LightDM/CMakeLists.txt+6
@@ -9,6 +9,9 @@ add_executable(GreeterDBusTestExec
9 dbus.cpp
9 dbus.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/LightDM/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/LightDM/liblightdm
17 -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
@@ -44,6 +47,9 @@ add_executable(GreeterUsersModelTestExec
44 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
47 ${CMAKE_SOURCE_DIR}/plugins/LightDM/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/LightDM/liblightdm
55 -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
tests/plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
3
3
4include_directories(
4include_directories(
5 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/Unity/Launcher/launchermodeltest.cpp+64−7
@@ -68,6 +68,7 @@ class MockAppManager: public unity::shell::application::ApplicationManagerInterf
68 Q_OBJECT
68 Q_OBJECT
69public:
69public:
70 MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
70 MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
71+ ~MockAppManager() {}
71 int rowCount(const QModelIndex &) const override { return m_list.count(); }
72 int rowCount(const QModelIndex &) const override { return m_list.count(); }
72 QVariant data(const QModelIndex &, int ) const override { return QVariant(); }
73 QVariant data(const QModelIndex &, int ) const override { return QVariant(); }
73 QString focusedApplicationId() const override {
74 QString focusedApplicationId() const override {
@@ -341,6 +342,46 @@ private Q_SLOTS:
341 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
342 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
342 }
343 }
343
344
345+ void testQuitMenuItem() {
346+ // we have 2 apps running, both should have the Quit action in its quick list
347+ QCOMPARE(launcherModel->rowCount(), 2);
348+
349+ // stop the second one keeping it pinned so that it doesn't go away
350+ launcherModel->pin("no-icon");
351+ appManager->stopApplication("no-icon");
352+
353+ // find the first Quit item, should be there
354+ QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
355+ int quitActionIndex = -1;
356+ for (int i = 0; i < model->rowCount(); ++i) {
357+ if (model->get(i).actionId() == "stop_item") {
358+ quitActionIndex = i;
359+ break;
360+ }
361+ }
362+ QVERIFY(quitActionIndex >= 0);
363+
364+ // find the second Quit item, should NOT be there, the app is stopped
365+ QuickListModel *model2 = qobject_cast<QuickListModel*>(launcherModel->get(1)->quickList());
366+ int quitActionIndex2 = -1;
367+ for (int i = 0; i < model2->rowCount(); ++i) {
368+ if (model2->get(i).actionId() == "stop_item") {
369+ quitActionIndex2 = i;
370+ break;
371+ }
372+ }
373+ QVERIFY(quitActionIndex2 == -1);
374+
375+ // trigger the first quit item quicklist action
376+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), quitActionIndex);
377+ // first app should be gone...
378+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
379+ // ... the second app (now at index 0) should still be there, pinned and stopped
380+ QCOMPARE(launcherModel->get(0)->appId(), QStringLiteral("no-icon"));
381+ QCOMPARE(launcherModel->get(0)->pinned(), true);
382+ QCOMPARE(launcherModel->get(0)->running(), false);
383+ }
384+
344 void testGetUrlForAppId() {
385 void testGetUrlForAppId() {
345 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
386 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
346 QCOMPARE(launcherModel->getUrlForAppId(""), QString());
387 QCOMPARE(launcherModel->getUrlForAppId(""), QString());
@@ -384,11 +425,18 @@ private Q_SLOTS:
384 }
425 }
385
426
386 void testCountEmblems() {
427 void testCountEmblems() {
428+ QSignalSpy spy(launcherModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
429+
387 // Call GetAll on abs-icon
430 // Call GetAll on abs-icon
388 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
431 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
389 QDBusReply<QVariantMap> reply = interface.call("GetAll");
432 QDBusReply<QVariantMap> reply = interface.call("GetAll");
390 QVariantMap map = reply.value();
433 QVariantMap map = reply.value();
391
434
435+ // Check that the alerting-status is still false, and the item on the upper side of the API
436+ int index = launcherModel->findApplication("abs-icon");
437+ QCOMPARE(index >= 0, true);
438+ QVERIFY(launcherModel->get(index)->alerting() == false);
439+
392 // Make sure GetAll returns a map with count and countVisible props
440 // Make sure GetAll returns a map with count and countVisible props
393 QCOMPARE(map.contains("count"), true);
441 QCOMPARE(map.contains("count"), true);
394 QCOMPARE(map.contains("countVisible"), true);
442 QCOMPARE(map.contains("countVisible"), true);
@@ -409,13 +457,8 @@ private Q_SLOTS:
409 QCOMPARE(map.value("count").toInt(), 55);
457 QCOMPARE(map.value("count").toInt(), 55);
410 QCOMPARE(map.value("countVisible").toBool(), true);
458 QCOMPARE(map.value("countVisible").toBool(), true);
411
459
412− // Now the item on the upper side of the API
460+ // Finally check, that the change to "count" implicitly also set the alerting-state to true
413− int index = launcherModel->findApplication("abs-icon");
414− QCOMPARE(index >= 0, true);
415−
416− // And make sure values have changed there as well
417− QCOMPARE(launcherModel->get(index)->countVisible(), true);
461+ QVERIFY(launcherModel->get(index)->alerting() == true);
418− QCOMPARE(launcherModel->get(index)->count(), 55);
419 }
462 }
420
463
421 void testCountEmblemAddsRemovesItem_data() {
464 void testCountEmblemAddsRemovesItem_data() {
@@ -475,6 +518,20 @@ private Q_SLOTS:
475 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
518 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
476 }
519 }
477
520
521+ void testAlert() {
522+ // Check that the alerting-status is still false
523+ int index = launcherModel->findApplication("abs-icon");
524+ QCOMPARE(index >= 0, true);
525+ QVERIFY(launcherModel->get(index)->alerting() == false);
526+
527+ // Call Alert() on "abs-icon"
528+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "com.canonical.Unity.Launcher.Item");
529+ interface.call("Alert");
530+
531+ // Check that the alerting-status is now true
532+ QVERIFY(launcherModel->get(index)->alerting() == true);
533+ }
534+
478 void testRefreshAfterDeletedDesktopFiles_data() {
535 void testRefreshAfterDeletedDesktopFiles_data() {
479 QTest::addColumn<bool>("deleted");
536 QTest::addColumn<bool>("deleted");
480 QTest::newRow("have .desktop files") << false;
537 QTest::newRow("have .desktop files") << false;
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/Launcher/tst_Launcher.qml+210−7
@@ -93,6 +93,57 @@ Item {
93 onClicked: launcherLoader.item.inverted = !launcherLoader.item.inverted
93 onClicked: launcherLoader.item.inverted = !launcherLoader.item.inverted
94 Layout.fillWidth: true
94 Layout.fillWidth: true
95 }
95 }
96+
97+ Row {
98+ spacing: units.gu(1)
99+
100+ Button {
101+ text: "35% bar"
102+ onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntryBar.displayText)).appId, 35)
103+ Layout.fillWidth: true
104+ }
105+
106+ TextArea {
107+ id: appIdEntryBar
108+ anchors.verticalCenter: parent.verticalCenter
109+ width: units.gu(4)
110+ height: units.gu(4)
111+ autoSize: true
112+ text: "2"
113+ maximumLineCount: 1
114+ }
115+
116+ Button {
117+ text: "no bar"
118+ onClicked: LauncherModel.setProgress(LauncherModel.get(parseInt(appIdEntryBar.displayText)).appId, -1)
119+ Layout.fillWidth: true
120+ }
121+ }
122+
123+ Row {
124+ spacing: units.gu(1)
125+
126+ Button {
127+ text: "set alert"
128+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntryAlert.displayText)).appId, true)
129+ }
130+
131+ TextArea {
132+ id: appIdEntryAlert
133+ anchors.verticalCenter: parent.verticalCenter
134+ width: units.gu(5)
135+ height: units.gu(4)
136+ autoSize: true
137+ text: "2"
138+ maximumLineCount: 1
139+ Layout.fillWidth: true
140+ }
141+
142+ Button {
143+ text: "unset alert"
144+ onClicked: LauncherModel.setAlerting(LauncherModel.get(parseInt(appIdEntryAlert.displayText)).appId, false)
145+ }
146+ }
96 }
147 }
97
148
98 SignalSpy {
149 SignalSpy {
@@ -146,7 +197,7 @@ Item {
146 startX+units.gu(8), startY);
197 startX+units.gu(8), startY);
147
198
148 var panel = findChild(launcher, "launcherPanel");
199 var panel = findChild(launcher, "launcherPanel");
149− verify(panel != undefined);
200+ verify(!!panel);
150
201
151 // wait until it gets fully extended
202 // wait until it gets fully extended
152 tryCompare(panel, "x", 0);
203 tryCompare(panel, "x", 0);
@@ -157,7 +208,7 @@ Item {
157 mouseMove(root, 1, root.height / 2);
208 mouseMove(root, 1, root.height / 2);
158
209
159 var panel = findChild(launcher, "launcherPanel");
210 var panel = findChild(launcher, "launcherPanel");
160− verify(panel != undefined);
211+ verify(!!panel);
161
212
162 // wait until it gets fully extended
213 // wait until it gets fully extended
163 tryCompare(panel, "x", 0);
214 tryCompare(panel, "x", 0);
@@ -169,6 +220,16 @@ Item {
169 tryCompare(panel, "x", -panel.width, 1000);
220 tryCompare(panel, "x", -panel.width, 1000);
170 }
221 }
171
222
223+ function waitForWiggleToStart(appIcon) {
224+ verify(appIcon != undefined)
225+ tryCompare(appIcon, "wiggling", true, 1000, "wiggle-anim should not be in stopped state")
226+ }
227+
228+ function waitForWiggleToStop(appIcon) {
229+ verify(appIcon != undefined)
230+ tryCompare(appIcon, "wiggling", false, 1000, "wiggle-anim should not be in running state")
231+ }
232+
172 function positionLauncherListAtBeginning() {
233 function positionLauncherListAtBeginning() {
173 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
234 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
174 listView.contentY = -listView.topMargin;
235 listView.contentY = -listView.topMargin;
@@ -187,7 +248,7 @@ Item {
187 waitUntilLauncherDisappears();
248 waitUntilLauncherDisappears();
188
249
189 var panel = findChild(launcher, "launcherPanel")
250 var panel = findChild(launcher, "launcherPanel")
190− verify(panel != undefined)
251+ verify(!!panel)
191
252
192 // it starts out hidden just left of the left launcher edge
253 // it starts out hidden just left of the left launcher edge
193 compare(panel.x, -panel.width)
254 compare(panel.x, -panel.width)
@@ -225,7 +286,7 @@ Item {
225
286
226 var appIcon = findChild(launcher, "launcherDelegate0");
287 var appIcon = findChild(launcher, "launcherDelegate0");
227
288
228− verify(appIcon != undefined);
289+ verify(!!appIcon);
229
290
230 if (data.mouse) {
291 if (data.mouse) {
231 mouseClick(appIcon);
292 mouseClick(appIcon);
@@ -248,7 +309,7 @@ Item {
248 dragLauncherIntoView()
309 dragLauncherIntoView()
249
310
250 var dashIcon = findChild(launcher, "dashItem")
311 var dashIcon = findChild(launcher, "dashItem")
251− verify(dashIcon != undefined)
312+ verify(!!dashIcon)
252
313
253 mouseClick(dashIcon)
314 mouseClick(dashIcon)
254
315
@@ -547,7 +608,7 @@ Item {
547
608
548 // Doing longpress
609 // Doing longpress
549 mousePress(draggedItem);
610 mousePress(draggedItem);
550− tryCompare(quickListShape, "opacity", 0.96);
611+ tryCompare(quickListShape, "opacity", 0.8);
551 mouseRelease(draggedItem);
612 mouseRelease(draggedItem);
552
613
553 verify(quickList.y >= units.gu(1));
614 verify(quickList.y >= units.gu(1));
@@ -632,9 +693,31 @@ Item {
632 tryCompare(quickList, "state", "");
693 tryCompare(quickList, "state", "");
633 }
694 }
634
695
696+ function test_quickListMenuOnRMB() {
697+ dragLauncherIntoView();
698+ var clickedItem = findChild(launcher, "launcherDelegate5")
699+ var quickList = findChild(launcher, "quickList")
700+ var quickListShape = findChild(launcher, "quickListShape")
701+ var dndArea = findChild(launcher, "dndArea");
702+
703+ // Initial state
704+ tryCompare(quickListShape, "visible", false)
705+
706+ // Doing RMB click
707+ mouseClick(clickedItem, clickedItem.width / 2, clickedItem.height / 2, Qt.RightButton)
708+ tryCompare(quickListShape, "visible", true)
709+ verify(quickList, "state", "open")
710+ verify(dndArea, "dragging", false)
711+
712+ // Click somewhere in the empty space to dismiss the quicklist
713+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
714+ tryCompare(quickListShape, "visible", false);
715+ verify(quickList, "state", "")
716+ }
717+
635 function test_revealByHover() {
718 function test_revealByHover() {
636 var panel = findChild(launcher, "launcherPanel");
719 var panel = findChild(launcher, "launcherPanel");
637− verify(panel != undefined);
720+ verify(!!panel);
638
721
639 revealByHover();
722 revealByHover();
640 tryCompare(launcher, "state", "visibleTemporary");
723 tryCompare(launcher, "state", "visibleTemporary");
@@ -645,5 +728,125 @@ Item {
645 tryCompare(launcher, "state", "", 1000, "Launcher didn't hide after moving mouse away from it");
728 tryCompare(launcher, "state", "", 1000, "Launcher didn't hide after moving mouse away from it");
646 waitUntilLauncherDisappears();
729 waitUntilLauncherDisappears();
647 }
730 }
731+
732+ function test_progressChangeViaModel() {
733+ dragLauncherIntoView();
734+ var item = findChild(launcher, "launcherDelegate0")
735+ verify(item != undefined)
736+ LauncherModel.setProgress(LauncherModel.get(0).appId, -1)
737+ compare(findChild(item, "progressOverlay").visible, false)
738+ LauncherModel.setProgress(LauncherModel.get(0).appId, 20)
739+ compare(findChild(item, "progressOverlay").visible, true)
740+ LauncherModel.setProgress(LauncherModel.get(0).appId, 0)
741+ }
742+
743+ function test_alertPeekingIcon() {
744+ var listView = findChild(launcher, "launcherListView")
745+ verify(listView != undefined)
746+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
747+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
748+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
749+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
750+ }
751+
752+ function test_alertHidingIcon() {
753+ var listView = findChild(launcher, "launcherListView")
754+ verify(listView != undefined)
755+ var appIcon6 = findChild(launcher, "launcherDelegate6")
756+ verify(appIcon6 != undefined)
757+ LauncherModel.setAlerting(LauncherModel.get(6).appId, true)
758+ waitForWiggleToStart(appIcon6)
759+ LauncherModel.setAlerting(LauncherModel.get(6).appId, false)
760+ waitForWiggleToStop(appIcon6)
761+ tryCompare(appIcon6, "x", 0, 1000, "x-value of appId #6 should not be non-zero")
762+ waitForRendering(listView)
763+ }
764+
765+ function test_alertIgnoreFocusedApp() {
766+ LauncherModel.setAlerting(LauncherModel.get(0).appId, true)
767+ compare(LauncherModel.get(0).alerting, false, "Focused app should not have the alert-state set")
768+ }
769+
770+ function test_alertOnlyOnePeekingIcon() {
771+ var listView = findChild(launcher, "launcherListView")
772+ verify(listView != undefined)
773+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
774+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
775+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
776+ tryCompare(listView, "peekingIndex", 3, 1000, "Wrong appId set as peeking-index")
777+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
778+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
779+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
780+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
781+ waitForRendering(listView)
782+ }
783+
784+ function test_alertMultipleApps() {
785+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
786+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
787+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
788+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
789+ compare(LauncherModel.get(1).alerting, true, "Alert-state of appId #1 should not be false")
790+ compare(LauncherModel.get(3).alerting, true, "Alert-state of appId #3 should not be false")
791+ compare(LauncherModel.get(5).alerting, true, "Alert-state of appId #5 should not be false")
792+ compare(LauncherModel.get(7).alerting, true, "Alert-state of appId #7 should not be false")
793+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
794+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
795+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
796+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
797+ compare(LauncherModel.get(1).alerting, false, "Alert-state of appId #1 should not be true")
798+ compare(LauncherModel.get(3).alerting, false, "Alert-state of appId #1 should not be true")
799+ compare(LauncherModel.get(5).alerting, false, "Alert-state of appId #1 should not be true")
800+ compare(LauncherModel.get(7).alerting, false, "Alert-state of appId #1 should not be true")
801+ }
802+
803+ function test_alertMoveIconIntoView() {
804+ dragLauncherIntoView();
805+ var appIcon1 = findChild(launcher, "launcherDelegate1");
806+ var appIcon7 = findChild(launcher, "launcherDelegate7");
807+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
808+ tryCompare(appIcon1, "angle", 0, 1000, "angle of appId #1 should not be non-zero")
809+ waitForWiggleToStart(appIcon1)
810+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
811+ tryCompare(appIcon7, "angle", 0, 1000, "angle of appId #7 should not be non-zero")
812+ waitForWiggleToStart(appIcon7)
813+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
814+ waitForWiggleToStop(appIcon1)
815+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
816+ waitForWiggleToStop(appIcon7)
817+ }
818+
819+ function test_alertWigglePeekDrag() {
820+ var appIcon5 = findChild(launcher, "launcherDelegate5");
821+ var listView = findChild(launcher, "launcherListView")
822+ verify(listView != undefined)
823+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
824+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
825+ waitForWiggleToStart(appIcon5)
826+ tryCompare(appIcon5, "wiggling", true, 1000, "appId #6 should not be still")
827+ dragLauncherIntoView();
828+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
829+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
830+ waitForWiggleToStop(appIcon5)
831+ tryCompare(appIcon5, "wiggling", false, 1000, "appId #1 should not be wiggling")
832+ }
833+
834+ function test_alertViaCountAndCountVisible() {
835+ dragLauncherIntoView();
836+ var appIcon1 = findChild(launcher, "launcherDelegate1")
837+ var oldCount = LauncherModel.get(1).count
838+ LauncherModel.setCount(LauncherModel.get(1).appId, 42)
839+ tryCompare(appIcon1, "wiggling", false, 1000, "appId #1 should be still")
840+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 1)
841+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
842+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
843+ waitForWiggleToStop(appIcon1)
844+ LauncherModel.setCount(LauncherModel.get(1).appId, 4711)
845+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
846+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
847+ waitForWiggleToStop(appIcon1)
848+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 0)
849+ LauncherModel.setCount(LauncherModel.get(1).appId, oldCount)
850+ }
648 }
851 }
649}
852}
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
@@ -319,6 +319,36 @@ Item {
319 tryCompare(left, "shown", true); // and we should still be on left
319 tryCompare(left, "shown", true); // and we should still be on left
320 }
320 }
321
321
322+ function test_launcherNoDragGap() {
323+ // See bug 1454882, where if you dragged the launcher while it was
324+ // visible, you could pull it further than the edge of the screen.
325+
326+ var left = goToPage("tutorialLeft");
327+ var launcher = findChild(shell, "launcher");
328+ var teaseAnimation = findInvisibleChild(left, "teaseAnimation");
329+
330+ // Wait for launcher to be really out there
331+ tryCompareFunction(function() {return launcher.x > teaseAnimation.maxBounce/2}, true);
332+ verify(teaseAnimation.running);
333+
334+ // Start a drag, make sure animation stops
335+ touchFlick(shell, 0, halfHeight, units.gu(4), halfHeight, true, false);
336+ verify(!teaseAnimation.running);
337+ verify(launcher.visibleWidth > 0);
338+ verify(launcher.x > 0);
339+ compare(launcher.x, teaseAnimation.bounce);
340+
341+ // Continue drag, make sure we don't create a gap on the left hand side
342+ touchFlick(shell, units.gu(4), halfHeight, shell.width, halfHeight, false, false);
343+ verify(!teaseAnimation.running);
344+ compare(launcher.visibleWidth, launcher.panelWidth);
345+ compare(launcher.x, 0);
346+
347+ // Finish and make sure we continue animation
348+ touchFlick(shell, shell.width, halfHeight, shell.width, halfHeight, false, true);
349+ tryCompare(teaseAnimation, "running", true);
350+ }
351+
322 function test_spread() {
352 function test_spread() {
323 // Unfortunately, most of what we want to test of the spread is
353 // Unfortunately, most of what we want to test of the spread is
324 // "did it render correctly?" but that's hard to test. So instead,
354 // "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() {
tests/scripts/READMEnew file+9
@@ -0,0 +1,9 @@
1+This directory holds a few handy shell-scripts to simplify runtime-testing of DBus-APIs (requires qdbus-qt5 to be installed).
2+
3+launcher-icon related:
4+ * list-launcher-icons.sh - get all icons of unity8-launcher
5+ * alert-launcher-icon.sh - trigger alert/wiggle of unfocused launcher-icon
6+ * get-progress.sh - get progress-value of a launcher-icon
7+ * set-progress.sh - set progress-value of a launcher-icon
8+ * set-count.sh - set the count of a launcher-icon
9+ * set-count-visible.sh - set the count-visible flag of a launcher-icon
tests/scripts/alert-launcher-icon.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 com.canonical.Unity.Launcher.Item.Alert
tests/scripts/get-progress.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Get com.canonical.Unity.Launcher.Item progress
tests/scripts/list-launcher-icons.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity | grep Launcher | cut -f6 -d/
tests/scripts/set-count-visible.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item countVisible $2
tests/scripts/set-count.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item count $2
tests/scripts/set-progress.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item progress $2
tools/CMakeLists.txt+1
@@ -1,6 +1,7 @@
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+};