on xenial help sign in
Merge trunk and resolve conflict
8989975Gary.Wzl
CMakeLists.txt+1−1
@@ -56,7 +56,7 @@ find_package(Qt5DBus 5.2 REQUIRED)
56find_package(Qt5Concurrent 5.2 REQUIRED)
56find_package(Qt5Concurrent 5.2 REQUIRED)
57find_package(Qt5Sql 5.2 REQUIRED)
57find_package(Qt5Sql 5.2 REQUIRED)
58
58
59−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
59+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=7)
60
60
61# Standard install paths
61# Standard install paths
62include(GNUInstallDirs)
62include(GNUInstallDirs)
debian/changelog+101−25
@@ -1,44 +1,120 @@
1+unity8 (8.11+15.04.20150821-0ubuntu1) vivid; urgency=medium
2+
3+ [ Michael Terry ]
4+ * Skip the tutorial entirely when in desktop mode. This fixes the
5+ emulator not being able to pass the tutorial.
6+ * Skip the tutorial entirely when in desktop mode. This fixes the
7+ emulator not being able to pass the tutorial.
8+
9+ -- CI Train Bot <ci-train-bot@canonical.com> Fri, 21 Aug 2015 14:34:50 +0000
10+
1−unity8 (8.10+15.10.20150721-0ubuntu1) wily; urgency=medium
11+unity8 (8.11+15.04.20150817-0ubuntu1) vivid; urgency=medium
2
12
3 [ Albert Astals Cid ]
13 [ Albert Astals Cid ]
4− * Add context for "Please re-enter" (LP: #1377195)
14+ * Drop gcc 4.9 requirement in debian/rules
5− * Fix i18n in scope tool (LP: #1317541)
15+ * Make the test more stable
6− * Fix warning when doing make try
16+ * Optimize scope.preview() calls (LP: #1478022)
7− * New phone background provided by design (LP: #1364537)
17+
8− * PreviewExpandable should be not expanded on startup
18+ [ Alberto Mardegan ]
9− * Update state of indicators bar only when the user is actively
19+ * Set currentIndex after the model change is completed (LP: #1433442)
10− dragging himself (LP: #1457044)
20+
11− * icon-actions preview widget
21+ -- CI Train Bot <ci-train-bot@canonical.com> Mon, 17 Aug 2015 11:57:31 +0000
12− * make testPageHeader more stable
22+
23+unity8 (8.11+15.04.20150814-0ubuntu1) vivid; urgency=medium
24+
25+ [ Albert Astals Cid ]
26+ * Make the test pass again after rework of appmanager
27+ * fix testPhoneStage
13
28
14 [ CI Train Bot ]
29 [ CI Train Bot ]
15− * Resync trunk.
30+ * New rebuild forced.
16
31
17− [ Josh Arenson ]
32+ [ Daniel d'Andrada ]
18− * Add handling for the location page to the autopilot settings wizard
33+ * Fix and clean up InputInfo plugin
19− test
20
34
21− [ Michael Zanetti ]
35+ [ Rodney Dawes ]
22− * allow opening the manage dash area by clicking with a mouse on the
36+ * Do not call verify in the payments button code. (LP: #1468038)
23− arrow label
37+
38+ -- CI Train Bot <ci-train-bot@canonical.com> Fri, 14 Aug 2015 09:53:11 +0000
39+
40+unity8 (8.11+15.04.20150812-0ubuntu1) vivid; urgency=medium
41+
42+ [ Albert Astals Cid ]
43+ * Add a test that checks that items in an expandable are the correct
44+ size after expanding the expandable
45+ * Fix the expandable test
46+ * Replace some waitForRendering with wait(0)
47+ * Workaround for bug 1473471 (LP: #1473471)
48+
49+ [ Gary.Wzl ]
50+ * Fix layout issue when adding audio track widget in expandable widget
24
51
25 [ handsome_feng ]
52 [ handsome_feng ]
26− * Added touch state on navigation. (LP: #1383213)
53+ * Added indication for running apps. (LP: #1471577)
54+ * Adjust the color to highlighting indicator. (LP: #1457098)
55+ * Convert the MouseArea to MultiPointTouchArea to track multiple touch
56+ points. (LP: #1445472)
27
57
28 [ handsome_feng<445865575@qq.com> ]
58 [ handsome_feng<445865575@qq.com> ]
29− * Added touch state on navigation. (LP: #1383213)
59+ * Convert the MouseArea to MultiPointTouchArea to track multiple touch
60+ points. (LP: #1445472)
30
61
31− -- CI Train Bot <ci-train-bot@canonical.com> Tue, 21 Jul 2015 09:10:05 +0000
62+ -- CI Train Bot <ci-train-bot@canonical.com> Wed, 12 Aug 2015 08:41:24 +0000
32
63
33−unity8 (8.10+15.10.20150713-0ubuntu1) wily; urgency=medium
64+unity8 (8.10+15.10.20150804-0ubuntu1) wily; urgency=medium
34
65
35− [ Albert Astals Cid ]
66+ [ Kevin Gunn ]
36− * Revert Session Screenshotter (LP: #1473476)
67+ * No change, rebuild for gcc5
68+
69+ [ CI Train Bot ]
70+ * Resync trunk. added: po/cy.po
71+ * Resync trunk. added: po/cy.po
72+
73+ [ Daniel d'Andrada ]
74+ * Stages now control the "requestedState" property of applications
75+ * Stages now control the "requestedState" property of applications
76+
77+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 04 Aug 2015 20:56:05 +0000
78+
79+unity8 (8.10+15.10.20150729-0ubuntu1) wily; urgency=medium
80+
81+ [ CI Train Bot ]
82+ * Resync trunk.
83+ * allow opening the manage dash area by clicking with a mouse on the
84+ arrow label (LP: #1431564)
37
85
38 [ Daniel d'Andrada ]
86 [ Daniel d'Andrada ]
39− * Dismiss the apps spread if greeter gets shown (LP: #1471609)
87+ * TouchRegistry: remove null candidates from list of candidates (LP:
88+ #1473492)
89+
90+ [ Lukáš Tinkl ]
91+ * Fix power dialogs on desktop
92+ * Provide DBUS compatibility with various session services
93+ (suspend/hibernate, lock/unlock, screensaver, etc)
94+ * React on PrtScr keyboard shortcut for taking screenshots on desktop
95+ (LP: #1474149)
96+ * launcher parity: close apps from quicklist (LP: #1457201)
97+
98+ [ Michael Zanetti ]
99+ * Implement first edition for a desktop Alt+Tab spread
100+ * drop the gcc-4.9 dependency (LP: #1452348)
101+
102+ [ Mirco Müller ]
103+ * Added corresponding tests and visual tweaks to a launcher-item's
104+ progress-overlay.
105+ * Added corresponding tests and visual tweaks to a launcher-item's
106+ progress-overlay.
107+ * Implemented alert/wiggle feature for launcher-icons.
108+ * Implemented alert/wiggle feature for launcher-icons.
109+
110+ [ handsome_feng ]
111+ * makes left swip reset the search string. (LP: #1413791)
112+
113+ [ handsome_feng<445865575@qq.com> ]
114+ * Don't expand indicators when tap to return to call. (LP: #1453217)
115+ * makes left swip reset the search string. (LP: #1413791)
40
116
41− -- CI Train Bot <ci-train-bot@canonical.com> Mon, 13 Jul 2015 14:17:20 +0000
117+ -- CI Train Bot <ci-train-bot@canonical.com> Wed, 29 Jul 2015 12:39:01 +0000
42
118
43unity8 (8.10+15.10.20150708.2-0ubuntu1) wily; urgency=medium
119unity8 (8.10+15.10.20150708.2-0ubuntu1) wily; urgency=medium
44
120
debian/control+7−9
@@ -6,12 +6,10 @@ Build-Depends: cmake,
6 dbus-test-runner,
6 dbus-test-runner,
7 debhelper (>= 9),
7 debhelper (>= 9),
8 doxyqml,
8 doxyqml,
9−# We rely on C++11 features, and to prevent from ABI breaks
10−# in libstdc++ causing us issues, we explicitly select a G++
11−# version. To allow cross-compiling to work, we also must
9+# To allow cross-compiling to work, we also must append :native
12−# append :native to g++-4.9 so we don't try to run armhf g++
10+# to g++ so we don't try to run armhf g++
13# on an x86 CPU for eaxmple, when cross-compiling.
11# on an x86 CPU for eaxmple, when cross-compiling.
14− g++-4.9:native,
12+ g++:native,
15 libandroid-properties-dev,
13 libandroid-properties-dev,
16 graphviz,
14 graphviz,
17 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
15 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
@@ -30,7 +28,7 @@ Build-Depends: cmake,
30 libqt5xmlpatterns5-dev,
28 libqt5xmlpatterns5-dev,
31 libsystemsettings-dev,
29 libsystemsettings-dev,
32 libudev-dev,
30 libudev-dev,
33− libunity-api-dev (>= 7.97),
31+ libunity-api-dev (>= 7.99),
34 libusermetricsoutput1-dev,
32 libusermetricsoutput1-dev,
35 libxcb1-dev,
33 libxcb1-dev,
36 pkg-config,
34 pkg-config,
@@ -101,7 +99,7 @@ Depends: gsettings-desktop-schemas,
101 qtdeclarative5-ubuntu-telephony0.1,
99 qtdeclarative5-ubuntu-telephony0.1,
102 qtdeclarative5-ubuntu-web-plugin,
100 qtdeclarative5-ubuntu-web-plugin,
103 ubuntu-system-settings,
101 ubuntu-system-settings,
104− unity-launcher-impl-4,
102+ unity-launcher-impl-7,
105 unity8-common (= ${source:Version}),
103 unity8-common (= ${source:Version}),
106 unity8-private (= ${binary:Version}),
104 unity8-private (= ${binary:Version}),
107 unity8-private | unity-launcher-impl,
105 unity8-private | unity-launcher-impl,
@@ -130,7 +128,7 @@ Depends: qml-module-qtquick-layouts,
130 unity-application-impl-6,
128 unity-application-impl-6,
131 unity-notifications-impl-3,
129 unity-notifications-impl-3,
132 unity-plugin-scopes | unity-scopes-impl,
130 unity-plugin-scopes | unity-scopes-impl,
133− unity-scopes-impl-6,
131+ unity-scopes-impl-7,
134 unity8-fake-env | unity-application-impl,
132 unity8-fake-env | unity-application-impl,
135 ${misc:Depends},
133 ${misc:Depends},
136Breaks: unity8 (<< 7.86),
134Breaks: unity8 (<< 7.86),
@@ -191,7 +189,7 @@ Depends: accountsservice-ubuntu-schemas (>= 0.0.3),
191 ${misc:Depends},
189 ${misc:Depends},
192 ${shlibs:Depends},
190 ${shlibs:Depends},
193Provides: unity-launcher-impl,
191Provides: unity-launcher-impl,
194− unity-launcher-impl-4,
192+ unity-launcher-impl-7,
195Description: Unity 8 private libs
193Description: Unity 8 private libs
196 The Unity 8 shell is the primary user interface for Ubuntu devices.
194 The Unity 8 shell is the primary user interface for Ubuntu devices.
197 .
195 .
debian/rules−5
@@ -8,11 +8,6 @@ include /usr/share/dpkg/default.mk
8export DPKG_GENSYMBOLS_CHECK_LEVEL=4
8export DPKG_GENSYMBOLS_CHECK_LEVEL=4
9export NO_PNG_PKG_MANGLE=1
9export NO_PNG_PKG_MANGLE=1
10
10
11−# Explicitly selecting a G{CC,++}-version here to avoid accidental
12−# ABI breaks introduced by toolchain updates.
13−export CC=$(DEB_HOST_GNU_TYPE)-gcc-4.9
14−export CXX=$(DEB_HOST_GNU_TYPE)-g++-4.9
15−
16%:
11%:
17 dh $@ --parallel --fail-missing --with python3
12 dh $@ --parallel --fail-missing --with python3
18
13
libs/UbuntuGestures/TouchRegistry.cpp+60−14
@@ -271,6 +271,8 @@ void TouchRegistry::addCandidateOwnerForTouch(int id, QQuickItem *candidate)
271 this, &TouchRegistry::rejectCandidateOwnerForTouch);
271 this, &TouchRegistry::rejectCandidateOwnerForTouch);
272
272
273 touchInfo->candidates.append(candidateInfo);
273 touchInfo->candidates.append(candidateInfo);
274+
275+ connect(candidate, &QObject::destroyed, this, [=](){ pruneNullCandidatesForTouch(id); });
274}
276}
275
277
276void TouchRegistry::addTouchWatcher(int touchId, QQuickItem *watcher)
278void TouchRegistry::addTouchWatcher(int touchId, QQuickItem *watcher)
@@ -296,25 +298,53 @@ void TouchRegistry::removeCandidateOwnerForTouch(int id, QQuickItem *candidate)
296 Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id);
298 Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(id);
297 if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); }
299 if (!touchInfo) { qFatal("TouchRegistry: Failed to find TouchInfo"); }
298
300
299− int indexRemoved = -1;
300
301
301 // TODO: check if the candidate is in fact the owner of the touch
302 // TODO: check if the candidate is in fact the owner of the touch
302
303
304+ bool removed = false;
303− for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
305+ for (int i = 0; i < touchInfo->candidates.count() && !removed; ++i) {
304− CandidateInfo &candidateInfo = touchInfo->candidates[i];
306+ if (touchInfo->candidates[i].item == candidate) {
305− if (candidateInfo.item == candidate) {
307+ removeCandidateOwnerForTouchByIndex(touchInfo, i);
306− Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
308+ removed = true;
307− if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
308− qCritical("TouchRegistry: touch owner is being removed.");
309− }
310− delete candidateInfo.inactivityTimer;
311− candidateInfo.inactivityTimer = nullptr;
312− touchInfo->candidates.removeAt(i);
313− indexRemoved = i;
314 }
309 }
315 }
310 }
311+}
312+
313+void TouchRegistry::pruneNullCandidatesForTouch(int touchId)
314+{
315+ #if TOUCHREGISTRY_DEBUG
316+ UG_DEBUG << "pruneNullCandidatesForTouch touchId" << touchId;
317+ #endif
318+
319+ Pool<TouchInfo>::Iterator touchInfo = findTouchInfo(touchId);
320+ if (!touchInfo) {
321+ // doesn't matter as touch is already gone.
322+ return;
323+ }
316
324
317− if (indexRemoved == 0) {
325+ int i = 0;
326+ while (i < touchInfo->candidates.count()) {
327+ if (touchInfo->candidates[i].item.isNull()) {
328+ removeCandidateOwnerForTouchByIndex(touchInfo, i);
329+ } else {
330+ ++i;
331+ }
332+ }
333+}
334+
335+void TouchRegistry::removeCandidateOwnerForTouchByIndex(Pool<TouchRegistry::TouchInfo>::Iterator &touchInfo,
336+ int candidateIndex)
337+{
338+ // TODO: check if the candidate is in fact the owner of the touch
339+
340+ Q_ASSERT(candidateIndex < touchInfo->candidates.count());
341+
342+ if (candidateIndex == 0 && touchInfo->candidates[candidateIndex].state != CandidateInfo::Undecided) {
343+ qCritical("TouchRegistry: touch owner is being removed.");
344+ }
345+ removeCandidateHelper(touchInfo, candidateIndex);
346+
347+ if (candidateIndex == 0) {
318 // the top candidate has been removed. if the new top candidate
348 // the top candidate has been removed. if the new top candidate
319 // wants the touch let him know he's now the owner.
349 // wants the touch let him know he's now the owner.
320 if (touchInfo->isOwned()) {
350 if (touchInfo->isOwned()) {
@@ -359,6 +389,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
359 touchInfo->candidates.append(candidateInfo);
389 touchInfo->candidates.append(candidateInfo);
360 // it's the last one
390 // it's the last one
361 candidateIndex = touchInfo->candidates.count() - 1;
391 candidateIndex = touchInfo->candidates.count() - 1;
392+ connect(candidate, &QObject::destroyed, this, [=](){ pruneNullCandidatesForTouch(id); });
362 }
393 }
363
394
364 // If it's the top candidate it means it's now the owner. Let
395 // If it's the top candidate it means it's now the owner. Let
@@ -430,7 +461,7 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
430 QCoreApplication::sendEvent(candidate, &lostOwnershipEvent);
461 QCoreApplication::sendEvent(candidate, &lostOwnershipEvent);
431 }
462 }
432
463
433− touchInfo->candidates.removeAt(rejectedCandidateIndex);
464+ removeCandidateHelper(touchInfo, rejectedCandidateIndex);
434
465
435 if (rejectedCandidateIndex == 0) {
466 if (rejectedCandidateIndex == 0) {
436 // the top candidate has been removed. if the new top candidate
467 // the top candidate has been removed. if the new top candidate
@@ -441,6 +472,21 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
441 }
472 }
442}
473}
443
474
475+void TouchRegistry::removeCandidateHelper(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex)
476+{
477+ {
478+ CandidateInfo &candidateInfo = touchInfo->candidates[candidateIndex];
479+
480+ delete candidateInfo.inactivityTimer;
481+ candidateInfo.inactivityTimer = nullptr;
482+
483+ if (candidateInfo.item) {
484+ disconnect(candidateInfo.item.data(), nullptr, this, nullptr);
485+ }
486+ }
487+ touchInfo->candidates.removeAt(candidateIndex);
488+}
489+
444////////////////////////////////////// TouchRegistry::TouchInfo ////////////////////////////////////
490////////////////////////////////////// TouchRegistry::TouchInfo ////////////////////////////////////
445
491
446TouchRegistry::TouchInfo::TouchInfo(int id)
492TouchRegistry::TouchInfo::TouchInfo(int id)
libs/UbuntuGestures/TouchRegistry.h+4−1
@@ -149,7 +149,6 @@ private:
149 // yet because of undecided candidates higher up.
149 // yet because of undecided candidates higher up.
150 InterimOwner = 2
150 InterimOwner = 2
151 } state;
151 } state;
152− // TODO: Prune candidates that become null and resolve ownership accordingly.
153 QPointer<QQuickItem> item;
152 QPointer<QQuickItem> item;
154 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
153 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
155 };
154 };
@@ -172,6 +171,10 @@ private:
172 QList<QPointer<QQuickItem>> watchers;
171 QList<QPointer<QQuickItem>> watchers;
173 };
172 };
174
173
174+ void pruneNullCandidatesForTouch(int touchId);
175+ void removeCandidateOwnerForTouchByIndex(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex);
176+ void removeCandidateHelper(Pool<TouchInfo>::Iterator &touchInfo, int candidateIndex);
177+
175 Pool<TouchInfo>::Iterator findTouchInfo(int id);
178 Pool<TouchInfo>::Iterator findTouchInfo(int id);
176
179
177 void deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QTouchEvent *event);
180 void deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QTouchEvent *event);
plugins/AccountsService/AccountsService.cpp+1−2
@@ -141,9 +141,9 @@ void AccountsService::updateDemoEdges(bool async)
141 this, [this](QDBusPendingCallWatcher* watcher) {
141 this, [this](QDBusPendingCallWatcher* watcher) {
142
142
143 QDBusPendingReply<QDBusVariant> reply = *watcher;
143 QDBusPendingReply<QDBusVariant> reply = *watcher;
144+ watcher->deleteLater();
144 if (reply.isError()) {
145 if (reply.isError()) {
145 qWarning() << "Failed to get 'demo-edges' property - " << reply.error().message();
146 qWarning() << "Failed to get 'demo-edges' property - " << reply.error().message();
146− watcher->deleteLater();
147 return;
147 return;
148 }
148 }
149
149
@@ -152,7 +152,6 @@ void AccountsService::updateDemoEdges(bool async)
152 m_demoEdges = demoEdges;
152 m_demoEdges = demoEdges;
153 Q_EMIT demoEdgesChanged();
153 Q_EMIT demoEdgesChanged();
154 }
154 }
155− watcher->deleteLater();
156 });
155 });
157 if (!async) {
156 if (!async) {
158 watcher->waitForFinished();
157 watcher->waitForFinished();
plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−2
@@ -1,5 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4
3
5add_definitions(-DSM_BUSNAME=systemBus)
4add_definitions(-DSM_BUSNAME=systemBus)
plugins/Greeter/Unity/Launcher/launcheritem.cpp+14
@@ -31,6 +31,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
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/ScreenGrabber/screengrabber.cpp+31−20
@@ -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
@@ -12,8 +12,6 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
15 */
18
16
19#include "screengrabber.h"
17#include "screengrabber.h"
@@ -28,27 +26,35 @@
28
26
29#include <QDebug>
27#include <QDebug>
30
28
31−void saveScreenshot(QImage screenshot, QString filename, QString format, int quality)
29+bool saveScreenshot(const QImage &screenshot, const QString &filename, const QString &format, int quality)
32{
30{
33− if (!screenshot.save(filename, format.toLatin1().data(), quality))
31+ if (!screenshot.save(filename, format.toLatin1().data(), quality)) {
34− qWarning() << "ScreenShotter: failed to save snapshot!";
32+ qWarning() << "ScreenGrabber: failed to save snapshot!";
33+ return false;
34+ }
35+
36+ return true;
35}
37}
36
38
37ScreenGrabber::ScreenGrabber(QObject *parent)
39ScreenGrabber::ScreenGrabber(QObject *parent)
38− : QObject(parent),
40+ : QObject(parent)
39− screenshotQuality(0)
40{
41{
42+ QDir screenshotsDir;
43+ if (qEnvironmentVariableIsSet("UNITY_TESTING")) {
44+ qDebug() << "Using test environment";
45+ QStandardPaths::setTestModeEnabled(true);
46+ screenshotsDir = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
47+ } else {
48+ qDebug() << "Using real environment";
41− QDir screenshotsDir(QStandardPaths::displayName(QStandardPaths::PicturesLocation));
49+ screenshotsDir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
50+ }
42− screenshotsDir.mkdir("Screenshots");
51+ screenshotsDir.mkpath("Screenshots");
43 screenshotsDir.cd("Screenshots");
52 screenshotsDir.cd("Screenshots");
44− if (screenshotsDir.exists())
53+ if (screenshotsDir.exists()) {
45− {
46 fileNamePrefix = screenshotsDir.absolutePath();
54 fileNamePrefix = screenshotsDir.absolutePath();
47 fileNamePrefix.append("/screenshot");
55 fileNamePrefix.append("/screenshot");
48− }
49− else
56+ } else {
50− {
51− qWarning() << "ScreenShotter: failed to create directory at: " << screenshotsDir.absolutePath();
57+ qWarning() << "ScreenGrabber: failed to create directory at: " << screenshotsDir.absolutePath();
52 }
58 }
53}
59}
54
60
@@ -74,11 +80,16 @@ void ScreenGrabber::captureAndSave()
74 return;
80 return;
75 }
81 }
76
82
77− QImage screenshot = main_window->grabWindow();
83+ const QImage screenshot = main_window->grabWindow();
84+ const QString filename = makeFileName();
85+ qDebug() << "Saving screenshot to" << filename;
78− QtConcurrent::run(saveScreenshot, screenshot, makeFileName(), getFormat(), screenshotQuality);
86+ auto saveOp = QtConcurrent::run(saveScreenshot, screenshot, filename, getFormat(), screenshotQuality);
87+ if (saveOp.result()) {
88+ Q_EMIT screenshotSaved(filename);
89+ }
79}
90}
80
91
81−QString ScreenGrabber::makeFileName()
92+QString ScreenGrabber::makeFileName() const
82{
93{
83 QString fileName(fileNamePrefix);
94 QString fileName(fileNamePrefix);
84 fileName.append(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz"));
95 fileName.append(QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz"));
@@ -87,7 +98,7 @@ QString ScreenGrabber::makeFileName()
87 return fileName;
98 return fileName;
88}
99}
89
100
90−QString ScreenGrabber::getFormat()
101+QString ScreenGrabber::getFormat() const
91{
102{
92 //TODO: This should be configurable (perhaps through gsettings?)
103 //TODO: This should be configurable (perhaps through gsettings?)
93 return "png";
104 return "png";
plugins/ScreenGrabber/screengrabber.h+8−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
@@ -12,8 +12,6 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
15 */
18
16
19#ifndef SNAPSHOTTER_H
17#ifndef SNAPSHOTTER_H
@@ -28,15 +26,19 @@ class ScreenGrabber: public QObject
28
26
29public:
27public:
30 explicit ScreenGrabber(QObject *parent = 0);
28 explicit ScreenGrabber(QObject *parent = 0);
29+ ~ScreenGrabber() = default;
31
30
32public Q_SLOTS:
31public Q_SLOTS:
33 void captureAndSave();
32 void captureAndSave();
34
33
34+Q_SIGNALS:
35+ void screenshotSaved(const QString &filename);
36+
35private:
37private:
36− QString makeFileName();
38+ QString makeFileName() const;
37− QString getFormat();
39+ QString getFormat() const;
38 QString fileNamePrefix;
40 QString fileNamePrefix;
39− int screenshotQuality;
41+ int screenshotQuality = -1; // default quality for the format
40};
42};
41
43
42#endif
44#endif
plugins/Ubuntu/Payments/Payments.cpp−2
@@ -123,8 +123,6 @@ void Payments::setStoreItemId(const QString &store_item_id)
123 if (m_store_item_id.isEmpty()) {
123 if (m_store_item_id.isEmpty()) {
124 return;
124 return;
125 }
125 }
126−
127− pay_package_item_start_verification(m_package, m_store_item_id.toLocal8Bit().data());
128}
126}
129
127
130void Payments::setPurchasing(bool is_purchasing)
128void Payments::setPurchasing(bool is_purchasing)
plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux.cpp+2−11
@@ -101,8 +101,6 @@ void QInputDeviceInfoPrivate::init()
101 if (!iDevice)
101 if (!iDevice)
102 continue;
102 continue;
103
103
104− qDebug() << "*** ADDING DEVICE" << eventPath;
105−
106 iDevice->setTypes(getInputTypes(dev));
104 iDevice->setTypes(getInputTypes(dev));
107
105
108 if (iDevice->switches().count() > 0 && iDevice->buttons().count() == 0)
106 if (iDevice->switches().count() > 0 && iDevice->buttons().count() == 0)
@@ -125,12 +123,6 @@ void QInputDeviceInfoPrivate::init()
125
123
126QInputDeviceInfo::InputTypes QInputDeviceInfoPrivate::getInputTypes( struct udev_device *dev)
124QInputDeviceInfo::InputTypes QInputDeviceInfoPrivate::getInputTypes( struct udev_device *dev)
127{
125{
128− qDebug() << "******* have input type Keyboard" << udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
129− qDebug() << "******* have input type Mouse" << udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
130− qDebug() << "******* have input type Touchpad" << udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD");
131− qDebug() << "******* have input type Touchscreen" << udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
132− qDebug() << "******* have input type Tablet" << udev_device_get_property_value(dev, "ID_INPUT_TABLET");
133− qDebug() << "******* have input type joystick" << udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
134 QInputDeviceInfo::InputTypes types = QInputDeviceInfo::Unknown;
126 QInputDeviceInfo::InputTypes types = QInputDeviceInfo::Unknown;
135 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"), "1") == 0 )
127 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"), "1") == 0 )
136 types |= QInputDeviceInfo::Keyboard;
128 types |= QInputDeviceInfo::Keyboard;
@@ -151,6 +143,7 @@ QInputDeviceInfo::InputTypes QInputDeviceInfoPrivate::getInputTypes( struct udev
151QInputDevice *QInputDeviceInfoPrivate::addDevice(const QString &path)
143QInputDevice *QInputDeviceInfoPrivate::addDevice(const QString &path)
152{
144{
153 QInputDevice *inputDevice = new QInputDevice(this);
145 QInputDevice *inputDevice = new QInputDevice(this);
146+ inputDevice->setDevicePath(path);
154
147
155 struct libevdev *dev = NULL;
148 struct libevdev *dev = NULL;
156 int fd;
149 int fd;
@@ -158,17 +151,15 @@ QInputDevice *QInputDeviceInfoPrivate::addDevice(const QString &path)
158 fd = open(path.toLatin1(), O_RDONLY|O_NONBLOCK);
151 fd = open(path.toLatin1(), O_RDONLY|O_NONBLOCK);
159
152
160 if (fd == -1) {
153 if (fd == -1) {
161− qDebug() << "Failed to open";
162 return inputDevice;
154 return inputDevice;
163 }
155 }
164 rc = libevdev_new_from_fd(fd, &dev);
156 rc = libevdev_new_from_fd(fd, &dev);
165 if (rc < 0) {
157 if (rc < 0) {
166− qDebug() << "Failed to init libevdev ("<< strerror(-rc) << ")";
158+ qWarning() << "Failed to init libevdev ("<< strerror(-rc) << ")";
167 return inputDevice;
159 return inputDevice;
168 }
160 }
169
161
170 inputDevice->setName(QString::fromLatin1(libevdev_get_name(dev)));
162 inputDevice->setName(QString::fromLatin1(libevdev_get_name(dev)));
171− inputDevice->setDevicePath(path);
172 for (int i = 0; i < EV_MAX; i++) {
163 for (int i = 0; i < EV_MAX; i++) {
173 if (i == EV_KEY || i == EV_SW || i == EV_REL
164 if (i == EV_KEY || i == EV_SW || i == EV_REL
174 || i == EV_REL || i == EV_ABS) {
165 || i == EV_REL || i == EV_ABS) {
plugins/Unity/Launcher/CMakeLists.txt+1−2
@@ -1,5 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
3pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4
3
5add_definitions(-DSM_BUSNAME=systemBus)
4add_definitions(-DSM_BUSNAME=systemBus)
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+42−12
@@ -62,6 +62,8 @@ QString DBusInterface::introspect(const QString &path) const
62 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
62 "<interface name=\"com.canonical.Unity.Launcher.Item\">"
63 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
63 "<property name=\"count\" type=\"i\" access=\"readwrite\" />"
64 "<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\" />"
65 "</interface>";
67 "</interface>";
66 return nodeiface;
68 return nodeiface;
67}
69}
@@ -122,6 +124,20 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
122 return false;
124 return false;
123 }
125 }
124
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+
125 // First handle methods of the Launcher interface
141 // First handle methods of the Launcher interface
126 if (message.interface() == "com.canonical.Unity.Launcher") {
142 if (message.interface() == "com.canonical.Unity.Launcher") {
127 if (message.member() == "Refresh") {
143 if (message.member() == "Refresh") {
@@ -129,6 +145,13 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
129 Q_EMIT refreshCalled();
145 Q_EMIT refreshCalled();
130 return connection.send(reply);
146 return connection.send(reply);
131 }
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+ }
132 }
155 }
133
156
134 // Now handle dynamic properties (for launcher emblems)
157 // Now handle dynamic properties (for launcher emblems)
@@ -136,54 +159,61 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
136 return false;
159 return false;
137 }
160 }
138
161
139− if (message.member() != "GetAll" && message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item") {
162+ if (message.member() == "Get" && (message.arguments().count() != 2 || message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item")) {
140 return false;
163 return false;
141 }
164 }
142
165
143− /* Break down the path to just the app id */
166+ if (message.member() == "Set" && (message.arguments().count() != 3 || message.arguments()[0].toString() != "com.canonical.Unity.Launcher.Item")) {
144− QString pathtemp = message.path();
145− if (!pathtemp.startsWith("/com/canonical/Unity/Launcher/")) {
146 return false;
167 return false;
147 }
168 }
148− pathtemp.remove("/com/canonical/Unity/Launcher/");
169+
149− if (pathtemp.indexOf('/') >= 0) {
170+ if (!validpath) {
150 return false;
171 return false;
151 }
172 }
152
173
153− /* Find ourselves an appid */
154− QString appid = decodeAppId(pathtemp);
155 int index = m_launcherModel->findApplication(appid);
174 int index = m_launcherModel->findApplication(appid);
156 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
175 LauncherItem *item = static_cast<LauncherItem*>(m_launcherModel->get(index));
157
176
158 QVariantList retval;
177 QVariantList retval;
159 if (message.member() == "Get") {
178 if (message.member() == "Get") {
179+ QString cachedString = message.arguments()[1].toString();
160 if (!item) {
180 if (!item) {
161 return false;
181 return false;
162 }
182 }
163− if (message.arguments()[1].toString() == "count") {
183+ if (cachedString == "count") {
164 retval.append(QVariant::fromValue(QDBusVariant(item->count())));
184 retval.append(QVariant::fromValue(QDBusVariant(item->count())));
165− } else if (message.arguments()[1].toString() == "countVisible") {
185+ } else if (cachedString == "countVisible") {
166 retval.append(QVariant::fromValue(QDBusVariant(item->countVisible())));
186 retval.append(QVariant::fromValue(QDBusVariant(item->countVisible())));
187+ } else if (cachedString == "progress") {
188+ retval.append(QVariant::fromValue(QDBusVariant(item->progress())));
167 }
189 }
168 } else if (message.member() == "Set") {
190 } else if (message.member() == "Set") {
169− if (message.arguments()[1].toString() == "count") {
191+ QString cachedString = message.arguments()[1].toString();
192+ if (cachedString == "count") {
170 int newCount = message.arguments()[2].value<QDBusVariant>().variant().toInt();
193 int newCount = message.arguments()[2].value<QDBusVariant>().variant().toInt();
171 if (!item || newCount != item->count()) {
194 if (!item || newCount != item->count()) {
172 Q_EMIT countChanged(appid, newCount);
195 Q_EMIT countChanged(appid, newCount);
173 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "count", QVariant(newCount));
196 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "count", QVariant(newCount));
174 }
197 }
175− } else if (message.arguments()[1].toString() == "countVisible") {
198+ } else if (cachedString == "countVisible") {
176 bool newVisible = message.arguments()[2].value<QDBusVariant>().variant().toBool();
199 bool newVisible = message.arguments()[2].value<QDBusVariant>().variant().toBool();
177 if (!item || newVisible != item->countVisible()) {
200 if (!item || newVisible != item->countVisible()) {
178 Q_EMIT countVisibleChanged(appid, newVisible);
201 Q_EMIT countVisibleChanged(appid, newVisible);
179 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "countVisible", newVisible);
202 notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "countVisible", newVisible);
180 }
203 }
204+ } else if (cachedString == "progress") {
205+ int newProgress = message.arguments()[2].value<QDBusVariant>().variant().toInt();
206+ if (!item || newProgress != item->progress()) {
207+ Q_EMIT progressChanged(appid, newProgress);
208+ notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "progress", QVariant(newProgress));
209+ }
181 }
210 }
182 } else if (message.member() == "GetAll") {
211 } else if (message.member() == "GetAll") {
183 if (item) {
212 if (item) {
184 QVariantMap all;
213 QVariantMap all;
185 all.insert("count", item->count());
214 all.insert("count", item->count());
186 all.insert("countVisible", item->countVisible());
215 all.insert("countVisible", item->countVisible());
216+ all.insert("progress", item->progress());
187 retval.append(all);
217 retval.append(all);
188 }
218 }
189 } else {
219 } else {
plugins/Unity/Launcher/dbusinterface.h+2
@@ -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+77−26
@@ -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
@@ -82,11 +84,30 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
82 return item->progress();
84 return item->progress();
83 case RoleFocused:
85 case RoleFocused:
84 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();
85 }
94 }
86
95
87 return QVariant();
96 return QVariant();
88}
97}
89
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+
90unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
111unity::shell::launcher::LauncherItemInterface *LauncherModel::get(int index) const
91{
112{
92 if (index < 0 || index >= m_list.count()) {
113 if (index < 0 || index >= m_list.count()) {
@@ -134,7 +155,7 @@ void LauncherModel::pin(const QString &appId, int index)
134 if (index == -1 || index == currentIndex) {
155 if (index == -1 || index == currentIndex) {
135 m_list.at(currentIndex)->setPinned(true);
156 m_list.at(currentIndex)->setPinned(true);
136 QModelIndex modelIndex = this->index(currentIndex);
157 QModelIndex modelIndex = this->index(currentIndex);
137− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
158+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
138 } else {
159 } else {
139 move(currentIndex, index);
160 move(currentIndex, index);
140 // 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.
@@ -147,7 +168,7 @@ void LauncherModel::pin(const QString &appId, int index)
147
168
148 DesktopFileHandler desktopFile(appId);
169 DesktopFileHandler desktopFile(appId);
149 if (!desktopFile.isValid()) {
170 if (!desktopFile.isValid()) {
150− qWarning() << "Can't pin this application, there is no .destkop file available.";
171+ qWarning() << "Can't pin this application, there is no .desktop file available.";
151 return;
172 return;
152 }
173 }
153
174
@@ -172,7 +193,7 @@ void LauncherModel::requestRemove(const QString &appId)
172
193
173void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
194void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex)
174{
195{
175− int index = findApplication(appId);
196+ const int index = findApplication(appId);
176 if (index < 0) {
197 if (index < 0) {
177 return;
198 return;
178 }
199 }
@@ -180,7 +201,7 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
180 LauncherItem *item = m_list.at(index);
201 LauncherItem *item = m_list.at(index);
181 QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
202 QuickListModel *model = qobject_cast<QuickListModel*>(item->quickList());
182 if (model) {
203 if (model) {
183− QString actionId = model->get(actionIndex).actionId();
204+ const QString actionId = model->get(actionIndex).actionId();
184
205
185 // 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
186 if (actionId == "pin_item") {
207 if (actionId == "pin_item") {
@@ -191,7 +212,10 @@ void LauncherModel::quickListActionInvoked(const QString &appId, int actionIndex
191 }
212 }
192 } else if (actionId == "launch_item") {
213 } else if (actionId == "launch_item") {
193 QDesktopServices::openUrl(getUrlForAppId(appId));
214 QDesktopServices::openUrl(getUrlForAppId(appId));
194−
215+ } else if (actionId == "stop_item") { // Quit
216+ if (m_appManager) {
217+ m_appManager->stopApplication(appId);
218+ }
195 // 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
196 } else {
220 } else {
197 // TODO: forward quicklist action to app, possibly via m_dbusIface
221 // TODO: forward quicklist action to app, possibly via m_dbusIface
@@ -289,7 +313,7 @@ void LauncherModel::storeAppList()
289
313
290void LauncherModel::unpin(const QString &appId)
314void LauncherModel::unpin(const QString &appId)
291{
315{
292− int index = findApplication(appId);
316+ const int index = findApplication(appId);
293 if (index < 0) {
317 if (index < 0) {
294 return;
318 return;
295 }
319 }
@@ -298,7 +322,7 @@ void LauncherModel::unpin(const QString &appId)
298 if (m_list.at(index)->pinned()) {
322 if (m_list.at(index)->pinned()) {
299 m_list.at(index)->setPinned(false);
323 m_list.at(index)->setPinned(false);
300 QModelIndex modelIndex = this->index(index);
324 QModelIndex modelIndex = this->index(index);
301− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
325+ Q_EMIT dataChanged(modelIndex, modelIndex, {RolePinned});
302 }
326 }
303 } else {
327 } else {
304 beginRemoveRows(QModelIndex(), index, index);
328 beginRemoveRows(QModelIndex(), index, index);
@@ -320,32 +344,38 @@ int LauncherModel::findApplication(const QString &appId)
320
344
321void LauncherModel::progressChanged(const QString &appId, int progress)
345void LauncherModel::progressChanged(const QString &appId, int progress)
322{
346{
323− int idx = findApplication(appId);
347+ const int idx = findApplication(appId);
324 if (idx >= 0) {
348 if (idx >= 0) {
325 LauncherItem *item = m_list.at(idx);
349 LauncherItem *item = m_list.at(idx);
326 item->setProgress(progress);
350 item->setProgress(progress);
327− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleProgress);
351+ Q_EMIT dataChanged(index(idx), index(idx), {RoleProgress});
328 }
352 }
329}
353}
330
354
331void LauncherModel::countChanged(const QString &appId, int count)
355void LauncherModel::countChanged(const QString &appId, int count)
332{
356{
333− int idx = findApplication(appId);
357+ const int idx = findApplication(appId);
334 if (idx >= 0) {
358 if (idx >= 0) {
335 LauncherItem *item = m_list.at(idx);
359 LauncherItem *item = m_list.at(idx);
336 item->setCount(count);
360 item->setCount(count);
337− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCount);
361+ if (item->countVisible()) {
362+ setAlerting(item->appId(), true);
363+ }
338 m_asAdapter->syncItems(m_list);
364 m_asAdapter->syncItems(m_list);
365+ Q_EMIT dataChanged(index(idx), index(idx), {RoleCount});
339 }
366 }
340}
367}
341
368
342−void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
369+void LauncherModel::countVisibleChanged(const QString &appId, bool countVisible)
343{
370{
344 int idx = findApplication(appId);
371 int idx = findApplication(appId);
345 if (idx >= 0) {
372 if (idx >= 0) {
346 LauncherItem *item = m_list.at(idx);
373 LauncherItem *item = m_list.at(idx);
347 item->setCountVisible(countVisible);
374 item->setCountVisible(countVisible);
375+ if (countVisible) {
376+ setAlerting(item->appId(), true);
377+ }
348− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleCountVisible);
378+ Q_EMIT dataChanged(index(idx), index(idx), {RoleCountVisible});
349
379
350 // 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
351 if (!countVisible && !item->pinned() && !item->recent()) {
381 if (!countVisible && !item->pinned() && !item->recent()) {
@@ -364,7 +394,6 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
364 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
394 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
365 m_list.append(item);
395 m_list.append(item);
366 endInsertRows();
396 endInsertRows();
367− Q_EMIT hint();
368 }
397 }
369 }
398 }
370 m_asAdapter->syncItems(m_list);
399 m_asAdapter->syncItems(m_list);
@@ -387,7 +416,8 @@ void LauncherModel::refresh()
387 item->setName(desktopFile.displayName());
416 item->setName(desktopFile.displayName());
388 item->setIcon(desktopFile.icon());
417 item->setIcon(desktopFile.icon());
389 item->setPinned(item->pinned()); // update pinned text if needed
418 item->setPinned(item->pinned()); // update pinned text if needed
419+ item->setRunning(item->running());
390− Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleName << RoleIcon);
420+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleRunning});
391 }
421 }
392 }
422 }
393
423
@@ -405,7 +435,7 @@ void LauncherModel::refresh()
405
435
406 // Now walk through settings and see if we need to add something
436 // Now walk through settings and see if we need to add something
407 for (int settingsIndex = 0; settingsIndex < m_settings->storedApplications().count(); ++settingsIndex) {
437 for (int settingsIndex = 0; settingsIndex < m_settings->storedApplications().count(); ++settingsIndex) {
408− QString entry = m_settings->storedApplications().at(settingsIndex);
438+ const QString entry = m_settings->storedApplications().at(settingsIndex);
409 int itemIndex = -1;
439 int itemIndex = -1;
410 for (int i = 0; i < m_list.count(); ++i) {
440 for (int i = 0; i < m_list.count(); ++i) {
411 if (m_list.at(i)->appId() == entry) {
441 if (m_list.at(i)->appId() == entry) {
@@ -453,6 +483,16 @@ void LauncherModel::refresh()
453 m_asAdapter->syncItems(m_list);
483 m_asAdapter->syncItems(m_list);
454}
484}
455
485
486+void LauncherModel::alert(const QString &appId)
487+{
488+ int idx = findApplication(appId);
489+ if (idx >= 0) {
490+ LauncherItem *item = m_list.at(idx);
491+ setAlerting(item->appId(), true);
492+ Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleAlerting);
493+ }
494+}
495+
456void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
496void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
457{
497{
458 Q_UNUSED(parent);
498 Q_UNUSED(parent);
@@ -468,25 +508,26 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
468 return;
508 return;
469 }
509 }
470
510
471− int itemIndex = findApplication(app->appId());
511+ const int itemIndex = findApplication(app->appId());
472 if (itemIndex != -1) {
512 if (itemIndex != -1) {
473 LauncherItem *item = m_list.at(itemIndex);
513 LauncherItem *item = m_list.at(itemIndex);
474 if (!item->recent()) {
514 if (!item->recent()) {
475 item->setRecent(true);
515 item->setRecent(true);
476− m_asAdapter->syncItems(m_list);
477− Q_EMIT dataChanged(index(itemIndex), index(itemIndex), QVector<int>() << RoleRecent);
516+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRecent});
478 }
517 }
479− // Shall we paint some running/recent app highlight? If yes, do it here.
518+ item->setRunning(true);
480 } else {
519 } else {
481 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
520 LauncherItem *item = new LauncherItem(app->appId(), app->name(), app->icon().toString(), this);
482 item->setRecent(true);
521 item->setRecent(true);
522+ item->setRunning(true);
483 item->setFocused(app->focused());
523 item->setFocused(app->focused());
484
524
485 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
525 beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
486 m_list.append(item);
526 m_list.append(item);
487 endInsertRows();
527 endInsertRows();
488− m_asAdapter->syncItems(m_list);
489 }
528 }
529+ m_asAdapter->syncItems(m_list);
530+ Q_EMIT dataChanged(index(itemIndex), index(itemIndex), {RoleRunning});
490}
531}
491
532
492void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
533void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
@@ -501,25 +542,35 @@ void LauncherModel::applicationRemoved(const QModelIndex &parent, int row)
501 }
542 }
502 }
543 }
503
544
545+ if (appIndex < 0) {
546+ qWarning() << Q_FUNC_INFO << "appIndex not found";
547+ return;
548+ }
549+
504− if (appIndex > -1 && !m_list.at(appIndex)->pinned()) {
550+ LauncherItem * item = m_list.at(appIndex);
551+ item->setRunning(false);
552+
553+ if (!item->pinned()) {
505 beginRemoveRows(QModelIndex(), appIndex, appIndex);
554 beginRemoveRows(QModelIndex(), appIndex, appIndex);
506 m_list.takeAt(appIndex)->deleteLater();
555 m_list.takeAt(appIndex)->deleteLater();
507 endRemoveRows();
556 endRemoveRows();
508 m_asAdapter->syncItems(m_list);
557 m_asAdapter->syncItems(m_list);
558+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RolePinned});
509 }
559 }
560+ Q_EMIT dataChanged(index(appIndex), index(appIndex), {RoleRunning});
510}
561}
511
562
512void LauncherModel::focusedAppIdChanged()
563void LauncherModel::focusedAppIdChanged()
513{
564{
514− QString appId = m_appManager->focusedApplicationId();
565+ const QString appId = m_appManager->focusedApplicationId();
515 for (int i = 0; i < m_list.count(); ++i) {
566 for (int i = 0; i < m_list.count(); ++i) {
516 LauncherItem *item = m_list.at(i);
567 LauncherItem *item = m_list.at(i);
517 if (!item->focused() && item->appId() == appId) {
568 if (!item->focused() && item->appId() == appId) {
518 item->setFocused(true);
569 item->setFocused(true);
519− Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
570+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
520 } else if (item->focused() && item->appId() != appId) {
571 } else if (item->focused() && item->appId() != appId) {
521 item->setFocused(false);
572 item->setFocused(false);
522− Q_EMIT dataChanged(index(i), index(i), QVector<int>() << RoleFocused);
573+ Q_EMIT dataChanged(index(i), index(i), {RoleFocused});
523 }
574 }
524 }
575 }
525}
576}
plugins/Unity/Launcher/launchermodel.h+3−1
@@ -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();
@@ -71,7 +73,7 @@ private:
71
73
72private Q_SLOTS:
74private Q_SLOTS:
73 void countChanged(const QString &appId, int count);
75 void countChanged(const QString &appId, int count);
74− void countVisibleChanged(const QString &appId, int count);
76+ void countVisibleChanged(const QString &appId, bool count);
75 void progressChanged(const QString &appId, int progress);
77 void progressChanged(const QString &appId, int progress);
76
78
77 void applicationAdded(const QModelIndex &parent, int row);
79 void applicationAdded(const QModelIndex &parent, int row);
plugins/Unity/Launcher/quicklistentry.cpp+6−2
@@ -1,4 +1,4 @@
1−/* Copyright (C) 2013 Canonical, Ltd.
1+/* Copyright (C) 2013, 2015 Canonical, Ltd.
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/CMakeLists.txt+1
@@ -2,6 +2,7 @@ pkg_search_module(GIO REQUIRED gio-2.0)
2
2
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
5+ ${CMAKE_CURRENT_BINARY_DIR}
5 ${GIO_INCLUDE_DIRS}
6 ${GIO_INCLUDE_DIRS}
6 ${libunity8-private_SOURCE_DIR}
7 ${libunity8-private_SOURCE_DIR}
7)
8)
plugins/Unity/Session/dbusunitysessionservice.cpp+372−24
@@ -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 it under
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -17,17 +17,186 @@
17// local
17// local
18#include "dbusunitysessionservice.h"
18#include "dbusunitysessionservice.h"
19
19
20+// system
21+#include <sys/types.h>
22+#include <unistd.h>
23+#include <pwd.h>
24+
20// Qt
25// Qt
26+#include <QDebug>
21#include <QDBusPendingCall>
27#include <QDBusPendingCall>
28+#include <QDBusReply>
29+#include <QElapsedTimer>
30+#include <QDateTime>
31+
32+#define LOGIN1_SERVICE QStringLiteral("org.freedesktop.login1")
33+#define LOGIN1_PATH QStringLiteral("/org/freedesktop/login1")
34+#define LOGIN1_IFACE QStringLiteral("org.freedesktop.login1.Manager")
35+#define LOGIN1_SESSION_IFACE QStringLiteral("org.freedesktop.login1.Session")
36+
37+#define ACTIVE_KEY QStringLiteral("Active")
38+#define IDLE_SINCE_KEY QStringLiteral("IdleSinceHint")
39+
40+class DBusUnitySessionServicePrivate: public QObject
41+{
42+ Q_OBJECT
43+public:
44+ QString logindSessionPath;
45+ bool isSessionActive = true;
46+ QElapsedTimer screensaverActiveTimer;
47+
48+ DBusUnitySessionServicePrivate(): QObject() {
49+ init();
50+ checkActive();
51+ }
52+
53+ void init()
54+ {
55+ // get our logind session path
56+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
57+ LOGIN1_PATH,
58+ LOGIN1_IFACE,
59+ "GetSessionByPID");
60+ msg << (quint32) getpid();
61+
62+ QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().asyncCall(msg);
63+ if (reply.isValid()) {
64+ logindSessionPath = reply.value().path();
65+
66+ // start watching the Active property
67+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, "org.freedesktop.DBus.Properties", "PropertiesChanged",
68+ this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
69+ } else {
70+ qWarning() << "Failed to get logind session path" << reply.error().message();
71+ }
72+ }
73+
74+ bool checkLogin1Call(const QString &method) const
75+ {
76+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
77+ QDBusReply<QString> reply = QDBusConnection::systemBus().asyncCall(msg);
78+ return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
79+ }
80+
81+ void makeLogin1Call(const QString &method, const QVariantList &args)
82+ {
83+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
84+ LOGIN1_PATH,
85+ LOGIN1_IFACE,
86+ method);
87+ msg.setArguments(args);
88+ QDBusConnection::systemBus().asyncCall(msg);
89+ }
90+
91+ void checkActive()
92+ {
93+ if (logindSessionPath.isEmpty()) {
94+ qWarning() << "Invalid session path";
95+ return;
96+ }
97+
98+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
99+ logindSessionPath,
100+ "org.freedesktop.DBus.Properties",
101+ "Get");
102+ msg << LOGIN1_SESSION_IFACE;
103+ msg << ACTIVE_KEY;
104+
105+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
106+ if (reply.isValid()) {
107+ isSessionActive = reply.value().toBool();
108+ qDebug() << "Session" << logindSessionPath << "is active:" << isSessionActive;
109+ } else {
110+ qWarning() << "Failed to get Active property" << reply.error().message();
111+ }
112+ }
113+
114+ quint32 screensaverActiveTime() const
115+ {
116+ if (!isSessionActive && screensaverActiveTimer.isValid()) {
117+ return screensaverActiveTimer.elapsed() / 1000;
118+ }
119+
120+ return 0;
121+ }
122+
123+ quint64 idleSinceUSecTimestamp() const
124+ {
125+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
126+ logindSessionPath,
127+ "org.freedesktop.DBus.Properties",
128+ "Get");
129+ msg << LOGIN1_SESSION_IFACE;
130+ msg << IDLE_SINCE_KEY;
131+
132+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
133+ if (reply.isValid()) {
134+ return reply.value().value<quint64>();
135+ } else {
136+ qWarning() << "Failed to get IdleSinceHint property" << reply.error().message();
137+ }
138+
139+ return 0;
140+ }
141+
142+ void setIdleHint(bool idle)
143+ {
144+ QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE,
145+ logindSessionPath,
146+ LOGIN1_SESSION_IFACE,
147+ "SetIdleHint");
148+ msg << idle;
149+ QDBusConnection::systemBus().asyncCall(msg);
150+ }
151+
152+private Q_SLOTS:
153+ void onPropertiesChanged(const QString &iface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
154+ {
155+ Q_UNUSED(iface)
156+
157+ if (changedProps.contains(ACTIVE_KEY) || invalidatedProps.contains(ACTIVE_KEY)) {
158+ if (changedProps.value(ACTIVE_KEY).isValid()) {
159+ isSessionActive = changedProps.value(ACTIVE_KEY).toBool();
160+ } else {
161+ checkActive();
162+ }
163+
164+ Q_EMIT screensaverActiveChanged(!isSessionActive);
165+
166+ if (isSessionActive) {
167+ screensaverActiveTimer.invalidate();
168+ setIdleHint(false);
169+ } else {
170+ screensaverActiveTimer.start();
171+ setIdleHint(true);
172+ }
173+ }
174+ }
175+
176+Q_SIGNALS:
177+ void screensaverActiveChanged(bool active);
178+};
179+
180+Q_GLOBAL_STATIC(DBusUnitySessionServicePrivate, d)
22
181
23DBusUnitySessionService::DBusUnitySessionService()
182DBusUnitySessionService::DBusUnitySessionService()
24 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
183 : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
25{
184{
185+ if (!d->logindSessionPath.isEmpty()) {
186+ // connect our Lock() slot to the logind's session Lock() signal
187+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Lock", this, SLOT(Lock()));
188+ // ... and our Unlocked() signal to the logind's session Unlock() signal
189+ // (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
190+ QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, "Unlock", this, SIGNAL(Unlocked()));
191+ } else {
192+ qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
193+ }
26}
194}
27
195
28void DBusUnitySessionService::Logout()
196void DBusUnitySessionService::Logout()
29{
197{
30 // TODO ask the apps to quit and then emit the signal
198 // TODO ask the apps to quit and then emit the signal
199+ Q_EMIT LogoutReady();
31 Q_EMIT logoutReady();
200 Q_EMIT logoutReady();
32}
201}
33
202
@@ -40,38 +209,139 @@ void DBusUnitySessionService::EndSession()
40 QDBusConnection::sessionBus().asyncCall(msg);
209 QDBusConnection::sessionBus().asyncCall(msg);
41}
210}
42
211
212+bool DBusUnitySessionService::CanHibernate() const
213+{
214+ return d->checkLogin1Call("CanHibernate");
215+}
216+
217+bool DBusUnitySessionService::CanSuspend() const
218+{
219+ return d->checkLogin1Call("CanSuspend");
220+}
221+
222+bool DBusUnitySessionService::CanHybridSleep() const
223+{
224+ return d->checkLogin1Call("CanHybridSleep");
225+}
226+
227+bool DBusUnitySessionService::CanReboot() const
228+{
229+ return d->checkLogin1Call("CanReboot");
230+}
231+
232+bool DBusUnitySessionService::CanShutdown() const
233+{
234+ return d->checkLogin1Call("CanPowerOff");
235+}
236+
237+bool DBusUnitySessionService::CanLock() const
238+{
239+ return true; // FIXME
240+}
241+
242+QString DBusUnitySessionService::UserName() const
243+{
244+ struct passwd *p = getpwuid(geteuid());
245+ if (p) {
246+ return QString::fromUtf8(p->pw_name);
247+ }
248+
249+ return QString();
250+}
251+
252+QString DBusUnitySessionService::RealName() const
253+{
254+ struct passwd *p = getpwuid(geteuid());
255+ if (p) {
256+ const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
257+ if (!gecos.isEmpty()) {
258+ return gecos.split(QLatin1Char(',')).first();
259+ }
260+ }
261+
262+ return QString();
263+}
264+
265+QString DBusUnitySessionService::HostName() const
266+{
267+ char hostName[512];
268+ if (gethostname(hostName, sizeof(hostName)) == -1) {
269+ qWarning() << "Could not determine local hostname";
270+ return QString();
271+ }
272+ hostName[sizeof(hostName) - 1] = '\0';
273+ return QString::fromLocal8Bit(hostName);
274+}
275+
276+void DBusUnitySessionService::PromptLock()
277+{
278+ Q_EMIT LockRequested();
279+ Q_EMIT lockRequested();
280+}
281+
282+void DBusUnitySessionService::Lock()
283+{
284+ // lock the session using the org.freedesktop.DisplayManager system DBUS service
285+ const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
286+ QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.DisplayManager",
287+ sessionPath,
288+ "org.freedesktop.DisplayManager.Session",
289+ "Lock");
290+ qDebug() << "Locking session" << msg.path();
291+ QDBusReply<void> reply = QDBusConnection::systemBus().asyncCall(msg);
292+ if (!reply.isValid()) {
293+ qWarning() << "Lock call failed" << reply.error().message();
294+ } else {
295+ // emit Locked when the call succeeds
296+ Q_EMIT Locked();
297+ }
298+}
299+
300+bool DBusUnitySessionService::IsLocked() const
301+{
302+ return !d->isSessionActive;
303+}
304+
43void DBusUnitySessionService::RequestLogout()
305void DBusUnitySessionService::RequestLogout()
44{
306{
307+ Q_EMIT LogoutRequested(false);
45 Q_EMIT logoutRequested(false);
308 Q_EMIT logoutRequested(false);
46}
309}
47
310
48void DBusUnitySessionService::Reboot()
311void DBusUnitySessionService::Reboot()
49{
312{
50− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
313+ d->makeLogin1Call("Reboot", {false});
51− "/org/freedesktop/login1",
52− "org.freedesktop.login1.Manager",
53− "Reboot");
54− msg << false;
55− QDBusConnection::systemBus().asyncCall(msg);
56}
314}
57
315
58void DBusUnitySessionService::RequestReboot()
316void DBusUnitySessionService::RequestReboot()
59{
317{
318+ Q_EMIT RebootRequested(false);
60 Q_EMIT rebootRequested(false);
319 Q_EMIT rebootRequested(false);
61}
320}
62
321
63void DBusUnitySessionService::Shutdown()
322void DBusUnitySessionService::Shutdown()
64{
323{
65− QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.login1",
66− "/org/freedesktop/login1",
67− "org.freedesktop.login1.Manager",
68− "PowerOff");
324+ d->makeLogin1Call("PowerOff", {false});
69− msg << false;
325+}
70− QDBusConnection::systemBus().asyncCall(msg);
326+
327+void DBusUnitySessionService::Suspend()
328+{
329+ d->makeLogin1Call("Suspend", {false});
330+}
331+
332+void DBusUnitySessionService::Hibernate()
333+{
334+ d->makeLogin1Call("Hibernate", {false});
335+}
336+
337+void DBusUnitySessionService::HybridSleep()
338+{
339+ d->makeLogin1Call("HybridSleep", {false});
71}
340}
72
341
73void DBusUnitySessionService::RequestShutdown()
342void DBusUnitySessionService::RequestShutdown()
74{
343{
344+ Q_EMIT ShutdownRequested(false);
75 Q_EMIT shutdownRequested(false);
345 Q_EMIT shutdownRequested(false);
76}
346}
77
347
@@ -83,12 +353,8 @@ enum class Action : unsigned
83 NONE
353 NONE
84};
354};
85
355
86−DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
87− : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
88−{
89−}
90
356
91−void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
357+void performAsyncUnityCall(const QString &method)
92{
358{
93 const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
359 const QDBusMessage msg = QDBusMessage::createMethodCall("com.canonical.Unity",
94 "/com/canonical/Unity/Session",
360 "/com/canonical/Unity/Session",
@@ -97,29 +363,111 @@ void DBusGnomeSessionManagerWrapper::performAsyncCall(const QString &method)
97 QDBusConnection::sessionBus().asyncCall(msg);
363 QDBusConnection::sessionBus().asyncCall(msg);
98}
364}
99
365
366+
367+DBusGnomeSessionManagerWrapper::DBusGnomeSessionManagerWrapper()
368+ : UnityDBusObject("/org/gnome/SessionManager/EndSessionDialog", "com.canonical.Unity")
369+{
370+}
371+
100void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
372void DBusGnomeSessionManagerWrapper::Open(const unsigned type, const unsigned arg_1, const unsigned max_wait, const QList<QDBusObjectPath> &inhibitors)
101{
373{
102 Q_UNUSED(arg_1);
374 Q_UNUSED(arg_1);
103 Q_UNUSED(max_wait);
375 Q_UNUSED(max_wait);
104 Q_UNUSED(inhibitors);
376 Q_UNUSED(inhibitors);
105
377
106− const Action action = (Action)type;
378+ switch (static_cast<Action>(type))
107−
108− switch (action)
109 {
379 {
110 case Action::LOGOUT:
380 case Action::LOGOUT:
111− performAsyncCall("RequestLogout");
381+ performAsyncUnityCall("RequestLogout");
112 break;
382 break;
113
383
114 case Action::REBOOT:
384 case Action::REBOOT:
115− performAsyncCall("RequestReboot");
385+ performAsyncUnityCall("RequestReboot");
116 break;
386 break;
117
387
118 case Action::SHUTDOWN:
388 case Action::SHUTDOWN:
119− performAsyncCall("RequestShutdown");
389+ performAsyncUnityCall("RequestShutdown");
120 break;
390 break;
121
391
122 default:
392 default:
123 break;
393 break;
124 }
394 }
125}
395}
396+
397+
398+DBusGnomeScreensaverWrapper::DBusGnomeScreensaverWrapper()
399+ : UnityDBusObject("/org/gnome/ScreenSaver", "org.gnome.ScreenSaver")
400+{
401+ connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusGnomeScreensaverWrapper::ActiveChanged);
402+}
403+
404+bool DBusGnomeScreensaverWrapper::GetActive() const
405+{
406+ return !d->isSessionActive; // return whether the session is not active
407+}
408+
409+void DBusGnomeScreensaverWrapper::SetActive(bool lock)
410+{
411+ if (lock) {
412+ Lock();
413+ }
414+}
415+
416+void DBusGnomeScreensaverWrapper::Lock()
417+{
418+ performAsyncUnityCall("Lock");
419+}
420+
421+quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
422+{
423+ return d->screensaverActiveTime();
424+}
425+
426+void DBusGnomeScreensaverWrapper::SimulateUserActivity()
427+{
428+ d->setIdleHint(false);
429+}
430+
431+
432+DBusScreensaverWrapper::DBusScreensaverWrapper()
433+ : UnityDBusObject("/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver")
434+{
435+ QDBusConnection::sessionBus().registerObject("/ScreenSaver", this, QDBusConnection::ExportScriptableContents); // compat path, also register here
436+ connect(d, &DBusUnitySessionServicePrivate::screensaverActiveChanged, this, &DBusScreensaverWrapper::ActiveChanged);
437+}
438+
439+bool DBusScreensaverWrapper::GetActive() const
440+{
441+ return !d->isSessionActive; // return whether the session is not active
442+}
443+
444+bool DBusScreensaverWrapper::SetActive(bool lock)
445+{
446+ if (lock) {
447+ Lock();
448+ return true;
449+ }
450+ return false;
451+}
452+
453+void DBusScreensaverWrapper::Lock()
454+{
455+ performAsyncUnityCall("Lock");
456+}
457+
458+quint32 DBusScreensaverWrapper::GetActiveTime() const
459+{
460+ return d->screensaverActiveTime();
461+}
462+
463+quint32 DBusScreensaverWrapper::GetSessionIdleTime() const
464+{
465+ return QDateTime::fromMSecsSinceEpoch(d->idleSinceUSecTimestamp()/1000).secsTo(QDateTime::currentDateTime());
466+}
467+
468+void DBusScreensaverWrapper::SimulateUserActivity()
469+{
470+ d->setIdleHint(false);
471+}
472+
473+#include "dbusunitysessionservice.moc"
plugins/Unity/Session/dbusunitysessionservice.h+200−16
@@ -1,5 +1,5 @@
1−/*
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 it under
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -29,7 +29,7 @@ Q_DECLARE_METATYPE(QList<QDBusObjectPath>)
29 * interface.
29 * interface.
30 *
30 *
31 * com.canonical.Unity.Session interface provides public methods
31 * com.canonical.Unity.Session interface provides public methods
32− * and signals to handle Logout/Reboot/Shutdown.
32+ * and signals to handle eg. Logout/Reboot/Shutdown.
33 */
33 */
34class DBusUnitySessionService : public UnityDBusObject
34class DBusUnitySessionService : public UnityDBusObject
35{
35{
@@ -46,46 +46,67 @@ public:
46 Q_INVOKABLE void shutdown() { Shutdown(); }
46 Q_INVOKABLE void shutdown() { Shutdown(); }
47 Q_INVOKABLE void endSession() { EndSession(); }
47 Q_INVOKABLE void endSession() { EndSession(); }
48
48
49+ // TODO: remove duplicate signals and split D-Bus and QML API's
50+ // Apparently QML needs the signals in lowercase, while DBUS spec needs the uppercase version
49Q_SIGNALS:
51Q_SIGNALS:
50 /**
52 /**
51− * logoutRequested signal
53+ * LogoutRequested signal
52 *
54 *
53 * This signal is emitted when some applications request the system to
55 * This signal is emitted when some applications request the system to
54 * logout.
56 * logout.
55 * @param have_inhibitors if there are any special running applications
57 * @param have_inhibitors if there are any special running applications
56 * which inhibit the logout.
58 * which inhibit the logout.
57 */
59 */
60+ Q_SCRIPTABLE void LogoutRequested(bool have_inhibitors);
58− Q_SCRIPTABLE void logoutRequested(bool have_inhibitors);
61+ void logoutRequested(bool have_inhibitors);
59
62
60 /**
63 /**
61− * rebootRequested signal
64+ * RebootRequested signal
62 *
65 *
63 * This signal is emitted when some applications request the system to
66 * This signal is emitted when some applications request the system to
64 * reboot.
67 * reboot.
65 * @param have_inhibitors if there are any special running applications
68 * @param have_inhibitors if there are any special running applications
66 * which inhibit the reboot.
69 * which inhibit the reboot.
67 */
70 */
71+ Q_SCRIPTABLE void RebootRequested(bool have_inhibitors);
68− Q_SCRIPTABLE void rebootRequested(bool have_inhibitors);
72+ void rebootRequested(bool have_inhibitors);
69
73
70 /**
74 /**
71− * shutdownRequested signal
75+ * ShutdownRequested signal
72 *
76 *
73 * This signal is emitted when some applications request the system to
77 * This signal is emitted when some applications request the system to
74 * shutdown.
78 * shutdown.
75 * @param have_inhibitors if there are any special running applications
79 * @param have_inhibitors if there are any special running applications
76 * which inhibit the shutdown.
80 * which inhibit the shutdown.
77 */
81 */
82+ Q_SCRIPTABLE void ShutdownRequested(bool have_inhibitors);
78− Q_SCRIPTABLE void shutdownRequested(bool have_inhibitors);
83+ void shutdownRequested(bool have_inhibitors);
79−
80
84
81 /**
85 /**
82− * logoutReady signal
86+ * LogoutReady signal
83 *
87 *
84 * This signal is emitted when all the apps are closed. And the system
88 * This signal is emitted when all the apps are closed. And the system
85 * is safe to logout.
89 * is safe to logout.
86 */
90 */
91+ Q_SCRIPTABLE void LogoutReady();
87 void logoutReady();
92 void logoutReady();
88
93
94+ /**
95+ * Emitted as a result of calling PromptLock()
96+ */
97+ Q_SCRIPTABLE void LockRequested();
98+ void lockRequested();
99+
100+ /**
101+ * Emitted after the session has been locked.
102+ */
103+ Q_SCRIPTABLE void Locked();
104+
105+ /**
106+ * Emitted after the session has been unlocked.
107+ */
108+ Q_SCRIPTABLE void Unlocked();
109+
89public Q_SLOTS:
110public Q_SLOTS:
90 /**
111 /**
91 * Logout the system.
112 * Logout the system.
@@ -114,10 +135,33 @@ public Q_SLOTS:
114 */
135 */
115 Q_SCRIPTABLE void Shutdown();
136 Q_SCRIPTABLE void Shutdown();
116
137
138+ /**
139+ * Suspend the system
140+ *
141+ * This method puts the system into sleep without user's confirmation.
142+ */
143+ Q_SCRIPTABLE void Suspend();
144+
145+ /**
146+ * Hibernate the system
147+ *
148+ * This method puts the system into hibernation without user's confirmation.
149+ */
150+ Q_SCRIPTABLE void Hibernate();
151+
152+ /**
153+ * Hybrid sleep
154+ *
155+ * This method puts the system into hybrid sleep without user's confirmation.
156+ *
157+ * @since unity8
158+ */
159+ Q_SCRIPTABLE void HybridSleep();
160+
117 /**
161 /**
118 * Issue a logout request.
162 * Issue a logout request.
119 *
163 *
120− * This method emits the logoutRequested signal to the shell with a boolean
164+ * This method emits the LogoutRequested signal to the shell with a boolean
121 * which indicates if there's any inhibitors. The shell should receive
165 * 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
166 * 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.
167 * action. If confirmed, shell can call Logout() method to logout.
@@ -127,21 +171,21 @@ public Q_SLOTS:
127 /**
171 /**
128 * Issue a reboot request.
172 * Issue a reboot request.
129 *
173 *
130− * This method emits the rebootRequested signal to the shell with a boolean
174+ * This method emits the RebootRequested signal to the shell with a boolean
131 * which indicates if there's any inhibitors. The shell should receive
175 * 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
176 * this signal and display a dialog to ask the user to confirm the reboot
133− * action. If confirmed, shell can call Reboot() method to reboot
177+ * action. If confirmed, shell can call Reboot() method to reboot.
134 */
178 */
135 Q_SCRIPTABLE void RequestReboot();
179 Q_SCRIPTABLE void RequestReboot();
136
180
137 /**
181 /**
138 * Issue a shutdown request.
182 * Issue a shutdown request.
139 *
183 *
140− * This method emits the shutdownRequested signal to the shell with a
184+ * This method emits the ShutdownRequested signal to the shell with a
141 * boolean which indicates if there's any inhibitors.
185 * boolean which indicates if there's any inhibitors.
142 * The shell should receive
186 * The shell should receive
143 * this signal and display a dialog to ask the user to confirm the reboot
187 * this signal and display a dialog to ask the user to confirm the reboot
144− * action. If confirmed, shell can call Shutdown() method to reboot
188+ * action. If confirmed, shell can call Shutdown() method to shutdown.
145 */
189 */
146 Q_SCRIPTABLE void RequestShutdown();
190 Q_SCRIPTABLE void RequestShutdown();
147
191
@@ -152,6 +196,68 @@ public Q_SLOTS:
152 * current DBus session bus.
196 * current DBus session bus.
153 */
197 */
154 Q_SCRIPTABLE void EndSession();
198 Q_SCRIPTABLE void EndSession();
199+
200+ /**
201+ * @return whether the system is capable of hibernating
202+ */
203+ Q_SCRIPTABLE bool CanHibernate() const;
204+
205+ /**
206+ * @return whether the system is capable of suspending
207+ */
208+ Q_SCRIPTABLE bool CanSuspend() const;
209+
210+ /**
211+ * @return whether the system is capable of hybrid sleep
212+ * @since unity8
213+ */
214+ Q_SCRIPTABLE bool CanHybridSleep() const;
215+
216+ /**
217+ * @return whether the system is capable of rebooting
218+ * @since unity8
219+ */
220+ Q_SCRIPTABLE bool CanReboot() const;
221+
222+ /**
223+ * @return whether the system is capable of shutting down
224+ */
225+ Q_SCRIPTABLE bool CanShutdown() const;
226+
227+ /**
228+ * @return whether the system is capable of locking the session
229+ */
230+ Q_SCRIPTABLE bool CanLock() const;
231+
232+ /**
233+ * @return the login name of the current user
234+ */
235+ Q_SCRIPTABLE QString UserName() const;
236+
237+ /**
238+ * @return the real name of the current user
239+ */
240+ Q_SCRIPTABLE QString RealName() const;
241+
242+ /**
243+ * @return the local hostname
244+ */
245+ Q_SCRIPTABLE QString HostName() const;
246+
247+ /**
248+ * Request that the session get locked, emits signal LockRequested()
249+ */
250+ Q_SCRIPTABLE void PromptLock();
251+
252+ /**
253+ * Locks the session immediately
254+ */
255+ Q_SCRIPTABLE void Lock();
256+
257+ /**
258+ * @return whether the session is currently locked
259+ */
260+ Q_SCRIPTABLE bool IsLocked() const;
155};
261};
156
262
157class DBusGnomeSessionManagerWrapper : public UnityDBusObject
263class DBusGnomeSessionManagerWrapper : public UnityDBusObject
@@ -169,4 +275,82 @@ public Q_SLOTS:
169private:
275private:
170 void performAsyncCall(const QString &method);
276 void performAsyncCall(const QString &method);
171};
277};
278+
279+class DBusGnomeScreensaverWrapper: public UnityDBusObject
280+{
281+ Q_OBJECT
282+ Q_CLASSINFO("D-Bus Interface", "org.gnome.ScreenSaver")
283+
284+public:
285+ DBusGnomeScreensaverWrapper();
286+ ~DBusGnomeScreensaverWrapper() = default;
287+
288+public Q_SLOTS:
289+ /**
290+ * @return whether the session is currently locked (screensaver is on)
291+ */
292+ Q_SCRIPTABLE bool GetActive() const;
293+
294+ /**
295+ * Locks the session immediately if @p lock is true
296+ */
297+ Q_SCRIPTABLE void SetActive(bool lock);
298+
299+ /**
300+ * Locks the session immediately
301+ */
302+ Q_SCRIPTABLE void Lock();
303+
304+ /**
305+ * @return the number of seconds elapsed since the screensaver had been active
306+ */
307+ Q_SCRIPTABLE quint32 GetActiveTime() const;
308+
309+ Q_SCRIPTABLE void SimulateUserActivity();
310+
311+Q_SIGNALS:
312+ void ActiveChanged(bool active);
313+};
314+
315+class DBusScreensaverWrapper: public UnityDBusObject
316+{
317+ Q_OBJECT
318+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.ScreenSaver")
319+
320+public:
321+ DBusScreensaverWrapper();
322+ ~DBusScreensaverWrapper() = default;
323+
324+public Q_SLOTS:
325+ /**
326+ * @return whether the session is currently locked (screensaver is on)
327+ */
328+ Q_SCRIPTABLE bool GetActive() const;
329+
330+ /**
331+ * Locks the session immediately if @p lock is true
332+ */
333+ Q_SCRIPTABLE bool SetActive(bool lock);
334+
335+ /**
336+ * Locks the session immediately
337+ */
338+ Q_SCRIPTABLE void Lock();
339+
340+ /**
341+ * @return the number of seconds elapsed since the screensaver had been active
342+ */
343+ Q_SCRIPTABLE quint32 GetActiveTime() const;
344+
345+ /**
346+ * @return the number of seconds that this session has been idle
347+ */
348+ Q_SCRIPTABLE quint32 GetSessionIdleTime() const;
349+
350+ Q_SCRIPTABLE void SimulateUserActivity();
351+
352+Q_SIGNALS:
353+ void ActiveChanged(bool active);
354+};
355+
172#endif // DBUSUNITYSESSIONSERVICE_H
356#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Unity/Session/plugin.cpp+2
@@ -27,6 +27,8 @@
27static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
27static QObject *dbusunitysessionservice_provider(QQmlEngine */*engine*/, QJSEngine */*jsEngine*/)
28{
28{
29 new DBusGnomeSessionManagerWrapper();
29 new DBusGnomeSessionManagerWrapper();
30+ new DBusGnomeScreensaverWrapper();
31+ new DBusScreensaverWrapper();
30 return new DBusUnitySessionService();
32 return new DBusUnitySessionService();
31}
33}
32
34
plugins/Utils/CMakeLists.txt+1
@@ -4,6 +4,7 @@ include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_BINARY_DIR}
5 ${CMAKE_CURRENT_BINARY_DIR}
6 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
6 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
7 ${GIO_INCLUDE_DIRS}
8 ${GIO_INCLUDE_DIRS}
8)
9)
9
10
po/am.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ar.po+2−2
@@ -16,8 +16,8 @@ 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-30 06:14+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17646)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/ast.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/az.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/be.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/bg.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/br.po+5−5
@@ -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-07-17 10:10+0200\n"
10"POT-Creation-Date: 2015-07-17 10:10+0200\n"
11−"PO-Revision-Date: 2015-07-04 13:51+0000\n"
11+"PO-Revision-Date: 2015-08-25 14:12+0000\n"
12−"Last-Translator: Rouxel <vincent.rouxel@mailoo.org>\n"
12+"Last-Translator: Fohanno Thierry <thierry.fohanno@ofis-bzh.org>\n"
13"Language-Team: Breton <br@li.org>\n"
13"Language-Team: Breton <br@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-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-26 06:12+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -374,7 +374,7 @@ msgstr "Siwazh, frazenn-dremen direizh"
374#: qml/Greeter/NarrowView.qml:121
374#: qml/Greeter/NarrowView.qml:121
375msgctxt "passphrase"
375msgctxt "passphrase"
376msgid "Please re-enter"
376msgid "Please re-enter"
377−msgstr ""
377+msgstr "Adverkañ, mar plij"
378
378
379#: qml/Greeter/NarrowView.qml:122
379#: qml/Greeter/NarrowView.qml:122
380msgid "Sorry, incorrect passcode"
380msgid "Sorry, incorrect passcode"
po/bs.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/ca.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -376,7 +376,7 @@ msgstr "La contrasenya no és correcta"
376#: qml/Greeter/NarrowView.qml:121
376#: qml/Greeter/NarrowView.qml:121
377msgctxt "passphrase"
377msgctxt "passphrase"
378msgid "Please re-enter"
378msgid "Please re-enter"
379−msgstr ""
379+msgstr "Torneu-ho a introduir"
380
380
381#: qml/Greeter/NarrowView.qml:122
381#: qml/Greeter/NarrowView.qml:122
382msgid "Sorry, incorrect passcode"
382msgid "Sorry, incorrect passcode"
po/ca@valencia.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ce.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ckb.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/cs.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
17"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20"Language: cs\n"
20"Language: cs\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
po/cy.po+2−2
@@ -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 ? 0 : n==2 ? 1 : (n != 8 && n != 11) ? "
17"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==2 ? 1 : (n != 8 && n != 11) ? "
18"2 : 3;\n"
18"2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-08-01 05:45+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
20−"X-Generator: Launchpad (build 17656)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/da.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Transfer-Encoding: 8bit\n"
17"Content-Transfer-Encoding: 8bit\n"
18"Plural-Forms: nplurals=2; plural=n != 1;\n"
18"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/de.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/el.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -376,7 +376,7 @@ msgstr "Συγνώμη, μη έγκυρο συνθηματικό"
376#: qml/Greeter/NarrowView.qml:121
376#: qml/Greeter/NarrowView.qml:121
377msgctxt "passphrase"
377msgctxt "passphrase"
378msgid "Please re-enter"
378msgid "Please re-enter"
379−msgstr ""
379+msgstr "Παρακαλώ πληκτρολογήστε ξανά"
380
380
381#: qml/Greeter/NarrowView.qml:122
381#: qml/Greeter/NarrowView.qml:122
382msgid "Sorry, incorrect passcode"
382msgid "Sorry, incorrect passcode"
po/en_AU.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/en_GB.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -370,7 +370,7 @@ msgstr "Sorry, incorrect passphrase"
370#: qml/Greeter/NarrowView.qml:121
370#: qml/Greeter/NarrowView.qml:121
371msgctxt "passphrase"
371msgctxt "passphrase"
372msgid "Please re-enter"
372msgid "Please re-enter"
373−msgstr ""
373+msgstr "Please re-enter"
374
374
375#: qml/Greeter/NarrowView.qml:122
375#: qml/Greeter/NarrowView.qml:122
376msgid "Sorry, incorrect passcode"
376msgid "Sorry, incorrect passcode"
po/eo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/es.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -374,7 +374,7 @@ msgstr "La contraseña es incorrecta"
374#: qml/Greeter/NarrowView.qml:121
374#: qml/Greeter/NarrowView.qml:121
375msgctxt "passphrase"
375msgctxt "passphrase"
376msgid "Please re-enter"
376msgid "Please re-enter"
377−msgstr ""
377+msgstr "Reintroduzca"
378
378
379#: qml/Greeter/NarrowView.qml:122
379#: qml/Greeter/NarrowView.qml:122
380msgid "Sorry, incorrect passcode"
380msgid "Sorry, incorrect passcode"
po/eu.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/fa.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/fi.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/fo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/fr.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-28 05:40+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ga.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/gd.po+2−2
@@ -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-25 05:38+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21"Language: gd\n"
21"Language: gd\n"
22
22
23#: plugins/LightDM/Greeter.cpp:112
23#: plugins/LightDM/Greeter.cpp:112
po/gl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-08-02 05:36+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17656)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/gv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : (n == 2 ? 1 : 2);\n"
17"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : (n == 2 ? 1 : 2);\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/he.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/hi.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/hr.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/hu.po+7−7
@@ -8,15 +8,15 @@ 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-07-17 10:10+0200\n"
10"POT-Creation-Date: 2015-07-17 10:10+0200\n"
11−"PO-Revision-Date: 2015-08-01 06:47+0000\n"
11+"PO-Revision-Date: 2015-08-25 18:11+0000\n"
12"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
12"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
13"Language-Team: Hungarian <hu@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-08-02 05:36+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-26 06:13+0000\n"
19−"X-Generator: Launchpad (build 17656)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -117,11 +117,11 @@ msgstr "Eszköz lezárva"
117
117
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."
120−msgstr "A túl sok hibás jelmondat próbálkozás miatt le lett zárva az eszköz."
120+msgstr "A túl sok hibás jelmondat próbálkozás miatt az eszköz zárolva lett."
121
121
122#: qml/Components/DelayedLockscreen.qml:65
122#: qml/Components/DelayedLockscreen.qml:65
123msgid "You have been locked out due to too many failed passcode attempts."
123msgid "You have been locked out due to too many failed passcode attempts."
124−msgstr "A túl sok hibás jelkód próbálkozás miatt le lett zárva az eszköz."
124+msgstr "A túl sok hibás jelkód próbálkozás miatt az eszköz zárolva lett."
125
125
126#: qml/Components/DelayedLockscreen.qml:74
126#: qml/Components/DelayedLockscreen.qml:74
127#, qt-format
127#, qt-format
@@ -219,7 +219,7 @@ msgstr "Vissza a híváshoz"
219
219
220#: qml/Components/Lockscreen.qml:245
220#: qml/Components/Lockscreen.qml:245
221msgid "Emergency Call"
221msgid "Emergency Call"
222−msgstr "Vészhívás"
222+msgstr "Segélyhívás"
223
223
224#: qml/Components/Lockscreen.qml:277
224#: qml/Components/Lockscreen.qml:277
225msgid "OK"
225msgid "OK"
@@ -656,7 +656,7 @@ msgid ""
656"Privacy</b>"
656"Privacy</b>"
657msgstr ""
657msgstr ""
658"Ezt kikapcsolhatja a <b>Rendszerbeállítások</b> alatt található <b>Biztonság "
658"Ezt kikapcsolhatja a <b>Rendszerbeállítások</b> alatt található <b>Biztonság "
659−"és adatvédelem</b> alatt"
659+"és adatvédelem</b> menüpontnál"
660
660
661#: qml/Wizard/Pages/80-finished.qml:24
661#: qml/Wizard/Pages/80-finished.qml:24
662msgid "All done"
662msgid "All done"
po/hy.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ia.po+22−22
@@ -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-07-17 10:10+0200\n"
10"POT-Creation-Date: 2015-07-17 10:10+0200\n"
11−"PO-Revision-Date: 2015-06-08 18:58+0000\n"
11+"PO-Revision-Date: 2015-08-25 19:31+0000\n"
12"Last-Translator: karm <melo@carmu.com>\n"
12"Last-Translator: karm <melo@carmu.com>\n"
13"Language-Team: Interlingua <ia@li.org>\n"
13"Language-Team: Interlingua <ia@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-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-26 06:13+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -37,14 +37,14 @@ msgstr "Via-breve a levar pin"
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 "Heri"
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.
44#. en_US example: "%l:%M %p" --> "1:00 PM"
44#. en_US example: "%l:%M %p" --> "1:00 PM"
45#: plugins/Utils/relativetimeformatter.cpp:164
45#: plugins/Utils/relativetimeformatter.cpp:164
46msgid "%l:%M %p"
46msgid "%l:%M %p"
47−msgstr ""
47+msgstr "%l:%M %p"
48
48
49#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
50#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
@@ -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 "Deman %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.
@@ -60,7 +60,7 @@ msgstr ""
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.
@@ -69,7 +69,7 @@ msgstr ""
69#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 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
70#: plugins/Utils/relativetimeformatter.cpp:190
71msgid "%a %d %b %l:%M %p"
71msgid "%a %d %b %l:%M %p"
72−msgstr ""
72+msgstr "%a %d %b %l:%M %p"
73
73
74#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
75#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
@@ -77,14 +77,14 @@ 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 "Heri %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.
84#. en_US example: "%H:%M" --> "13:00"
84#. en_US example: "%H:%M" --> "13:00"
85#: plugins/Utils/relativetimeformatter.cpp:208
85#: plugins/Utils/relativetimeformatter.cpp:208
86msgid "%H:%M"
86msgid "%H:%M"
87−msgstr ""
87+msgstr "%H:%M"
88
88
89#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
90#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
@@ -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 "Deman %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.
@@ -100,7 +100,7 @@ msgstr ""
100#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
100#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
101#: plugins/Utils/relativetimeformatter.cpp:225
101#: plugins/Utils/relativetimeformatter.cpp:225
102msgid "%a %H:%M"
102msgid "%a %H:%M"
103−msgstr ""
103+msgstr "%a %H:%M"
104
104
105#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
106#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
@@ -109,7 +109,7 @@ msgstr ""
109#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
109#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
110#: plugins/Utils/relativetimeformatter.cpp:234
110#: plugins/Utils/relativetimeformatter.cpp:234
111msgid "%a %d %b %H:%M"
111msgid "%a %d %b %H:%M"
112−msgstr ""
112+msgstr "%a %d %b %H:%M"
113
113
114#: qml/Components/DelayedLockscreen.qml:49
114#: qml/Components/DelayedLockscreen.qml:49
115msgid "Device Locked"
115msgid "Device Locked"
@@ -372,7 +372,7 @@ msgstr "Displacente, phrase-contrasigno incorrecte"
372#: qml/Greeter/NarrowView.qml:121
372#: qml/Greeter/NarrowView.qml:121
373msgctxt "passphrase"
373msgctxt "passphrase"
374msgid "Please re-enter"
374msgid "Please re-enter"
375−msgstr ""
375+msgstr "Per favor re-insere"
376
376
377#: qml/Greeter/NarrowView.qml:122
377#: qml/Greeter/NarrowView.qml:122
378msgid "Sorry, incorrect passcode"
378msgid "Sorry, incorrect passcode"
@@ -398,32 +398,32 @@ msgstr "Nil es jocante"
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 hora"
402−msgstr[1] ""
402+msgstr[1] "%1 horas"
403
403
404#: qml/Panel/Indicators/MenuItemFactory.qml:772
404#: qml/Panel/Indicators/MenuItemFactory.qml:772
405#, qt-format
405#, qt-format
406msgid "%1 minute"
406msgid "%1 minute"
407msgid_plural "%1 minutes"
407msgid_plural "%1 minutes"
408−msgstr[0] ""
408+msgstr[0] "%1 minuta"
409−msgstr[1] ""
409+msgstr[1] "%1 minutas"
410
410
411#: qml/Panel/Indicators/MenuItemFactory.qml:777
411#: qml/Panel/Indicators/MenuItemFactory.qml:777
412#, qt-format
412#, qt-format
413msgid "%1 second"
413msgid "%1 second"
414msgid_plural "%1 seconds"
414msgid_plural "%1 seconds"
415−msgstr[0] ""
415+msgstr[0] "%1 secunda"
416−msgstr[1] ""
416+msgstr[1] "%1 secundas"
417
417
418#: qml/Panel/Indicators/MenuItemFactory.qml:780
418#: qml/Panel/Indicators/MenuItemFactory.qml:780
419msgid "0 seconds"
419msgid "0 seconds"
420−msgstr ""
420+msgstr "0 secundas"
421
421
422#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
422#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
423#: qml/Panel/Indicators/MenuItemFactory.qml:782
423#: qml/Panel/Indicators/MenuItemFactory.qml:782
424#, qt-format
424#, qt-format
425msgid "%1 remaining"
425msgid "%1 remaining"
426−msgstr ""
426+msgstr "%1 restante"
427
427
428#: qml/Panel/Indicators/MenuItemFactory.qml:788
428#: qml/Panel/Indicators/MenuItemFactory.qml:788
429msgid "In queue…"
429msgid "In queue…"
po/id.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/is.po+2−2
@@ -17,8 +17,8 @@ msgstr ""
17"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Type: text/plain; charset=UTF-8\n"
18"Content-Transfer-Encoding: 8bit\n"
18"Content-Transfer-Encoding: 8bit\n"
19"Plural-Forms: nplurals=2; plural=n != 1;\n"
19"Plural-Forms: nplurals=2; plural=n != 1;\n"
20−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
20+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
21−"X-Generator: Launchpad (build 17628)\n"
21+"X-Generator: Launchpad (build 17690)\n"
22"Language: is\n"
22"Language: is\n"
23
23
24#: plugins/LightDM/Greeter.cpp:112
24#: plugins/LightDM/Greeter.cpp:112
po/it.po+3−3
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -374,7 +374,7 @@ msgstr "Passphrase errata"
374#: qml/Greeter/NarrowView.qml:121
374#: qml/Greeter/NarrowView.qml:121
375msgctxt "passphrase"
375msgctxt "passphrase"
376msgid "Please re-enter"
376msgid "Please re-enter"
377−msgstr ""
377+msgstr "Inserire nuovamente"
378
378
379#: qml/Greeter/NarrowView.qml:122
379#: qml/Greeter/NarrowView.qml:122
380msgid "Sorry, incorrect passcode"
380msgid "Sorry, incorrect passcode"
po/ja.po+5−5
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -192,7 +192,7 @@ msgstr "はい"
192#: qml/Components/Dialogs.qml:152
192#: qml/Components/Dialogs.qml:152
193msgctxt "Title: Power off/Restart dialog"
193msgctxt "Title: Power off/Restart dialog"
194msgid "Power"
194msgid "Power"
195−msgstr ""
195+msgstr "電源"
196
196
197#: qml/Components/Dialogs.qml:153
197#: qml/Components/Dialogs.qml:153
198msgid ""
198msgid ""
@@ -203,12 +203,12 @@ msgstr "本当に電源を切りますか?"
203#: qml/Components/Dialogs.qml:155
203#: qml/Components/Dialogs.qml:155
204msgctxt "Button: Power off the system"
204msgctxt "Button: Power off the system"
205msgid "Power off"
205msgid "Power off"
206−msgstr ""
206+msgstr "電源を切る"
207
207
208#: qml/Components/Dialogs.qml:164
208#: qml/Components/Dialogs.qml:164
209msgctxt "Button: Restart the system"
209msgctxt "Button: Restart the system"
210msgid "Restart"
210msgid "Restart"
211−msgstr ""
211+msgstr "再起動"
212
212
213#: qml/Components/Lockscreen.qml:245
213#: qml/Components/Lockscreen.qml:245
214msgid "Return to Call"
214msgid "Return to Call"
po/km.po+40−40
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -133,7 +133,7 @@ msgstr[1] ""
133#: qml/Components/Dialogs.qml:69
133#: qml/Components/Dialogs.qml:69
134msgctxt "Title: Lock/Log out dialog"
134msgctxt "Title: Lock/Log out dialog"
135msgid "Log out"
135msgid "Log out"
136−msgstr ""
136+msgstr "ចេញ"
137
137
138#: qml/Components/Dialogs.qml:70
138#: qml/Components/Dialogs.qml:70
139msgid "Are you sure you want to log out?"
139msgid "Are you sure you want to log out?"
@@ -142,12 +142,12 @@ msgstr "តើ​អ្នក​ពិតជា​ចង់​ចេញ​មែ
142#: qml/Components/Dialogs.qml:72
142#: qml/Components/Dialogs.qml:72
143msgctxt "Button: Lock the system"
143msgctxt "Button: Lock the system"
144msgid "Lock"
144msgid "Lock"
145−msgstr ""
145+msgstr "ចាក់សោ"
146
146
147#: qml/Components/Dialogs.qml:79
147#: qml/Components/Dialogs.qml:79
148msgctxt "Button: Log out from the system"
148msgctxt "Button: Log out from the system"
149msgid "Log Out"
149msgid "Log Out"
150−msgstr ""
150+msgstr "ចេញ"
151
151
152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153#: qml/Components/Dialogs.qml:173
153#: qml/Components/Dialogs.qml:173
@@ -157,7 +157,7 @@ msgstr "បោះបង់"
157#: qml/Components/Dialogs.qml:98
157#: qml/Components/Dialogs.qml:98
158msgctxt "Title: Reboot/Shut down dialog"
158msgctxt "Title: Reboot/Shut down dialog"
159msgid "Shut down"
159msgid "Shut down"
160−msgstr ""
160+msgstr "បិទ"
161
161
162#: qml/Components/Dialogs.qml:99
162#: qml/Components/Dialogs.qml:99
163msgid "Are you sure you want to shut down?"
163msgid "Are you sure you want to shut down?"
@@ -166,17 +166,17 @@ msgstr "តើ​អ្នក​ពិតជា​ចង់​បិទ​មែ
166#: qml/Components/Dialogs.qml:101
166#: qml/Components/Dialogs.qml:101
167msgctxt "Button: Reboot the system"
167msgctxt "Button: Reboot the system"
168msgid "Reboot"
168msgid "Reboot"
169−msgstr ""
169+msgstr "ចាប់ផ្ដើម​ឡើងវិញ"
170
170
171#: qml/Components/Dialogs.qml:109
171#: qml/Components/Dialogs.qml:109
172msgctxt "Button: Shut down the system"
172msgctxt "Button: Shut down the system"
173msgid "Shut down"
173msgid "Shut down"
174−msgstr ""
174+msgstr "បិទ"
175
175
176#: qml/Components/Dialogs.qml:129
176#: qml/Components/Dialogs.qml:129
177msgctxt "Title: Reboot dialog"
177msgctxt "Title: Reboot dialog"
178msgid "Reboot"
178msgid "Reboot"
179−msgstr ""
179+msgstr "ចាប់ផ្ដើម​ឡើងវិញ"
180
180
181#: qml/Components/Dialogs.qml:130
181#: qml/Components/Dialogs.qml:130
182msgid "Are you sure you want to reboot?"
182msgid "Are you sure you want to reboot?"
@@ -193,7 +193,7 @@ msgstr "បាទ/ចាស"
193#: qml/Components/Dialogs.qml:152
193#: qml/Components/Dialogs.qml:152
194msgctxt "Title: Power off/Restart dialog"
194msgctxt "Title: Power off/Restart dialog"
195msgid "Power"
195msgid "Power"
196−msgstr ""
196+msgstr "ថាមពល"
197
197
198#: qml/Components/Dialogs.qml:153
198#: qml/Components/Dialogs.qml:153
199msgid ""
199msgid ""
@@ -206,12 +206,12 @@ msgstr ""
206#: qml/Components/Dialogs.qml:155
206#: qml/Components/Dialogs.qml:155
207msgctxt "Button: Power off the system"
207msgctxt "Button: Power off the system"
208msgid "Power off"
208msgid "Power off"
209−msgstr ""
209+msgstr "បិទ"
210
210
211#: qml/Components/Dialogs.qml:164
211#: qml/Components/Dialogs.qml:164
212msgctxt "Button: Restart the system"
212msgctxt "Button: Restart the system"
213msgid "Restart"
213msgid "Restart"
214−msgstr ""
214+msgstr "ចាប់ផ្ដើម​ឡើងវិញ"
215
215
216#: qml/Components/Lockscreen.qml:245
216#: qml/Components/Lockscreen.qml:245
217msgid "Return to Call"
217msgid "Return to Call"
@@ -236,22 +236,22 @@ msgstr "មើល​ទាំងអស់"
236#: qml/Dash/GenericScopeView.qml:601
236#: qml/Dash/GenericScopeView.qml:601
237msgctxt "Label: Hint for dash search line edit"
237msgctxt "Label: Hint for dash search line edit"
238msgid "Search"
238msgid "Search"
239−msgstr ""
239+msgstr "ស្វែងរក"
240
240
241#: qml/Dash/PageHeader.qml:269
241#: qml/Dash/PageHeader.qml:269
242msgctxt "Button: Open the Ubuntu Store"
242msgctxt "Button: Open the Ubuntu Store"
243msgid "Store"
243msgid "Store"
244−msgstr ""
244+msgstr "រក្សាទុក"
245
245
246#: qml/Dash/PageHeader.qml:276
246#: qml/Dash/PageHeader.qml:276
247msgctxt "Button: Start a search in the current dash scope"
247msgctxt "Button: Start a search in the current dash scope"
248msgid "Search"
248msgid "Search"
249−msgstr ""
249+msgstr "ស្វែងរក"
250
250
251#: qml/Dash/PageHeader.qml:286
251#: qml/Dash/PageHeader.qml:286
252msgctxt "Button: Show the current dash scope settings"
252msgctxt "Button: Show the current dash scope settings"
253msgid "Settings"
253msgid "Settings"
254−msgstr ""
254+msgstr "ការ​កំណត់"
255
255
256#: qml/Dash/PageHeader.qml:293
256#: qml/Dash/PageHeader.qml:293
257msgid "Remove from Favorites"
257msgid "Remove from Favorites"
@@ -295,11 +295,11 @@ msgstr ""
295
295
296#: qml/Dash/ScopesList.qml:67
296#: qml/Dash/ScopesList.qml:67
297msgid "Manage"
297msgid "Manage"
298−msgstr ""
298+msgstr "គ្រប់គ្រង"
299
299
300#: qml/Dash/ScopesList.qml:113
300#: qml/Dash/ScopesList.qml:113
301msgid "Home"
301msgid "Home"
302−msgstr ""
302+msgstr "ផ្ទះ"
303
303
304#: qml/Dash/ScopesList.qml:114
304#: qml/Dash/ScopesList.qml:114
305msgid "Also installed"
305msgid "Also installed"
@@ -307,11 +307,11 @@ msgstr ""
307
307
308#: qml/Greeter/CoverPage.qml:107
308#: qml/Greeter/CoverPage.qml:107
309msgid "Unlock"
309msgid "Unlock"
310−msgstr ""
310+msgstr "ដោះ​សោ"
311
311
312#: qml/Greeter/LoginList.qml:236
312#: qml/Greeter/LoginList.qml:236
313msgid "Retry"
313msgid "Retry"
314−msgstr ""
314+msgstr "ព្យាយាម​ម្ដងទៀត"
315
315
316#: qml/Greeter/LoginList.qml:237
316#: qml/Greeter/LoginList.qml:237
317msgid "Tap to unlock"
317msgid "Tap to unlock"
@@ -452,7 +452,7 @@ msgstr "បាន​បរាជ័យ ប៉ះ​ ដើម្បី​ព្
452#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
452#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
453msgctxt "Label: Hint in message indicator line edit"
453msgctxt "Label: Hint in message indicator line edit"
454msgid "Reply"
454msgid "Reply"
455−msgstr ""
455+msgstr "ឆ្លើយតប"
456
456
457#: qml/Panel/Indicators/ModemInfoItem.qml:105
457#: qml/Panel/Indicators/ModemInfoItem.qml:105
458msgid "Unlock SIM"
458msgid "Unlock SIM"
@@ -522,26 +522,26 @@ msgstr ""
522#: qml/Wizard/Page.qml:89
522#: qml/Wizard/Page.qml:89
523msgctxt "Button: Go back one page in the Wizard"
523msgctxt "Button: Go back one page in the Wizard"
524msgid "Back"
524msgid "Back"
525−msgstr ""
525+msgstr "ថយក្រោយ"
526
526
527#: qml/Wizard/Pages/10-welcome.qml:27
527#: qml/Wizard/Pages/10-welcome.qml:27
528msgid "Hi!"
528msgid "Hi!"
529−msgstr ""
529+msgstr "សួស្ដី!"
530
530
531#: qml/Wizard/Pages/10-welcome.qml:44
531#: qml/Wizard/Pages/10-welcome.qml:44
532msgid "Welcome to your Ubuntu phone."
532msgid "Welcome to your Ubuntu phone."
533−msgstr ""
533+msgstr "សូម​ស្វាគមន៍​មកកាន់​ទូរស័ព្ទ Ubuntu ។"
534
534
535#: qml/Wizard/Pages/10-welcome.qml:52
535#: qml/Wizard/Pages/10-welcome.qml:52
536msgid "Let’s get started."
536msgid "Let’s get started."
537−msgstr ""
537+msgstr "តោះ​ចាប់ផ្ដើម។"
538
538
539#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
539#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
540#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
540#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
541#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
541#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
542#: qml/Wizard/Pages/passwd-set.qml:90
542#: qml/Wizard/Pages/passwd-set.qml:90
543msgid "Continue"
543msgid "Continue"
544−msgstr ""
544+msgstr "បន្ត"
545
545
546#: qml/Wizard/Pages/20-sim.qml:25
546#: qml/Wizard/Pages/20-sim.qml:25
547msgid "Add a SIM card and restart your device"
547msgid "Add a SIM card and restart your device"
@@ -553,11 +553,11 @@ msgstr ""
553
553
554#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
554#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
555msgid "Skip"
555msgid "Skip"
556−msgstr ""
556+msgstr "រំលង"
557
557
558#: qml/Wizard/Pages/30-passwd-type.qml:39
558#: qml/Wizard/Pages/30-passwd-type.qml:39
559msgid "Lock security"
559msgid "Lock security"
560−msgstr ""
560+msgstr "ចាក់សោ​សុវត្ថិភាព"
561
561
562#: qml/Wizard/Pages/30-passwd-type.qml:74
562#: qml/Wizard/Pages/30-passwd-type.qml:74
563msgid "Please select how you’d like to unlock your phone."
563msgid "Please select how you’d like to unlock your phone."
@@ -566,7 +566,7 @@ msgstr ""
566#: qml/Wizard/Pages/30-passwd-type.qml:97
566#: qml/Wizard/Pages/30-passwd-type.qml:97
567msgctxt "Label: Type of security method"
567msgctxt "Label: Type of security method"
568msgid "Swipe"
568msgid "Swipe"
569−msgstr ""
569+msgstr "រុញ"
570
570
571#: qml/Wizard/Pages/30-passwd-type.qml:98
571#: qml/Wizard/Pages/30-passwd-type.qml:98
572msgctxt "Label: Description of security method"
572msgctxt "Label: Description of security method"
@@ -586,7 +586,7 @@ msgstr ""
586#: qml/Wizard/Pages/30-passwd-type.qml:103
586#: qml/Wizard/Pages/30-passwd-type.qml:103
587msgctxt "Label: Type of security method"
587msgctxt "Label: Type of security method"
588msgid "Passphrase"
588msgid "Passphrase"
589−msgstr ""
589+msgstr "ឃ្លាសម្ងាត់"
590
590
591#: qml/Wizard/Pages/30-passwd-type.qml:104
591#: qml/Wizard/Pages/30-passwd-type.qml:104
592msgctxt "Label: Description of security method"
592msgctxt "Label: Description of security method"
@@ -607,7 +607,7 @@ msgstr ""
607
607
608#: qml/Wizard/Pages/50-location.qml:27
608#: qml/Wizard/Pages/50-location.qml:27
609msgid "Location"
609msgid "Location"
610−msgstr ""
610+msgstr "ទីតាំង"
611
611
612#: qml/Wizard/Pages/50-location.qml:62
612#: qml/Wizard/Pages/50-location.qml:62
613msgid "Let the phone detect your location:"
613msgid "Let the phone detect your location:"
@@ -654,27 +654,27 @@ msgstr ""
654
654
655#: qml/Wizard/Pages/80-finished.qml:24
655#: qml/Wizard/Pages/80-finished.qml:24
656msgid "All done"
656msgid "All done"
657−msgstr ""
657+msgstr "ធ្វើ​រួច​ទាំងអស់"
658
658
659#: qml/Wizard/Pages/80-finished.qml:39
659#: qml/Wizard/Pages/80-finished.qml:39
660msgid "Nice work!"
660msgid "Nice work!"
661−msgstr ""
661+msgstr "ដំណើរការ​ល្អ!"
662
662
663#: qml/Wizard/Pages/80-finished.qml:46
663#: qml/Wizard/Pages/80-finished.qml:46
664msgid "Your phone is now ready to use."
664msgid "Your phone is now ready to use."
665−msgstr ""
665+msgstr "ទូរស័ព្ទ​របស់​អ្នក​ឥឡូវ​អាច​ប្រើ​បាន។"
666
666
667#: qml/Wizard/Pages/80-finished.qml:53
667#: qml/Wizard/Pages/80-finished.qml:53
668msgid "Finish"
668msgid "Finish"
669−msgstr ""
669+msgstr "បញ្ចប់"
670
670
671#: qml/Wizard/Pages/here-terms.qml:27
671#: qml/Wizard/Pages/here-terms.qml:27
672msgid "Terms & Conditions"
672msgid "Terms & Conditions"
673−msgstr ""
673+msgstr "លក្ខខណ្ឌ​ប្រើប្រាស់"
674
674
675#: qml/Wizard/Pages/passwd-confirm.qml:49
675#: qml/Wizard/Pages/passwd-confirm.qml:49
676msgid "Confirm passphrase"
676msgid "Confirm passphrase"
677−msgstr ""
677+msgstr "បញ្ជាក់​ឃ្លា​សម្ងាត់"
678
678
679#: qml/Wizard/Pages/passwd-confirm.qml:50
679#: qml/Wizard/Pages/passwd-confirm.qml:50
680msgid "Confirm passcode"
680msgid "Confirm passcode"
@@ -683,7 +683,7 @@ msgstr ""
683#: qml/Wizard/Pages/passwd-confirm.qml:53
683#: qml/Wizard/Pages/passwd-confirm.qml:53
684#: qml/Wizard/Pages/passwd-confirm.qml:54
684#: qml/Wizard/Pages/passwd-confirm.qml:54
685msgid "Please try again."
685msgid "Please try again."
686−msgstr ""
686+msgstr "សូម​ព្យាយាម​ម្ដងទៀត។"
687
687
688#: qml/Wizard/Pages/passwd-set.qml:61
688#: qml/Wizard/Pages/passwd-set.qml:61
689msgid "Choose your passcode"
689msgid "Choose your passcode"
@@ -697,13 +697,13 @@ msgstr ""
697#: qml/Wizard/StackButton.qml:39
697#: qml/Wizard/StackButton.qml:39
698#, qt-format
698#, qt-format
699msgid "〈 %1"
699msgid "〈 %1"
700−msgstr ""
700+msgstr "〈 %1"
701
701
702#. Translators: This is the arrow for "Forward" buttons
702#. Translators: This is the arrow for "Forward" buttons
703#: qml/Wizard/StackButton.qml:42
703#: qml/Wizard/StackButton.qml:42
704#, qt-format
704#, qt-format
705msgid "%1 〉"
705msgid "%1 〉"
706−msgstr ""
706+msgstr "%1 〉"
707
707
708#~ msgid "Try swiping from the right edge to unlock the phone"
708#~ msgid "Try swiping from the right edge to unlock the phone"
709#~ msgstr "សាកល្បង​​អូស​ពី​គែម​ស្ដាំ​ ដើម្បី​ដោះ​សោ​ទូរស័ព្ទ"
709#~ msgstr "សាកល្បង​​អូស​ពី​គែម​ស្ដាំ​ ដើម្បី​ដោះ​សោ​ទូរស័ព្ទ"
po/kn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ko.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ku.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ky.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/lo.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/lt.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"(n%100<10 || n%100>=20) ? 1 : 2;\n"
18"(n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/lv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ml.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/mr.po+12−12
@@ -15,12 +15,12 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
23−msgstr ""
23+msgstr "पासवर्ड: "
24
24
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
@@ -142,7 +142,7 @@ msgstr ""
142#: qml/Components/Dialogs.qml:72
142#: qml/Components/Dialogs.qml:72
143msgctxt "Button: Lock the system"
143msgctxt "Button: Lock the system"
144msgid "Lock"
144msgid "Lock"
145−msgstr ""
145+msgstr "लॉक करा"
146
146
147#: qml/Components/Dialogs.qml:79
147#: qml/Components/Dialogs.qml:79
148msgctxt "Button: Log out from the system"
148msgctxt "Button: Log out from the system"
@@ -152,7 +152,7 @@ msgstr ""
152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153#: qml/Components/Dialogs.qml:173
153#: qml/Components/Dialogs.qml:173
154msgid "Cancel"
154msgid "Cancel"
155−msgstr ""
155+msgstr "रद्द"
156
156
157#: qml/Components/Dialogs.qml:98
157#: qml/Components/Dialogs.qml:98
158msgctxt "Title: Reboot/Shut down dialog"
158msgctxt "Title: Reboot/Shut down dialog"
@@ -184,11 +184,11 @@ msgstr ""
184
184
185#: qml/Components/Dialogs.qml:132
185#: qml/Components/Dialogs.qml:132
186msgid "No"
186msgid "No"
187−msgstr ""
187+msgstr "नाही"
188
188
189#: qml/Components/Dialogs.qml:138
189#: qml/Components/Dialogs.qml:138
190msgid "Yes"
190msgid "Yes"
191−msgstr ""
191+msgstr "हो"
192
192
193#: qml/Components/Dialogs.qml:152
193#: qml/Components/Dialogs.qml:152
194msgctxt "Title: Power off/Restart dialog"
194msgctxt "Title: Power off/Restart dialog"
@@ -234,7 +234,7 @@ msgstr ""
234#: qml/Dash/GenericScopeView.qml:601
234#: qml/Dash/GenericScopeView.qml:601
235msgctxt "Label: Hint for dash search line edit"
235msgctxt "Label: Hint for dash search line edit"
236msgid "Search"
236msgid "Search"
237−msgstr ""
237+msgstr "शोध"
238
238
239#: qml/Dash/PageHeader.qml:269
239#: qml/Dash/PageHeader.qml:269
240msgctxt "Button: Open the Ubuntu Store"
240msgctxt "Button: Open the Ubuntu Store"
@@ -244,7 +244,7 @@ msgstr ""
244#: qml/Dash/PageHeader.qml:276
244#: qml/Dash/PageHeader.qml:276
245msgctxt "Button: Start a search in the current dash scope"
245msgctxt "Button: Start a search in the current dash scope"
246msgid "Search"
246msgid "Search"
247−msgstr ""
247+msgstr "शोध"
248
248
249#: qml/Dash/PageHeader.qml:286
249#: qml/Dash/PageHeader.qml:286
250msgctxt "Button: Show the current dash scope settings"
250msgctxt "Button: Show the current dash scope settings"
@@ -253,11 +253,11 @@ msgstr ""
253
253
254#: qml/Dash/PageHeader.qml:293
254#: qml/Dash/PageHeader.qml:293
255msgid "Remove from Favorites"
255msgid "Remove from Favorites"
256−msgstr ""
256+msgstr "आवड पासून काढूण टाका"
257
257
258#: qml/Dash/PageHeader.qml:293
258#: qml/Dash/PageHeader.qml:293
259msgid "Add to Favorites"
259msgid "Add to Favorites"
260−msgstr ""
260+msgstr "आवड मध्ये जोडा"
261
261
262#: qml/Dash/Previews/PreviewActionCombo.qml:35
262#: qml/Dash/Previews/PreviewActionCombo.qml:35
263msgid "More..."
263msgid "More..."
@@ -277,7 +277,7 @@ msgstr ""
277
277
278#: qml/Dash/Previews/PreviewRatingInput.qml:169
278#: qml/Dash/Previews/PreviewRatingInput.qml:169
279msgid "Send"
279msgid "Send"
280−msgstr ""
280+msgstr "पाठवा"
281
281
282#: qml/Dash/PullToRefreshScopeStyle.qml:55
282#: qml/Dash/PullToRefreshScopeStyle.qml:55
283msgid "Pull to refresh…"
283msgid "Pull to refresh…"
po/ms.po+81−73
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -135,7 +135,7 @@ msgstr[1] "Sila tunggu %1 minit kemudian cuba lagi..."
135#: qml/Components/Dialogs.qml:69
135#: qml/Components/Dialogs.qml:69
136msgctxt "Title: Lock/Log out dialog"
136msgctxt "Title: Lock/Log out dialog"
137msgid "Log out"
137msgid "Log out"
138−msgstr ""
138+msgstr "Daftar keluar"
139
139
140#: qml/Components/Dialogs.qml:70
140#: qml/Components/Dialogs.qml:70
141msgid "Are you sure you want to log out?"
141msgid "Are you sure you want to log out?"
@@ -144,12 +144,12 @@ msgstr "Anda pasti mahu mendaftar keluar?"
144#: qml/Components/Dialogs.qml:72
144#: qml/Components/Dialogs.qml:72
145msgctxt "Button: Lock the system"
145msgctxt "Button: Lock the system"
146msgid "Lock"
146msgid "Lock"
147−msgstr ""
147+msgstr "Kunci"
148
148
149#: qml/Components/Dialogs.qml:79
149#: qml/Components/Dialogs.qml:79
150msgctxt "Button: Log out from the system"
150msgctxt "Button: Log out from the system"
151msgid "Log Out"
151msgid "Log Out"
152−msgstr ""
152+msgstr "Daftar Keluar"
153
153
154#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
154#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
155#: qml/Components/Dialogs.qml:173
155#: qml/Components/Dialogs.qml:173
@@ -159,7 +159,7 @@ msgstr "Batal"
159#: qml/Components/Dialogs.qml:98
159#: qml/Components/Dialogs.qml:98
160msgctxt "Title: Reboot/Shut down dialog"
160msgctxt "Title: Reboot/Shut down dialog"
161msgid "Shut down"
161msgid "Shut down"
162−msgstr ""
162+msgstr "Matikan"
163
163
164#: qml/Components/Dialogs.qml:99
164#: qml/Components/Dialogs.qml:99
165msgid "Are you sure you want to shut down?"
165msgid "Are you sure you want to shut down?"
@@ -168,17 +168,17 @@ msgstr "Anda pasti mahu matikan?"
168#: qml/Components/Dialogs.qml:101
168#: qml/Components/Dialogs.qml:101
169msgctxt "Button: Reboot the system"
169msgctxt "Button: Reboot the system"
170msgid "Reboot"
170msgid "Reboot"
171−msgstr ""
171+msgstr "But Semula"
172
172
173#: qml/Components/Dialogs.qml:109
173#: qml/Components/Dialogs.qml:109
174msgctxt "Button: Shut down the system"
174msgctxt "Button: Shut down the system"
175msgid "Shut down"
175msgid "Shut down"
176−msgstr ""
176+msgstr "Matikan"
177
177
178#: qml/Components/Dialogs.qml:129
178#: qml/Components/Dialogs.qml:129
179msgctxt "Title: Reboot dialog"
179msgctxt "Title: Reboot dialog"
180msgid "Reboot"
180msgid "Reboot"
181−msgstr ""
181+msgstr "But Semula"
182
182
183#: qml/Components/Dialogs.qml:130
183#: qml/Components/Dialogs.qml:130
184msgid "Are you sure you want to reboot?"
184msgid "Are you sure you want to reboot?"
@@ -195,7 +195,7 @@ msgstr "Ya"
195#: qml/Components/Dialogs.qml:152
195#: qml/Components/Dialogs.qml:152
196msgctxt "Title: Power off/Restart dialog"
196msgctxt "Title: Power off/Restart dialog"
197msgid "Power"
197msgid "Power"
198−msgstr ""
198+msgstr "Kuasa"
199
199
200#: qml/Components/Dialogs.qml:153
200#: qml/Components/Dialogs.qml:153
201msgid ""
201msgid ""
@@ -206,16 +206,16 @@ msgstr "Anda pasti mahu tutup?"
206#: qml/Components/Dialogs.qml:155
206#: qml/Components/Dialogs.qml:155
207msgctxt "Button: Power off the system"
207msgctxt "Button: Power off the system"
208msgid "Power off"
208msgid "Power off"
209−msgstr ""
209+msgstr "Padam"
210
210
211#: qml/Components/Dialogs.qml:164
211#: qml/Components/Dialogs.qml:164
212msgctxt "Button: Restart the system"
212msgctxt "Button: Restart the system"
213msgid "Restart"
213msgid "Restart"
214−msgstr ""
214+msgstr "Mula Semula"
215
215
216#: qml/Components/Lockscreen.qml:245
216#: qml/Components/Lockscreen.qml:245
217msgid "Return to Call"
217msgid "Return to Call"
218−msgstr ""
218+msgstr "Kembali ke Panggilan"
219
219
220#: qml/Components/Lockscreen.qml:245
220#: qml/Components/Lockscreen.qml:245
221msgid "Emergency Call"
221msgid "Emergency Call"
@@ -236,22 +236,22 @@ msgstr "Lihat semua"
236#: qml/Dash/GenericScopeView.qml:601
236#: qml/Dash/GenericScopeView.qml:601
237msgctxt "Label: Hint for dash search line edit"
237msgctxt "Label: Hint for dash search line edit"
238msgid "Search"
238msgid "Search"
239−msgstr ""
239+msgstr "Gelintar"
240
240
241#: qml/Dash/PageHeader.qml:269
241#: qml/Dash/PageHeader.qml:269
242msgctxt "Button: Open the Ubuntu Store"
242msgctxt "Button: Open the Ubuntu Store"
243msgid "Store"
243msgid "Store"
244−msgstr ""
244+msgstr "Kedai"
245
245
246#: qml/Dash/PageHeader.qml:276
246#: qml/Dash/PageHeader.qml:276
247msgctxt "Button: Start a search in the current dash scope"
247msgctxt "Button: Start a search in the current dash scope"
248msgid "Search"
248msgid "Search"
249−msgstr ""
249+msgstr "Gelintar"
250
250
251#: qml/Dash/PageHeader.qml:286
251#: qml/Dash/PageHeader.qml:286
252msgctxt "Button: Show the current dash scope settings"
252msgctxt "Button: Show the current dash scope settings"
253msgid "Settings"
253msgid "Settings"
254−msgstr ""
254+msgstr "Tetapan"
255
255
256#: qml/Dash/PageHeader.qml:293
256#: qml/Dash/PageHeader.qml:293
257msgid "Remove from Favorites"
257msgid "Remove from Favorites"
@@ -291,19 +291,19 @@ msgstr "Lepas untuk segar semula..."
291
291
292#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
292#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
293msgid "Enable location data"
293msgid "Enable location data"
294−msgstr ""
294+msgstr "Benarkan data lokasi"
295
295
296#: qml/Dash/ScopesList.qml:67
296#: qml/Dash/ScopesList.qml:67
297msgid "Manage"
297msgid "Manage"
298−msgstr ""
298+msgstr "Urus"
299
299
300#: qml/Dash/ScopesList.qml:113
300#: qml/Dash/ScopesList.qml:113
301msgid "Home"
301msgid "Home"
302−msgstr ""
302+msgstr "Rumah"
303
303
304#: qml/Dash/ScopesList.qml:114
304#: qml/Dash/ScopesList.qml:114
305msgid "Also installed"
305msgid "Also installed"
306−msgstr ""
306+msgstr "Juga dipasang"
307
307
308#: qml/Greeter/CoverPage.qml:107
308#: qml/Greeter/CoverPage.qml:107
309msgid "Unlock"
309msgid "Unlock"
@@ -311,11 +311,11 @@ msgstr "Buka"
311
311
312#: qml/Greeter/LoginList.qml:236
312#: qml/Greeter/LoginList.qml:236
313msgid "Retry"
313msgid "Retry"
314−msgstr ""
314+msgstr "Cuba Lagi"
315
315
316#: qml/Greeter/LoginList.qml:237
316#: qml/Greeter/LoginList.qml:237
317msgid "Tap to unlock"
317msgid "Tap to unlock"
318−msgstr ""
318+msgstr "Ketik untuk buka"
319
319
320#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:53
320#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:53
321msgid "Sorry, incorrect passphrase."
321msgid "Sorry, incorrect passphrase."
@@ -450,7 +450,7 @@ msgstr "Gagal, ketuk untuk cuba lagi"
450#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
450#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
451msgctxt "Label: Hint in message indicator line edit"
451msgctxt "Label: Hint in message indicator line edit"
452msgid "Reply"
452msgid "Reply"
453−msgstr ""
453+msgstr "Balas"
454
454
455#: qml/Panel/Indicators/ModemInfoItem.qml:105
455#: qml/Panel/Indicators/ModemInfoItem.qml:105
456msgid "Unlock SIM"
456msgid "Unlock SIM"
@@ -462,24 +462,24 @@ msgstr "Perayauan"
462
462
463#: qml/Tutorial/TutorialBottomFinish.qml:24
463#: qml/Tutorial/TutorialBottomFinish.qml:24
464msgid "This action does different things for different apps"
464msgid "This action does different things for different apps"
465−msgstr ""
465+msgstr "Tindakan ini melakukan perkara yang berlainan untuk apl berbeza"
466
466
467#: qml/Tutorial/TutorialBottomFinish.qml:25
467#: qml/Tutorial/TutorialBottomFinish.qml:25
468msgid "Tap here to finish."
468msgid "Tap here to finish."
469−msgstr ""
469+msgstr "Ketik di sini untuk selesaikan."
470
470
471#: qml/Tutorial/TutorialBottom.qml:28
471#: qml/Tutorial/TutorialBottom.qml:28
472msgid "Open special menus"
472msgid "Open special menus"
473−msgstr ""
473+msgstr "Buka menu khas"
474
474
475#: qml/Tutorial/TutorialBottom.qml:29
475#: qml/Tutorial/TutorialBottom.qml:29
476msgid "Swipe up from the bottom edge."
476msgid "Swipe up from the bottom edge."
477−msgstr ""
477+msgstr "Leret ke ata dari pinggir bawah."
478
478
479#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
479#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
480#: qml/Tutorial/TutorialRight.qml:144
480#: qml/Tutorial/TutorialRight.qml:144
481msgid "Tap here to continue."
481msgid "Tap here to continue."
482−msgstr ""
482+msgstr "Ketik di sini untuk teruskan."
483
483
484#: qml/Tutorial/TutorialContent.qml:81
484#: qml/Tutorial/TutorialContent.qml:81
485msgid "Click here to finish."
485msgid "Click here to finish."
@@ -487,137 +487,139 @@ msgstr ""
487
487
488#: qml/Tutorial/TutorialLeftFinish.qml:24
488#: qml/Tutorial/TutorialLeftFinish.qml:24
489msgid "These are the shortcuts to favorite apps"
489msgid "These are the shortcuts to favorite apps"
490−msgstr ""
490+msgstr "Ini adalah pintasan ke apl kegemaran"
491
491
492#: qml/Tutorial/TutorialLeft.qml:26
492#: qml/Tutorial/TutorialLeft.qml:26
493msgid "Open the launcher"
493msgid "Open the launcher"
494−msgstr ""
494+msgstr "Buka pelancar"
495
495
496#: qml/Tutorial/TutorialLeft.qml:27
496#: qml/Tutorial/TutorialLeft.qml:27
497msgid "Short swipe from the left edge."
497msgid "Short swipe from the left edge."
498−msgstr ""
498+msgstr "Leret pendek dari pinggir kiri."
499
499
500#: qml/Tutorial/TutorialPage.qml:178
500#: qml/Tutorial/TutorialPage.qml:178
501msgid "You almost got it!"
501msgid "You almost got it!"
502−msgstr ""
502+msgstr "Anda hampir berjaya!"
503
503
504#: qml/Tutorial/TutorialPage.qml:195
504#: qml/Tutorial/TutorialPage.qml:195
505msgid "Try again."
505msgid "Try again."
506−msgstr ""
506+msgstr "Cuba lagi."
507
507
508#: qml/Tutorial/TutorialRight.qml:31
508#: qml/Tutorial/TutorialRight.qml:31
509msgid "To view open apps"
509msgid "To view open apps"
510−msgstr ""
510+msgstr "Untuk melihat apl dibuka"
511
511
512#: qml/Tutorial/TutorialRight.qml:32
512#: qml/Tutorial/TutorialRight.qml:32
513msgid "Long swipe from the right edge."
513msgid "Long swipe from the right edge."
514−msgstr ""
514+msgstr "Leret panjang dari pinggir kanan."
515
515
516#: qml/Tutorial/TutorialRight.qml:122
516#: qml/Tutorial/TutorialRight.qml:122
517msgid "View all your running tasks."
517msgid "View all your running tasks."
518−msgstr ""
518+msgstr "Lihat semua tugas yang berjalan."
519
519
520#: qml/Wizard/Page.qml:89
520#: qml/Wizard/Page.qml:89
521msgctxt "Button: Go back one page in the Wizard"
521msgctxt "Button: Go back one page in the Wizard"
522msgid "Back"
522msgid "Back"
523−msgstr ""
523+msgstr "Kembali"
524
524
525#: qml/Wizard/Pages/10-welcome.qml:27
525#: qml/Wizard/Pages/10-welcome.qml:27
526msgid "Hi!"
526msgid "Hi!"
527−msgstr ""
527+msgstr "Hai"
528
528
529#: qml/Wizard/Pages/10-welcome.qml:44
529#: qml/Wizard/Pages/10-welcome.qml:44
530msgid "Welcome to your Ubuntu phone."
530msgid "Welcome to your Ubuntu phone."
531−msgstr ""
531+msgstr "Selamat datang ke telefon Ubuntu anda."
532
532
533#: qml/Wizard/Pages/10-welcome.qml:52
533#: qml/Wizard/Pages/10-welcome.qml:52
534msgid "Let’s get started."
534msgid "Let’s get started."
535−msgstr ""
535+msgstr "Mari mulakan."
536
536
537#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
537#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
538#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
538#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
539#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
539#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
540#: qml/Wizard/Pages/passwd-set.qml:90
540#: qml/Wizard/Pages/passwd-set.qml:90
541msgid "Continue"
541msgid "Continue"
542−msgstr ""
542+msgstr "Teruskan"
543
543
544#: qml/Wizard/Pages/20-sim.qml:25
544#: qml/Wizard/Pages/20-sim.qml:25
545msgid "Add a SIM card and restart your device"
545msgid "Add a SIM card and restart your device"
546−msgstr ""
546+msgstr "Tambah kad SIM dan mulakan semula peranti anda"
547
547
548#: qml/Wizard/Pages/20-sim.qml:59
548#: qml/Wizard/Pages/20-sim.qml:59
549msgid "Without it, you won’t be able to make calls or use text messaging."
549msgid "Without it, you won’t be able to make calls or use text messaging."
550msgstr ""
550msgstr ""
551+"Tanpanya, anda tidak boleh membuat panggilan atau guna pemesejan teks."
551
552
552#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
553#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
553msgid "Skip"
554msgid "Skip"
554−msgstr ""
555+msgstr "Langkau"
555
556
556#: qml/Wizard/Pages/30-passwd-type.qml:39
557#: qml/Wizard/Pages/30-passwd-type.qml:39
557msgid "Lock security"
558msgid "Lock security"
558−msgstr ""
559+msgstr "Kunci keselamatan"
559
560
560#: qml/Wizard/Pages/30-passwd-type.qml:74
561#: qml/Wizard/Pages/30-passwd-type.qml:74
561msgid "Please select how you’d like to unlock your phone."
562msgid "Please select how you’d like to unlock your phone."
562−msgstr ""
563+msgstr "Sila pilih bagaimana anda mahu membuka telefon anda."
563
564
564#: qml/Wizard/Pages/30-passwd-type.qml:97
565#: qml/Wizard/Pages/30-passwd-type.qml:97
565msgctxt "Label: Type of security method"
566msgctxt "Label: Type of security method"
566msgid "Swipe"
567msgid "Swipe"
567−msgstr ""
568+msgstr "Leret"
568
569
569#: qml/Wizard/Pages/30-passwd-type.qml:98
570#: qml/Wizard/Pages/30-passwd-type.qml:98
570msgctxt "Label: Description of security method"
571msgctxt "Label: Description of security method"
571msgid "No security"
572msgid "No security"
572−msgstr ""
573+msgstr "Tanpa keselamatan"
573
574
574#: qml/Wizard/Pages/30-passwd-type.qml:100
575#: qml/Wizard/Pages/30-passwd-type.qml:100
575msgctxt "Label: Type of security method"
576msgctxt "Label: Type of security method"
576msgid "Passcode"
577msgid "Passcode"
577−msgstr ""
578+msgstr "Kod laluan"
578
579
579#: qml/Wizard/Pages/30-passwd-type.qml:101
580#: qml/Wizard/Pages/30-passwd-type.qml:101
580msgctxt "Label: Description of security method"
581msgctxt "Label: Description of security method"
581msgid "4 digits only"
582msgid "4 digits only"
582−msgstr ""
583+msgstr "4 digit sahaja"
583
584
584#: qml/Wizard/Pages/30-passwd-type.qml:103
585#: qml/Wizard/Pages/30-passwd-type.qml:103
585msgctxt "Label: Type of security method"
586msgctxt "Label: Type of security method"
586msgid "Passphrase"
587msgid "Passphrase"
587−msgstr ""
588+msgstr "Frasa laluan"
588
589
589#: qml/Wizard/Pages/30-passwd-type.qml:104
590#: qml/Wizard/Pages/30-passwd-type.qml:104
590msgctxt "Label: Description of security method"
591msgctxt "Label: Description of security method"
591msgid "Numbers and letters"
592msgid "Numbers and letters"
592−msgstr ""
593+msgstr "Nombor dan abjad"
593
594
594#: qml/Wizard/Pages/40-wifi.qml:29
595#: qml/Wizard/Pages/40-wifi.qml:29
595msgid "Connect to Wi‑Fi"
596msgid "Connect to Wi‑Fi"
596−msgstr ""
597+msgstr "Sambung ke Wi-Fi"
597
598
598#: qml/Wizard/Pages/40-wifi.qml:168
599#: qml/Wizard/Pages/40-wifi.qml:168
599msgid "Available networks…"
600msgid "Available networks…"
600−msgstr ""
601+msgstr "Rangkaian tersedia..."
601
602
602#: qml/Wizard/Pages/40-wifi.qml:169
603#: qml/Wizard/Pages/40-wifi.qml:169
603msgid "No available networks."
604msgid "No available networks."
604−msgstr ""
605+msgstr "Tiada rangkaian tersedia."
605
606
606#: qml/Wizard/Pages/50-location.qml:27
607#: qml/Wizard/Pages/50-location.qml:27
607msgid "Location"
608msgid "Location"
608−msgstr ""
609+msgstr "Lokasi"
609
610
610#: qml/Wizard/Pages/50-location.qml:62
611#: qml/Wizard/Pages/50-location.qml:62
611msgid "Let the phone detect your location:"
612msgid "Let the phone detect your location:"
612−msgstr ""
613+msgstr "Biarkan telefon mengesan lokasi anda:"
613
614
614#: qml/Wizard/Pages/50-location.qml:69
615#: qml/Wizard/Pages/50-location.qml:69
615msgid "Using GPS only (less accurate)"
616msgid "Using GPS only (less accurate)"
616−msgstr ""
617+msgstr "Hanya gunakan GPS (kurang tepat)"
617
618
618#: qml/Wizard/Pages/50-location.qml:86
619#: qml/Wizard/Pages/50-location.qml:86
619msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
620msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
620msgstr ""
621msgstr ""
622+"Gunakan GPS, Wi-Fi awanama dan maklumat rangkaian selular (disarankan)"
621
623
622#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
624#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
623#: qml/Wizard/Pages/50-location.qml:103
625#: qml/Wizard/Pages/50-location.qml:103
@@ -625,83 +627,89 @@ msgid ""
625"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
627"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
626"conditions</a>."
628"conditions</a>."
627msgstr ""
629msgstr ""
630+"Dengan memilih pilihan ini anda menyetujui <a href='#'>terma dan syarat</a> "
631+"Nokia HERE."
628
632
629#: qml/Wizard/Pages/50-location.qml:112
633#: qml/Wizard/Pages/50-location.qml:112
630msgid "Not at all"
634msgid "Not at all"
631−msgstr ""
635+msgstr "Tidak sama sekali"
632
636
633#: qml/Wizard/Pages/50-location.qml:124
637#: qml/Wizard/Pages/50-location.qml:124
634msgid "You can change your mind later in <b>System Settings</b>."
638msgid "You can change your mind later in <b>System Settings</b>."
635−msgstr ""
639+msgstr "Anda boleh mengubahnya kemudian di dalam <b>Tetapan Sistem</b>."
636
640
637#: qml/Wizard/Pages/60-reporting.qml:24
641#: qml/Wizard/Pages/60-reporting.qml:24
638msgid "Improving your experience"
642msgid "Improving your experience"
639−msgstr ""
643+msgstr "Menambahbaik pengalaman anda"
640
644
641#: qml/Wizard/Pages/60-reporting.qml:36
645#: qml/Wizard/Pages/60-reporting.qml:36
642msgid ""
646msgid ""
643"Your phone is set up to automatically report errors to Canonical and its "
647"Your phone is set up to automatically report errors to Canonical and its "
644"partners, the makers of the operating system."
648"partners, the makers of the operating system."
645msgstr ""
649msgstr ""
650+"Telefon anda ditetapkan secara automatik melaporkan ralat kepada Canonical "
651+"dan rakannya, pencipta sistem pengoperasian ini."
646
652
647#: qml/Wizard/Pages/60-reporting.qml:43
653#: qml/Wizard/Pages/60-reporting.qml:43
648msgid ""
654msgid ""
649"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
655"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
650"Privacy</b>"
656"Privacy</b>"
651msgstr ""
657msgstr ""
658+"Ia boleh dilumpuhkan di dalam <b>Tetapan Sistem</b> di bawah <b>Keselamatan "
659+"&amp; Kerahsiaan</b>"
652
660
653#: qml/Wizard/Pages/80-finished.qml:24
661#: qml/Wizard/Pages/80-finished.qml:24
654msgid "All done"
662msgid "All done"
655−msgstr ""
663+msgstr "Semua selesai"
656
664
657#: qml/Wizard/Pages/80-finished.qml:39
665#: qml/Wizard/Pages/80-finished.qml:39
658msgid "Nice work!"
666msgid "Nice work!"
659−msgstr ""
667+msgstr "Selesai!"
660
668
661#: qml/Wizard/Pages/80-finished.qml:46
669#: qml/Wizard/Pages/80-finished.qml:46
662msgid "Your phone is now ready to use."
670msgid "Your phone is now ready to use."
663−msgstr ""
671+msgstr "Telefon anda kini boleh digunakan."
664
672
665#: qml/Wizard/Pages/80-finished.qml:53
673#: qml/Wizard/Pages/80-finished.qml:53
666msgid "Finish"
674msgid "Finish"
667−msgstr ""
675+msgstr "Selesai"
668
676
669#: qml/Wizard/Pages/here-terms.qml:27
677#: qml/Wizard/Pages/here-terms.qml:27
670msgid "Terms & Conditions"
678msgid "Terms & Conditions"
671−msgstr ""
679+msgstr "Terma & Syarat"
672
680
673#: qml/Wizard/Pages/passwd-confirm.qml:49
681#: qml/Wizard/Pages/passwd-confirm.qml:49
674msgid "Confirm passphrase"
682msgid "Confirm passphrase"
675−msgstr ""
683+msgstr "Sahkan frasa laluan"
676
684
677#: qml/Wizard/Pages/passwd-confirm.qml:50
685#: qml/Wizard/Pages/passwd-confirm.qml:50
678msgid "Confirm passcode"
686msgid "Confirm passcode"
679−msgstr ""
687+msgstr "Sahkan kod laluan"
680
688
681#: qml/Wizard/Pages/passwd-confirm.qml:53
689#: qml/Wizard/Pages/passwd-confirm.qml:53
682#: qml/Wizard/Pages/passwd-confirm.qml:54
690#: qml/Wizard/Pages/passwd-confirm.qml:54
683msgid "Please try again."
691msgid "Please try again."
684−msgstr ""
692+msgstr "Sila cuba lagi."
685
693
686#: qml/Wizard/Pages/passwd-set.qml:61
694#: qml/Wizard/Pages/passwd-set.qml:61
687msgid "Choose your passcode"
695msgid "Choose your passcode"
688−msgstr ""
696+msgstr "Pilih kod laluan anda"
689
697
690#: qml/Wizard/Pages/passwd-set.qml:68
698#: qml/Wizard/Pages/passwd-set.qml:68
691msgid "Passphrase must be 4 characters long"
699msgid "Passphrase must be 4 characters long"
692−msgstr ""
700+msgstr "Frasa laluan mestilah sebanyak 4 aksara"
693
701
694#. Translators: This is the arrow for "Back" buttons
702#. Translators: This is the arrow for "Back" buttons
695#: qml/Wizard/StackButton.qml:39
703#: qml/Wizard/StackButton.qml:39
696#, qt-format
704#, qt-format
697msgid "〈 %1"
705msgid "〈 %1"
698−msgstr ""
706+msgstr "〈 %1"
699
707
700#. Translators: This is the arrow for "Forward" buttons
708#. Translators: This is the arrow for "Forward" buttons
701#: qml/Wizard/StackButton.qml:42
709#: qml/Wizard/StackButton.qml:42
702#, qt-format
710#, qt-format
703msgid "%1 〉"
711msgid "%1 〉"
704−msgstr ""
712+msgstr "%1 〉"
705
713
706#~ msgid "Top edge"
714#~ msgid "Top edge"
707#~ msgstr "Pinggir atas"
715#~ msgstr "Pinggir atas"
po/my.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/nb.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/nl.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/nn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/oc.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/or.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n!=1;\n"
17"Plural-Forms: nplurals=2; plural=n!=1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/pa.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/pl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
17"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
18"|| n%100>=20) ? 1 : 2;\n"
18"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/pt.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-30 06:14+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17646)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/pt_BR.po+12−12
@@ -8,15 +8,15 @@ 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-07-17 10:10+0200\n"
10"POT-Creation-Date: 2015-07-17 10:10+0200\n"
11−"PO-Revision-Date: 2015-05-04 21:50+0000\n"
11+"PO-Revision-Date: 2015-08-05 04:40+0000\n"
12−"Last-Translator: Tiago Hillebrandt <tiagohillebrandt@gmail.com>\n"
12+"Last-Translator: Julio Alexander Sieg <julio.sieg@gmail.com>\n"
13"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
13"Language-Team: Brazilian Portuguese <pt_BR@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-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -398,32 +398,32 @@ msgstr "Nada está tocando"
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 hora"
402−msgstr[1] ""
402+msgstr[1] "%1 horas"
403
403
404#: qml/Panel/Indicators/MenuItemFactory.qml:772
404#: qml/Panel/Indicators/MenuItemFactory.qml:772
405#, qt-format
405#, qt-format
406msgid "%1 minute"
406msgid "%1 minute"
407msgid_plural "%1 minutes"
407msgid_plural "%1 minutes"
408−msgstr[0] ""
408+msgstr[0] "%1 minuto"
409−msgstr[1] ""
409+msgstr[1] "%1 minutos"
410
410
411#: qml/Panel/Indicators/MenuItemFactory.qml:777
411#: qml/Panel/Indicators/MenuItemFactory.qml:777
412#, qt-format
412#, qt-format
413msgid "%1 second"
413msgid "%1 second"
414msgid_plural "%1 seconds"
414msgid_plural "%1 seconds"
415−msgstr[0] ""
415+msgstr[0] "%1 segundo"
416−msgstr[1] ""
416+msgstr[1] "%1 segundos"
417
417
418#: qml/Panel/Indicators/MenuItemFactory.qml:780
418#: qml/Panel/Indicators/MenuItemFactory.qml:780
419msgid "0 seconds"
419msgid "0 seconds"
420−msgstr ""
420+msgstr "0 segundo"
421
421
422#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
422#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
423#: qml/Panel/Indicators/MenuItemFactory.qml:782
423#: qml/Panel/Indicators/MenuItemFactory.qml:782
424#, qt-format
424#, qt-format
425msgid "%1 remaining"
425msgid "%1 remaining"
426−msgstr ""
426+msgstr "%1 restantes"
427
427
428#: qml/Panel/Indicators/MenuItemFactory.qml:788
428#: qml/Panel/Indicators/MenuItemFactory.qml:788
429msgid "In queue…"
429msgid "In queue…"
po/ro.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
17"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
18"== 0) && (n != 0))) ? 2: 1));\n"
18"== 0) && (n != 0))) ? 2: 1));\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/ru.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/shn.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=(n == 1) ? 0 : 1;\n"
17"Plural-Forms: nplurals=2; plural=(n == 1) ? 0 : 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/si.po+75−13
@@ -15,12 +15,12 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
23−msgstr ""
23+msgstr "මුරපදය: "
24
24
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
@@ -152,7 +152,7 @@ msgstr ""
152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153#: qml/Components/Dialogs.qml:173
153#: qml/Components/Dialogs.qml:173
154msgid "Cancel"
154msgid "Cancel"
155−msgstr ""
155+msgstr "අවලංගු කරන්න"
156
156
157#: qml/Components/Dialogs.qml:98
157#: qml/Components/Dialogs.qml:98
158msgctxt "Title: Reboot/Shut down dialog"
158msgctxt "Title: Reboot/Shut down dialog"
@@ -161,7 +161,7 @@ msgstr ""
161
161
162#: qml/Components/Dialogs.qml:99
162#: qml/Components/Dialogs.qml:99
163msgid "Are you sure you want to shut down?"
163msgid "Are you sure you want to shut down?"
164−msgstr ""
164+msgstr "නිවාදැමිම තහවුරු කරන්න"
165
165
166#: qml/Components/Dialogs.qml:101
166#: qml/Components/Dialogs.qml:101
167msgctxt "Button: Reboot the system"
167msgctxt "Button: Reboot the system"
@@ -180,15 +180,15 @@ msgstr ""
180
180
181#: qml/Components/Dialogs.qml:130
181#: qml/Components/Dialogs.qml:130
182msgid "Are you sure you want to reboot?"
182msgid "Are you sure you want to reboot?"
183−msgstr ""
183+msgstr "යළි පණගැන්වීම තහවුරු කරන්න"
184
184
185#: qml/Components/Dialogs.qml:132
185#: qml/Components/Dialogs.qml:132
186msgid "No"
186msgid "No"
187−msgstr ""
187+msgstr "නැත"
188
188
189#: qml/Components/Dialogs.qml:138
189#: qml/Components/Dialogs.qml:138
190msgid "Yes"
190msgid "Yes"
191−msgstr ""
191+msgstr "ඔව්"
192
192
193#: qml/Components/Dialogs.qml:152
193#: qml/Components/Dialogs.qml:152
194msgctxt "Title: Power off/Restart dialog"
194msgctxt "Title: Power off/Restart dialog"
@@ -200,6 +200,8 @@ msgid ""
200"Are you sure you would like\n"
200"Are you sure you would like\n"
201"to power off?"
201"to power off?"
202msgstr ""
202msgstr ""
203+"පරිගණකය වසාදැමිම\n"
204+"ඔබ තහවුරු කරන්නේද?"
203
205
204#: qml/Components/Dialogs.qml:155
206#: qml/Components/Dialogs.qml:155
205msgctxt "Button: Power off the system"
207msgctxt "Button: Power off the system"
@@ -217,7 +219,7 @@ msgstr ""
217
219
218#: qml/Components/Lockscreen.qml:245
220#: qml/Components/Lockscreen.qml:245
219msgid "Emergency Call"
221msgid "Emergency Call"
220−msgstr ""
222+msgstr "හදිසි ඇමතුම"
221
223
222#: qml/Components/Lockscreen.qml:277
224#: qml/Components/Lockscreen.qml:277
223msgid "OK"
225msgid "OK"
@@ -269,15 +271,15 @@ msgstr ""
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 "මෙය අගය කරන්න"
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 "විචාරයක් ලියන්න"
277
279
278#: qml/Dash/Previews/PreviewRatingInput.qml:169
280#: qml/Dash/Previews/PreviewRatingInput.qml:169
279msgid "Send"
281msgid "Send"
280−msgstr ""
282+msgstr "යවන්න"
281
283
282#: qml/Dash/PullToRefreshScopeStyle.qml:55
284#: qml/Dash/PullToRefreshScopeStyle.qml:55
283msgid "Pull to refresh…"
285msgid "Pull to refresh…"
@@ -372,7 +374,7 @@ msgstr ""
372
374
373#: qml/Notifications/NotificationMenuItemFactory.qml:128
375#: qml/Notifications/NotificationMenuItemFactory.qml:128
374msgid "Show password"
376msgid "Show password"
375−msgstr ""
377+msgstr "මුරපදය පෙන්වන්න"
376
378
377#: qml/Panel/ActiveCallHint.qml:79
379#: qml/Panel/ActiveCallHint.qml:79
378msgid "Tap to return to call..."
380msgid "Tap to return to call..."
@@ -696,3 +698,63 @@ msgstr ""
696#, qt-format
698#, qt-format
697msgid "%1 〉"
699msgid "%1 〉"
698msgstr ""
700msgstr ""
701+
702+#~ msgid "Left edge"
703+#~ msgstr "වම් අන්තය"
704+
705+#~ msgid "Try swiping from the right edge to unlock the phone"
706+#~ msgstr "දුරකථනය අගුලුහරින්න දකුණු අන්තයේ සිට අදින්න"
707+
708+#~ msgid "Right edge"
709+#~ msgstr "දකුණු අන්තය"
710+
711+#~ msgid "Close"
712+#~ msgstr "වසන්න"
713+
714+#~ msgid "Skip intro"
715+#~ msgstr "හැඳින්වීම මඟහරින්න"
716+
717+#~ msgid "Loading. Please Wait..."
718+#~ msgstr "පූරණය වෙමින්. මඳක් රැඳෙන්න..."
719+
720+#~ msgid "Speaking..."
721+#~ msgstr "කතාකරමින්..."
722+
723+#~ msgid "Speak Now..."
724+#~ msgstr "දැන් කථා කරන්න..."
725+
726+#~ msgid "Confirm"
727+#~ msgstr "ස්ථීර කරන්න"
728+
729+#~ msgid "Top edge"
730+#~ msgstr "ඉහළ අන්තය"
731+
732+#~ msgid "Try swiping from the top edge to access the indicators"
733+#~ msgstr "දර්ශකවලට ප්‍රවේශවන්න ඉහළ අන්තයේ සිට අදින්න"
734+
735+#~ msgid "Well done"
736+#~ msgstr "කදිමයි"
737+
738+#~ msgid "Search"
739+#~ msgstr "සෙවුම"
740+
741+#~ msgid "Type or say a command"
742+#~ msgstr "විධානයක් ඇතුල්කරන්න හෝ කියන්න"
743+
744+#~ msgid "Log out"
745+#~ msgstr "ඉවත් වන්න"
746+
747+#~ msgid "Reboot"
748+#~ msgstr "යළි පණගන්වන්න"
749+
750+#~ msgid "Shut down"
751+#~ msgstr "පරිගණකය නිවා දමන්න"
752+
753+#~ msgid "Power"
754+#~ msgstr "බලය"
755+
756+#~ msgid "Restart"
757+#~ msgstr "පරිගණකය යළි පණගන්වන්න"
758+
759+#~ msgid "Power off"
760+#~ msgstr "පරිගණකය නිවා දමන්න"
po/sk.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
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=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
17"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/sl.po+2−2
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
17"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
18"n%100==4 ? 3 : 0);\n"
18"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
po/sq.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:38+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/sr.po+2−2
@@ -17,8 +17,8 @@ msgstr ""
17"Content-Transfer-Encoding: 8bit\n"
17"Content-Transfer-Encoding: 8bit\n"
18"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
19"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
20−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
20+"X-Launchpad-Export-Date: 2015-08-20 06:00+0000\n"
21−"X-Generator: Launchpad (build 17628)\n"
21+"X-Generator: Launchpad (build 17690)\n"
22"Language: sr\n"
22"Language: sr\n"
23
23
24#: plugins/LightDM/Greeter.cpp:112
24#: plugins/LightDM/Greeter.cpp:112
po/sv.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20"Language: sv\n"
20"Language: sv\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
po/sw.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/ta.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20"Language: ta\n"
20"Language: ta\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
po/te.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/th.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/tr.po+40−40
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -37,14 +37,14 @@ msgstr "Kısayol iğnesini kaldır"
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 "Dün %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.
44#. en_US example: "%l:%M %p" --> "1:00 PM"
44#. en_US example: "%l:%M %p" --> "1:00 PM"
45#: plugins/Utils/relativetimeformatter.cpp:164
45#: plugins/Utils/relativetimeformatter.cpp:164
46msgid "%l:%M %p"
46msgid "%l:%M %p"
47−msgstr ""
47+msgstr "%l:%M %p"
48
48
49#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
50#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
@@ -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 "Yarın %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.
@@ -60,7 +60,7 @@ msgstr ""
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 "%l:%M %p %a"
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.
@@ -69,7 +69,7 @@ msgstr ""
69#. en_GB example: "%a %b %d\u2003%l:%M %p" --> "Fri 31 Oct 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
70#: plugins/Utils/relativetimeformatter.cpp:190
71msgid "%a %d %b %l:%M %p"
71msgid "%a %d %b %l:%M %p"
72−msgstr ""
72+msgstr "%a %d %b %l:%M %p"
73
73
74#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
75#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
@@ -77,14 +77,14 @@ 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 "Dün %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.
84#. en_US example: "%H:%M" --> "13:00"
84#. en_US example: "%H:%M" --> "13:00"
85#: plugins/Utils/relativetimeformatter.cpp:208
85#: plugins/Utils/relativetimeformatter.cpp:208
86msgid "%H:%M"
86msgid "%H:%M"
87−msgstr ""
87+msgstr "%H:%M"
88
88
89#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
90#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
@@ -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 "Yarın %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.
@@ -100,7 +100,7 @@ msgstr ""
100#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
100#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
101#: plugins/Utils/relativetimeformatter.cpp:225
101#: plugins/Utils/relativetimeformatter.cpp:225
102msgid "%a %H:%M"
102msgid "%a %H:%M"
103−msgstr ""
103+msgstr "%H:%M %a"
104
104
105#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
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.
106#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
@@ -109,7 +109,7 @@ msgstr ""
109#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
109#. en_GB example: "%a %b %d\u2003%H:%M" --> "Fri 31 Oct 13:00"
110#: plugins/Utils/relativetimeformatter.cpp:234
110#: plugins/Utils/relativetimeformatter.cpp:234
111msgid "%a %d %b %H:%M"
111msgid "%a %d %b %H:%M"
112−msgstr ""
112+msgstr "%a %b %d\\u2003%H:%M"
113
113
114#: qml/Components/DelayedLockscreen.qml:49
114#: qml/Components/DelayedLockscreen.qml:49
115msgid "Device Locked"
115msgid "Device Locked"
@@ -133,7 +133,7 @@ msgstr[1] "Lütfen %1 dakika bekleyin ve yeniden deneyin..."
133#: qml/Components/Dialogs.qml:69
133#: qml/Components/Dialogs.qml:69
134msgctxt "Title: Lock/Log out dialog"
134msgctxt "Title: Lock/Log out dialog"
135msgid "Log out"
135msgid "Log out"
136−msgstr ""
136+msgstr "Oturumu kapat"
137
137
138#: qml/Components/Dialogs.qml:70
138#: qml/Components/Dialogs.qml:70
139msgid "Are you sure you want to log out?"
139msgid "Are you sure you want to log out?"
@@ -142,12 +142,12 @@ msgstr "Oturumu kapatmak istediğinize emin misiniz?"
142#: qml/Components/Dialogs.qml:72
142#: qml/Components/Dialogs.qml:72
143msgctxt "Button: Lock the system"
143msgctxt "Button: Lock the system"
144msgid "Lock"
144msgid "Lock"
145−msgstr ""
145+msgstr "Kilitle"
146
146
147#: qml/Components/Dialogs.qml:79
147#: qml/Components/Dialogs.qml:79
148msgctxt "Button: Log out from the system"
148msgctxt "Button: Log out from the system"
149msgid "Log Out"
149msgid "Log Out"
150−msgstr ""
150+msgstr "Oturumu Kapat"
151
151
152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
152#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
153#: qml/Components/Dialogs.qml:173
153#: qml/Components/Dialogs.qml:173
@@ -157,7 +157,7 @@ msgstr "İptal"
157#: qml/Components/Dialogs.qml:98
157#: qml/Components/Dialogs.qml:98
158msgctxt "Title: Reboot/Shut down dialog"
158msgctxt "Title: Reboot/Shut down dialog"
159msgid "Shut down"
159msgid "Shut down"
160−msgstr ""
160+msgstr "Kapat"
161
161
162#: qml/Components/Dialogs.qml:99
162#: qml/Components/Dialogs.qml:99
163msgid "Are you sure you want to shut down?"
163msgid "Are you sure you want to shut down?"
@@ -171,7 +171,7 @@ msgstr "Yeniden başlat"
171#: qml/Components/Dialogs.qml:109
171#: qml/Components/Dialogs.qml:109
172msgctxt "Button: Shut down the system"
172msgctxt "Button: Shut down the system"
173msgid "Shut down"
173msgid "Shut down"
174−msgstr ""
174+msgstr "Kapat"
175
175
176#: qml/Components/Dialogs.qml:129
176#: qml/Components/Dialogs.qml:129
177msgctxt "Title: Reboot dialog"
177msgctxt "Title: Reboot dialog"
@@ -204,7 +204,7 @@ msgstr "Bilgisayarı kapatmak istediğinize emin misiniz?"
204#: qml/Components/Dialogs.qml:155
204#: qml/Components/Dialogs.qml:155
205msgctxt "Button: Power off the system"
205msgctxt "Button: Power off the system"
206msgid "Power off"
206msgid "Power off"
207−msgstr ""
207+msgstr "Sistemi Kapat"
208
208
209#: qml/Components/Dialogs.qml:164
209#: qml/Components/Dialogs.qml:164
210msgctxt "Button: Restart the system"
210msgctxt "Button: Restart the system"
@@ -309,7 +309,7 @@ msgstr "Kilidi Aç"
309
309
310#: qml/Greeter/LoginList.qml:236
310#: qml/Greeter/LoginList.qml:236
311msgid "Retry"
311msgid "Retry"
312−msgstr ""
312+msgstr "Tekrar Dene"
313
313
314#: qml/Greeter/LoginList.qml:237
314#: qml/Greeter/LoginList.qml:237
315msgid "Tap to unlock"
315msgid "Tap to unlock"
@@ -368,7 +368,7 @@ msgstr "Üzgünüm, hatalı parola"
368#: qml/Greeter/NarrowView.qml:121
368#: qml/Greeter/NarrowView.qml:121
369msgctxt "passphrase"
369msgctxt "passphrase"
370msgid "Please re-enter"
370msgid "Please re-enter"
371−msgstr ""
371+msgstr "Lütfen yeniden girin"
372
372
373#: qml/Greeter/NarrowView.qml:122
373#: qml/Greeter/NarrowView.qml:122
374msgid "Sorry, incorrect passcode"
374msgid "Sorry, incorrect passcode"
@@ -394,32 +394,32 @@ msgstr "Hiçbir şey çalmıyor"
394#, qt-format
394#, qt-format
395msgid "%1 hour"
395msgid "%1 hour"
396msgid_plural "%1 hours"
396msgid_plural "%1 hours"
397−msgstr[0] ""
397+msgstr[0] "%1 saat"
398−msgstr[1] ""
398+msgstr[1] "%1 saat"
399
399
400#: qml/Panel/Indicators/MenuItemFactory.qml:772
400#: qml/Panel/Indicators/MenuItemFactory.qml:772
401#, qt-format
401#, qt-format
402msgid "%1 minute"
402msgid "%1 minute"
403msgid_plural "%1 minutes"
403msgid_plural "%1 minutes"
404−msgstr[0] ""
404+msgstr[0] "%1 dakika"
405−msgstr[1] ""
405+msgstr[1] "%1 dakika"
406
406
407#: qml/Panel/Indicators/MenuItemFactory.qml:777
407#: qml/Panel/Indicators/MenuItemFactory.qml:777
408#, qt-format
408#, qt-format
409msgid "%1 second"
409msgid "%1 second"
410msgid_plural "%1 seconds"
410msgid_plural "%1 seconds"
411−msgstr[0] ""
411+msgstr[0] "%1 saniye"
412−msgstr[1] ""
412+msgstr[1] "%1 saniye"
413
413
414#: qml/Panel/Indicators/MenuItemFactory.qml:780
414#: qml/Panel/Indicators/MenuItemFactory.qml:780
415msgid "0 seconds"
415msgid "0 seconds"
416−msgstr ""
416+msgstr "0 saniye"
417
417
418#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
418#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
419#: qml/Panel/Indicators/MenuItemFactory.qml:782
419#: qml/Panel/Indicators/MenuItemFactory.qml:782
420#, qt-format
420#, qt-format
421msgid "%1 remaining"
421msgid "%1 remaining"
422−msgstr ""
422+msgstr "%1 kaldı"
423
423
424#: qml/Panel/Indicators/MenuItemFactory.qml:788
424#: qml/Panel/Indicators/MenuItemFactory.qml:788
425msgid "In queue…"
425msgid "In queue…"
@@ -481,23 +481,23 @@ msgstr "Devam etmek için buraya dokunun."
481
481
482#: qml/Tutorial/TutorialContent.qml:81
482#: qml/Tutorial/TutorialContent.qml:81
483msgid "Click here to finish."
483msgid "Click here to finish."
484−msgstr ""
484+msgstr "Bitirmek için buraya tıklayın."
485
485
486#: qml/Tutorial/TutorialLeftFinish.qml:24
486#: qml/Tutorial/TutorialLeftFinish.qml:24
487msgid "These are the shortcuts to favorite apps"
487msgid "These are the shortcuts to favorite apps"
488−msgstr ""
488+msgstr "Bunlar gözde uygulamalarınız için kısayollardır"
489
489
490#: qml/Tutorial/TutorialLeft.qml:26
490#: qml/Tutorial/TutorialLeft.qml:26
491msgid "Open the launcher"
491msgid "Open the launcher"
492−msgstr ""
492+msgstr "Başlatıcıyı aç"
493
493
494#: qml/Tutorial/TutorialLeft.qml:27
494#: qml/Tutorial/TutorialLeft.qml:27
495msgid "Short swipe from the left edge."
495msgid "Short swipe from the left edge."
496−msgstr ""
496+msgstr "Sol kenardan kısa kaydırma"
497
497
498#: qml/Tutorial/TutorialPage.qml:178
498#: qml/Tutorial/TutorialPage.qml:178
499msgid "You almost got it!"
499msgid "You almost got it!"
500−msgstr ""
500+msgstr "Neredeyse yaptınız!"
501
501
502#: qml/Tutorial/TutorialPage.qml:195
502#: qml/Tutorial/TutorialPage.qml:195
503msgid "Try again."
503msgid "Try again."
@@ -509,7 +509,7 @@ msgstr "Açık uygulamaları görmek için"
509
509
510#: qml/Tutorial/TutorialRight.qml:32
510#: qml/Tutorial/TutorialRight.qml:32
511msgid "Long swipe from the right edge."
511msgid "Long swipe from the right edge."
512−msgstr ""
512+msgstr "Sağ kenardan uzan kaydırma"
513
513
514#: qml/Tutorial/TutorialRight.qml:122
514#: qml/Tutorial/TutorialRight.qml:122
515msgid "View all your running tasks."
515msgid "View all your running tasks."
@@ -518,7 +518,7 @@ msgstr "Çalışan tüm görevleri incele."
518#: qml/Wizard/Page.qml:89
518#: qml/Wizard/Page.qml:89
519msgctxt "Button: Go back one page in the Wizard"
519msgctxt "Button: Go back one page in the Wizard"
520msgid "Back"
520msgid "Back"
521−msgstr ""
521+msgstr "Geri"
522
522
523#: qml/Wizard/Pages/10-welcome.qml:27
523#: qml/Wizard/Pages/10-welcome.qml:27
524msgid "Hi!"
524msgid "Hi!"
@@ -563,27 +563,27 @@ msgstr "Lütfen telefonunuzun kilidini nasıl açmak istediğinizi seçin."
563#: qml/Wizard/Pages/30-passwd-type.qml:97
563#: qml/Wizard/Pages/30-passwd-type.qml:97
564msgctxt "Label: Type of security method"
564msgctxt "Label: Type of security method"
565msgid "Swipe"
565msgid "Swipe"
566−msgstr ""
566+msgstr "Kaydır"
567
567
568#: qml/Wizard/Pages/30-passwd-type.qml:98
568#: qml/Wizard/Pages/30-passwd-type.qml:98
569msgctxt "Label: Description of security method"
569msgctxt "Label: Description of security method"
570msgid "No security"
570msgid "No security"
571−msgstr ""
571+msgstr "Güvenlik yok"
572
572
573#: qml/Wizard/Pages/30-passwd-type.qml:100
573#: qml/Wizard/Pages/30-passwd-type.qml:100
574msgctxt "Label: Type of security method"
574msgctxt "Label: Type of security method"
575msgid "Passcode"
575msgid "Passcode"
576−msgstr ""
576+msgstr "Parola"
577
577
578#: qml/Wizard/Pages/30-passwd-type.qml:101
578#: qml/Wizard/Pages/30-passwd-type.qml:101
579msgctxt "Label: Description of security method"
579msgctxt "Label: Description of security method"
580msgid "4 digits only"
580msgid "4 digits only"
581−msgstr ""
581+msgstr "Yalnızca 4 rakam"
582
582
583#: qml/Wizard/Pages/30-passwd-type.qml:103
583#: qml/Wizard/Pages/30-passwd-type.qml:103
584msgctxt "Label: Type of security method"
584msgctxt "Label: Type of security method"
585msgid "Passphrase"
585msgid "Passphrase"
586−msgstr ""
586+msgstr "Parola Metni"
587
587
588#: qml/Wizard/Pages/30-passwd-type.qml:104
588#: qml/Wizard/Pages/30-passwd-type.qml:104
589msgctxt "Label: Description of security method"
589msgctxt "Label: Description of security method"
po/ug.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/uk.po+3−3
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
19+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
20−"X-Generator: Launchpad (build 17628)\n"
20+"X-Generator: Launchpad (build 17690)\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
23msgid "Password: "
23msgid "Password: "
@@ -376,7 +376,7 @@ msgstr "Вибачте, помилковий пароль"
376#: qml/Greeter/NarrowView.qml:121
376#: qml/Greeter/NarrowView.qml:121
377msgctxt "passphrase"
377msgctxt "passphrase"
378msgid "Please re-enter"
378msgid "Please re-enter"
379−msgstr ""
379+msgstr "Введіть ще раз"
380
380
381#: qml/Greeter/NarrowView.qml:122
381#: qml/Greeter/NarrowView.qml:122
382msgid "Sorry, incorrect passcode"
382msgid "Sorry, incorrect passcode"
po/ur.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/uz.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/vi.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/xh.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/zh_CN.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-28 05:40+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/zh_HK.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-07-25 05:39+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17628)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/zh_TW.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-08-01 05:45+0000\n"
18+"X-Launchpad-Export-Date: 2015-08-20 06:01+0000\n"
19−"X-Generator: Launchpad (build 17656)\n"
19+"X-Generator: Launchpad (build 17690)\n"
20"Language: zh_TW\n"
20"Language: zh_TW\n"
21
21
22#: plugins/LightDM/Greeter.cpp:112
22#: plugins/LightDM/Greeter.cpp:112
qml/Components/PhysicalKeysMapper.qml+24
@@ -41,14 +41,23 @@ Item {
41 signal volumeUpTriggered;
41 signal volumeUpTriggered;
42 signal screenshotTriggered;
42 signal screenshotTriggered;
43
43
44+ readonly property bool altTabPressed: d.altTabPressed
45+
44 property int powerKeyLongPressTime: 2000
46 property int powerKeyLongPressTime: 2000
45
47
48+ // For testing. If running windowed (e.g. tryShell), Alt+Tab is taken by the
49+ // running desktop, set this to true to use Ctrl+Tab instead.
50+ property bool controlInsteadOfAlt: false
51+
46 QtObject {
52 QtObject {
47 id: d
53 id: d
48
54
49 property bool volumeDownKeyPressed: false
55 property bool volumeDownKeyPressed: false
50 property bool volumeUpKeyPressed: false
56 property bool volumeUpKeyPressed: false
51 property bool ignoreVolumeEvents: false
57 property bool ignoreVolumeEvents: false
58+
59+ property bool altPressed: false
60+ property bool altTabPressed: false
52 }
61 }
53
62
54 Timer {
63 Timer {
@@ -93,6 +102,15 @@ Item {
93 }
102 }
94 d.volumeUpKeyPressed = true;
103 d.volumeUpKeyPressed = true;
95 }
104 }
105+ } else if (event.key == Qt.Key_Alt || (root.controlInsteadOfAlt && event.key == Qt.Key_Control)) {
106+ d.altPressed = true;
107+ } else if (event.key == Qt.Key_Tab) {
108+ if (d.altPressed && !d.altTabPressed) {
109+ d.altTabPressed = true;
110+ event.accepted = true;
111+ }
112+ } else if (event.key == Qt.Key_Print) {
113+ root.screenshotTriggered();
96 }
114 }
97 }
115 }
98
116
@@ -108,6 +126,12 @@ Item {
108 if (!d.ignoreVolumeEvents) root.volumeUpTriggered();
126 if (!d.ignoreVolumeEvents) root.volumeUpTriggered();
109 d.volumeUpKeyPressed = false;
127 d.volumeUpKeyPressed = false;
110 if (!d.volumeDownKeyPressed) d.ignoreVolumeEvents = false;
128 if (!d.volumeDownKeyPressed) d.ignoreVolumeEvents = false;
129+ } else if (event.key == Qt.Key_Alt || (root.controlInsteadOfAlt && event.key == Qt.Key_Control)) {
130+ d.altPressed = false;
131+ if (d.altTabPressed) {
132+ d.altTabPressed = false;
133+ event.accepted = true;
134+ }
111 }
135 }
112 }
136 }
113}
137}
qml/Components/ScreenGrabber.qml+1−5
@@ -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
@@ -20,7 +20,6 @@ import ScreenGrabber 0.1
20
20
21Rectangle {
21Rectangle {
22 id: root
22 id: root
23− enabled: true
24 visible: false
23 visible: false
25 color: "white"
24 color: "white"
26 anchors.fill: parent
25 anchors.fill: parent
@@ -38,9 +37,6 @@ Rectangle {
38 }
37 }
39
38
40 function capture() {
39 function capture() {
41− if (!enabled)
42− return;
43−
44 visible = true;
40 visible = true;
45 shutterSound.stop();
41 shutterSound.stop();
46 shutterSound.play();
42 shutterSound.play();
qml/Components/Unity8Settings.qmlnew file+50
@@ -0,0 +1,50 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import GSettings 1.0
19+
20+QtObject {
21+ id: root
22+
23+ property string usageMode: "Staged"
24+
25+ // FIXME: Works around a bug where if we change a Loader's source in response to a GSettings
26+ // property change in the same event loop, the Loader's previously loaded component does not
27+ // get destroyed and its bindings continue to operate!
28+ //
29+ // Shouldn't be needed after
30+ // https://code.launchpad.net/~lukas-kde/gsettings-qt/queued-processing/+merge/259883 gets
31+ // merged.
32+ property var timer: Timer {
33+ interval: 1
34+ onTriggered: { root.usageMode = root.wrapped.usageMode; }
35+ }
36+ property var wrappedConnections: Connections {
37+ target: root.wrapped
38+ ignoreUnknownSignals: true // don't spam us
39+ onUsageModeChanged: { root.timer.start(); }
40+ }
41+ property var wrapped: GSettings {
42+ schema.id: "com.canonical.Unity8"
43+ Component.onCompleted: {
44+ // init the value. it's a dynamic prop, so we have to check first
45+ if (root.usageMode) {
46+ root.usageMode = root.wrapped.usageMode;
47+ }
48+ }
49+ }
50+}
qml/Components/UnityInputInfo/UnityInputInfo.qml+2
@@ -18,6 +18,8 @@ pragma Singleton
18
18
19import QtQuick 2.3
19import QtQuick 2.3
20import Unity.InputInfo 0.1
20import Unity.InputInfo 0.1
21+// Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
22+import Ubuntu.Components 1.2
21
23
22Item {
24Item {
23 id: root
25 id: root
qml/Dash/Dash.qml+1−1
@@ -41,7 +41,7 @@ Showable {
41 onSetCurrentScopeRequested: {
41 onSetCurrentScopeRequested: {
42 if (!isSwipe || !window.active || bottomEdgeController.progress != 0 || scopeItem.scope || dashContent.subPageShown) {
42 if (!isSwipe || !window.active || bottomEdgeController.progress != 0 || scopeItem.scope || dashContent.subPageShown) {
43 if (bottomEdgeController.progress != 0 && window.active) animate = false;
43 if (bottomEdgeController.progress != 0 && window.active) animate = false;
44− dashContent.setCurrentScopeAtIndex(index, animate, isSwipe)
44+ dashContent.setCurrentScopeAtIndex(index, animate, true)
45 backToDashContent()
45 backToDashContent()
46 }
46 }
47 }
47 }
qml/Dash/DashContent.qml+1
@@ -84,6 +84,7 @@ Item {
84
84
85 if (reset) {
85 if (reset) {
86 dashContentList.currentItem.item.positionAtBeginning()
86 dashContentList.currentItem.item.positionAtBeginning()
87+ dashContentList.currentItem.item.resetSearch()
87 }
88 }
88 }
89 }
89
90
qml/Dash/GenericScopeView.qml+66−31
@@ -67,38 +67,57 @@ FocusScope {
67 subPageLoader.closeSubPage()
67 subPageLoader.closeSubPage()
68 }
68 }
69
69
70− function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
70+ function resetSearch() {
71− if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope" || (scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
71+ if(pageHeaderLoader.item && showPageHeader)
72− // TODO Technically it is possible that calling activate() will make the scope emit
72+ pageHeaderLoader.item.resetSearch()
73− // previewRequested so that we show a preview but there's no scope that does that yet
74− // so it's not implemented
75− scope.activate(result)
76− } else {
77− if (scope.preview(result)) {
78− openPreview(index, resultsModel, limitedCategoryItemCount);
79− }
80− }
81 }
73 }
82
74
75+ property var maybePreviewResult;
76+ property int maybePreviewIndex;
77+ property var maybePreviewResultsModel;
78+ property int maybePreviewLimitedCategoryItemCount;
79+ property string maybePreviewCategoryId;
80+
81+ function clearMaybePreviewData() {
82+ scopeView.maybePreviewResult = undefined;
83+ scopeView.maybePreviewIndex = -1;
84+ scopeView.maybePreviewResultsModel = undefined;
85+ scopeView.maybePreviewLimitedCategoryItemCount = -1;
86+ scopeView.maybePreviewCategoryId = "";
87+ }
88+
83− function itemPressedAndHeld(index, result, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
89+ function itemClicked(index, result, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
84− if (itemModel.uri.indexOf("scope://") !== 0 && !(scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
90+ scopeView.maybePreviewResult = result;
91+ scopeView.maybePreviewIndex = index;
92+ scopeView.maybePreviewResultsModel = resultsModel;
93+ scopeView.maybePreviewLimitedCategoryItemCount = limitedCategoryItemCount;
94+ scopeView.maybePreviewCategoryId = categoryId;
95+
85− if (scope.preview(result)) {
96+ scope.activate(result, categoryId);
86− openPreview(index, resultsModel, limitedCategoryItemCount);
87− }
88− }
89 }
97 }
90
98
99+ function itemPressedAndHeld(index, result, resultsModel, limitedCategoryItemCount, categoryId) {
100+ clearMaybePreviewData();
101+
91− function openPreview(index, resultsModel, limitedCategoryItemCount) {
102+ openPreview(result, index, resultsModel, limitedCategoryItemCount, categoryId);
103+ }
104+
105+ function openPreview(result, index, resultsModel, limitedCategoryItemCount, categoryId) {
106+ var previewStack = scope.preview(result, categoryId);
107+ if (previewStack) {
92− if (limitedCategoryItemCount > 0) {
108+ if (limitedCategoryItemCount > 0) {
93− previewLimitModel.model = resultsModel;
109+ previewLimitModel.model = resultsModel;
94− previewLimitModel.limit = limitedCategoryItemCount;
110+ previewLimitModel.limit = limitedCategoryItemCount;
95− subPageLoader.model = previewLimitModel;
111+ subPageLoader.model = previewLimitModel;
96− } else {
112+ } else {
97− subPageLoader.model = resultsModel;
113+ subPageLoader.model = resultsModel;
114+ }
115+ subPageLoader.initialIndex = -1;
116+ subPageLoader.initialIndex = index;
117+ subPageLoader.categoryId = categoryId;
118+ subPageLoader.previewStack = previewStack;
119+ subPageLoader.openSubPage("preview");
98 }
120 }
99− subPageLoader.initialIndex = -1;
100− subPageLoader.initialIndex = index;
101− subPageLoader.openSubPage("preview");
102 }
121 }
103
122
104 Binding {
123 Binding {
@@ -144,6 +163,17 @@ FocusScope {
144 target: scopeView.scope
163 target: scopeView.scope
145 onShowDash: subPageLoader.closeSubPage()
164 onShowDash: subPageLoader.closeSubPage()
146 onHideDash: subPageLoader.closeSubPage()
165 onHideDash: subPageLoader.closeSubPage()
166+ onPreviewRequested: { // (QVariant const& result)
167+ if (result === scopeView.maybePreviewResult) {
168+ openPreview(result,
169+ scopeView.maybePreviewIndex,
170+ scopeView.maybePreviewResultsModel,
171+ scopeView.maybePreviewLimitedCategoryItemCount,
172+ scopeView.maybePreviewCategoryId);
173+
174+ clearMaybePreviewData();
175+ }
176+ }
147 }
177 }
148
178
149 Connections {
179 Connections {
@@ -352,12 +382,12 @@ FocusScope {
352
382
353 Connections {
383 Connections {
354 target: rendererLoader.item
384 target: rendererLoader.item
355− onClicked: {
385+ onClicked: { // (int index, var result, var item, var itemModel)
356− scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount(), baseItem.category);
386+ scopeView.itemClicked(index, result, itemModel, target.model, categoryItemCount(), baseItem.category);
357 }
387 }
358
388
359− onPressAndHold: {
389+ onPressAndHold: { // (int index, var result, var itemModel)
360− scopeView.itemPressedAndHeld(index, result, itemModel, target.model, categoryItemCount(), baseItem.category);
390+ scopeView.itemPressedAndHeld(index, result, target.model, categoryItemCount(), baseItem.category);
361 }
391 }
362
392
363 function categoryItemCount() {
393 function categoryItemCount() {
@@ -731,6 +761,8 @@ FocusScope {
731 property var scope: scopeView.scope
761 property var scope: scopeView.scope
732 property var scopeStyle: scopeView.scopeStyle
762 property var scopeStyle: scopeView.scopeStyle
733 property int initialIndex: -1
763 property int initialIndex: -1
764+ property var previewStack;
765+ property string categoryId
734 property var model: null
766 property var model: null
735
767
736 readonly property bool processing: item && item.processing || false
768 readonly property bool processing: item && item.processing || false
@@ -762,6 +794,9 @@ FocusScope {
762 item.open = Qt.binding(function() { return subPageLoader.open; } )
794 item.open = Qt.binding(function() { return subPageLoader.open; } )
763 item.initialIndex = Qt.binding(function() { return subPageLoader.initialIndex; } )
795 item.initialIndex = Qt.binding(function() { return subPageLoader.initialIndex; } )
764 item.model = Qt.binding(function() { return subPageLoader.model; } )
796 item.model = Qt.binding(function() { return subPageLoader.model; } )
797+ item.categoryId = Qt.binding(function() { return subPageLoader.categoryId; } )
798+ item.initialIndexPreviewStack = subPageLoader.previewStack;
799+ subPageLoader.previewStack = null;
765 }
800 }
766 open = true;
801 open = true;
767 }
802 }
qml/Dash/PreviewListView.qml+17−6
@@ -24,8 +24,11 @@ Item {
24 id: root
24 id: root
25
25
26 property int initialIndex: -1
26 property int initialIndex: -1
27+ property var initialIndexPreviewStack: null
27 property var scope: null
28 property var scope: null
28 property var scopeStyle: null
29 property var scopeStyle: null
30+ property string categoryId
31+ property bool usedInitialIndex: false
29
32
30 property alias showSignatureLine: header.showSignatureLine
33 property alias showSignatureLine: header.showSignatureLine
31
34
@@ -83,10 +86,10 @@ Item {
83 }
86 }
84 }
87 }
85
88
86− onCountChanged: {
89+ onModelChanged: {
87− if (count > 0 && initialIndex >= 0) {
90+ if (count > 0 && initialIndex >= 0 && !usedInitialIndex) {
88− currentIndex = initialIndex;
91+ usedInitialIndex = true;
89− initialIndex = -1;
92+ previewListView.positionViewAtIndex(initialIndex, ListView.SnapPosition);
90 }
93 }
91 }
94 }
92
95
@@ -99,8 +102,16 @@ Item {
99 isCurrent: ListView.isCurrentItem
102 isCurrent: ListView.isCurrentItem
100
103
101 previewModel: {
104 previewModel: {
105+ if (root.open) {
106+ if (index === root.initialIndex) {
107+ return root.initialIndexPreviewStack.getPreviewModel(0);
108+ } else {
102− var previewStack = root.scope.preview(result);
109+ var previewStack = root.scope.preview(result, root.categoryId);
103− return previewStack.getPreviewModel(0);
110+ return previewStack.getPreviewModel(0);
111+ }
112+ } else {
113+ return null;
114+ }
104 }
115 }
105 scopeStyle: root.scopeStyle
116 scopeStyle: root.scopeStyle
106 }
117 }
qml/Dash/Previews/PreviewActions.qml+1−1
@@ -25,7 +25,7 @@ import Ubuntu.Components 1.1
25PreviewWidget {
25PreviewWidget {
26 id: root
26 id: root
27
27
28− height: row.height + units.gu(1)
28+ implicitHeight: row.height + units.gu(1)
29
29
30 Row {
30 Row {
31 id: row
31 id: row
qml/Dash/Previews/PreviewAudioPlayback.qml+1−1
@@ -34,7 +34,7 @@ import Ubuntu.Components 0.1
34
34
35PreviewWidget {
35PreviewWidget {
36 id: root
36 id: root
37− height: childrenRect.height
37+ implicitHeight: childrenRect.height
38
38
39 onIsCurrentPreviewChanged: if (!isCurrentPreview) audio.stop()
39 onIsCurrentPreviewChanged: if (!isCurrentPreview) audio.stop()
40
40
qml/Dash/Previews/PreviewHeader.qml+1−1
@@ -30,7 +30,7 @@ import "../"
30PreviewWidget {
30PreviewWidget {
31 id: root
31 id: root
32
32
33− height: childrenRect.height
33+ implicitHeight: childrenRect.height
34
34
35 Item {
35 Item {
36 id: headerRoot
36 id: headerRoot
qml/Dash/Previews/PreviewRatingDisplay.qml+1−1
@@ -38,7 +38,7 @@ import "../../Components"
38
38
39PreviewWidget {
39PreviewWidget {
40 id: root
40 id: root
41− height: childrenRect.height
41+ implicitHeight: childrenRect.height
42
42
43 Column {
43 Column {
44 anchors { left: parent.left; right: parent.right; }
44 anchors { left: parent.left; right: parent.right; }
qml/Dash/ScopesList.qml+1−1
@@ -124,7 +124,7 @@ Item {
124 onRequestFavorite: root.requestFavorite(scopeId, favorite);
124 onRequestFavorite: root.requestFavorite(scopeId, favorite);
125 onRequestEditMode: root.state = "edit";
125 onRequestEditMode: root.state = "edit";
126 onRequestScopeMoveTo: root.requestFavoriteMoveTo(scopeId, index);
126 onRequestScopeMoveTo: root.requestFavoriteMoveTo(scopeId, index);
127− onRequestActivate: root.scope.activate(result);
127+ onRequestActivate: root.scope.activate(result, categoryId);
128 onRequestRestore: root.requestRestore(scopeId);
128 onRequestRestore: root.requestRestore(scopeId);
129 }
129 }
130 }
130 }
qml/Launcher/Launcher.qml+2−1
@@ -177,7 +177,8 @@ Item {
177 }
177 }
178
178
179 }
179 }
180− MouseArea {
180+
181+ MultiPointTouchArea {
181 id: closeMouseArea
182 id: closeMouseArea
182 anchors {
183 anchors {
183 left: launcherDragArea.right
184 left: launcherDragArea.right
qml/Launcher/LauncherDelegate.qml+113−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
@@ -24,9 +24,12 @@ Item {
24 property int count: 0
24 property int count: 0
25 property bool countVisible: false
25 property bool countVisible: false
26 property int progress: -1
26 property int progress: -1
27+ property bool itemRunning: false
27 property bool itemFocused: false
28 property bool itemFocused: false
28 property real maxAngle: 0
29 property real maxAngle: 0
29 property bool inverted: false
30 property bool inverted: false
31+ property bool alerting: false
32+ readonly property alias wiggling: wiggleAnim.running
30
33
31 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
34 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
32 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
35 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
@@ -39,6 +42,82 @@ Item {
39 property real offset: 0
42 property real offset: 0
40 property real itemOpacity: 1
43 property real itemOpacity: 1
41 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+ }
42
121
43 Item {
122 Item {
44 id: iconItem
123 id: iconItem
@@ -75,7 +154,7 @@ Item {
75 objectName: "countEmblem"
154 objectName: "countEmblem"
76 anchors {
155 anchors {
77 right: parent.right
156 right: parent.right
78− top: parent.top
157+ bottom: parent.bottom
79 margins: units.dp(3)
158 margins: units.dp(3)
80 }
159 }
81 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)))
@@ -100,23 +179,21 @@ Item {
100 }
179 }
101 }
180 }
102
181
103− BorderImage {
182+ UbuntuShape {
104 id: progressOverlay
183 id: progressOverlay
105 objectName: "progressOverlay"
184 objectName: "progressOverlay"
185+
106 anchors {
186 anchors {
107 left: iconItem.left
187 left: iconItem.left
108 right: iconItem.right
188 right: iconItem.right
109− bottom: iconItem.bottom
189+ verticalCenter: parent.verticalCenter
110− leftMargin: units.gu(1)
190+ leftMargin: units.gu(1.5)
111− rightMargin: units.gu(1)
191+ rightMargin: units.gu(1.5)
112− bottomMargin: units.gu(1)
113 }
192 }
114− height: units.gu(1.5)
193+ height: units.gu(1)
115 visible: root.progress > -1
194 visible: root.progress > -1
116− source: "graphics/progressbar-trough.sci"
195+ color: UbuntuColors.darkGrey
117−
196+ borderSource: "none"
118− // For fill calculation we need to remove the 2 units of border defined in .sci file
119− property int adjustedWidth: width - units.gu(2)
120
197
121 Item {
198 Item {
122 anchors {
199 anchors {
@@ -124,20 +201,33 @@ Item {
124 top: parent.top
201 top: parent.top
125 bottom: parent.bottom
202 bottom: parent.bottom
126 }
203 }
127− width: Math.min(100, root.progress) / 100 * parent.adjustedWidth + units.gu(1)
204+ width: Math.min(100, root.progress) / 100 * parent.width
128 clip: true
205 clip: true
129
206
130− BorderImage {
207+ UbuntuShape {
131 anchors {
208 anchors {
132 left: parent.left
209 left: parent.left
133 top: parent.top
210 top: parent.top
134 bottom: parent.bottom
211 bottom: parent.bottom
135 }
212 }
213+ color: "white"
214+ borderSource: "none"
136 width: progressOverlay.width
215 width: progressOverlay.width
137− source: "graphics/progressbar-fill.sci"
138 }
216 }
139 }
217 }
140 }
218 }
219+
220+ Image {
221+ objectName: "runningHighlight"
222+ anchors {
223+ left: parent.left
224+ verticalCenter: parent.verticalCenter
225+ }
226+ visible: root.itemRunning
227+ rotation: 180
228+ source: "graphics/focused_app_arrow.png"
229+ }
230+
141 Image {
231 Image {
142 objectName: "focusedHighlight"
232 objectName: "focusedHighlight"
143 anchors {
233 anchors {
@@ -167,6 +257,14 @@ Item {
167 }
257 }
168
258
169 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+ },
170 // Rotating 3 times at top/bottom because that increases the perspective.
268 // Rotating 3 times at top/bottom because that increases the perspective.
171 // 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
172 // 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+95−14
@@ -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,6 +110,9 @@ 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
@@ -159,6 +163,17 @@ Rectangle {
159 to: launcherListView.contentHeight - launcherListView.height + launcherListView.originY - launcherListView.topMargin
163 to: launcherListView.contentHeight - launcherListView.height + launcherListView.originY - launcherListView.topMargin
160 }
164 }
161
165
166+ UbuntuNumberAnimation {
167+ id: moveAnimation
168+ target: launcherListView
169+ property: "contentY"
170+ function moveTo(contentY) {
171+ from = launcherListView.contentY;
172+ to = contentY;
173+ start();
174+ }
175+ }
176+
162 displaced: Transition {
177 displaced: Transition {
163 NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
178 NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
164 }
179 }
@@ -178,12 +193,70 @@ Rectangle {
178 count: model.count
193 count: model.count
179 countVisible: model.countVisible
194 countVisible: model.countVisible
180 progress: model.progress
195 progress: model.progress
196+ itemRunning: model.running
181 itemFocused: model.focused
197 itemFocused: model.focused
182 inverted: root.inverted
198 inverted: root.inverted
199+ alerting: model.alerting
183 z: -Math.abs(offset)
200 z: -Math.abs(offset)
184 maxAngle: 55
201 maxAngle: 55
185 property bool dragging: false
202 property bool dragging: false
186
203
204+ SequentialAnimation {
205+ id: peekingAnimation
206+
207+ // revealing
208+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 1 : 0 }
209+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 0 }
210+
211+ UbuntuNumberAnimation {
212+ target: launcherDelegate
213+ alwaysRunToEnd: true
214+ loops: 1
215+ properties: "x"
216+ to: (units.gu(.5) + launcherListView.width * .5) * (root.inverted ? -1 : 1)
217+ duration: UbuntuAnimation.BriskDuration
218+ }
219+
220+ // hiding
221+ UbuntuNumberAnimation {
222+ target: launcherDelegate
223+ alwaysRunToEnd: true
224+ loops: 1
225+ properties: "x"
226+ to: 0
227+ duration: UbuntuAnimation.BriskDuration
228+ }
229+
230+ PropertyAction { target: launcherListViewItem; property: "clip"; value: 1 }
231+ PropertyAction { target: root; property: "visible"; value: (launcher.visibleWidth === 0) ? 0 : 1 }
232+ }
233+
234+ onAlertingChanged: {
235+ if(alerting) {
236+ if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
237+ var itemPosition = index * launcherListView.itemHeight;
238+ var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
239+ var distanceToEnd = index == 0 || index == launcherListView.count - 1 ? 0 : launcherListView.itemHeight
240+ if (itemPosition + launcherListView.itemHeight + distanceToEnd > launcherListView.contentY + launcherListView.topMargin + height) {
241+ moveAnimation.moveTo(itemPosition + launcherListView.itemHeight - launcherListView.topMargin - height + distanceToEnd);
242+ } else if (itemPosition - distanceToEnd < launcherListView.contentY + launcherListView.topMargin) {
243+ moveAnimation.moveTo(itemPosition - distanceToEnd - launcherListView.topMargin);
244+ }
245+ if (!dragging && launcher.state !== "visible") {
246+ peekingAnimation.start()
247+ }
248+ }
249+
250+ if (launcherListView.peekingIndex === -1) {
251+ launcherListView.peekingIndex = index
252+ }
253+ } else {
254+ if (launcherListView.peekingIndex === index) {
255+ launcherListView.peekingIndex = -1
256+ }
257+ }
258+ }
259+
187 ThinDivider {
260 ThinDivider {
188 id: dropIndicator
261 id: dropIndicator
189 objectName: "dropIndicator"
262 objectName: "dropIndicator"
@@ -286,6 +359,7 @@ Rectangle {
286 MouseArea {
359 MouseArea {
287 id: dndArea
360 id: dndArea
288 objectName: "dndArea"
361 objectName: "dndArea"
362+ acceptedButtons: Qt.LeftButton | Qt.RightButton
289 anchors {
363 anchors {
290 fill: parent
364 fill: parent
291 topMargin: launcherListView.topMargin
365 topMargin: launcherListView.topMargin
@@ -297,7 +371,7 @@ Rectangle {
297 property int draggedIndex: -1
371 property int draggedIndex: -1
298 property var selectedItem
372 property var selectedItem
299 property bool preDragging: false
373 property bool preDragging: false
300− property bool dragging: selectedItem !== undefined && selectedItem !== null && selectedItem.dragging
374+ property bool dragging: !!selectedItem && selectedItem.dragging
301 property bool postDragging: false
375 property bool postDragging: false
302 property int startX
376 property int startX
303 property int startY
377 property int startY
@@ -315,6 +389,15 @@ Rectangle {
315 return;
389 return;
316 }
390 }
317
391
392+ if (mouse.button & Qt.RightButton) { // context menu
393+ // Opening QuickList
394+ quickList.item = clickedItem;
395+ quickList.model = launcherListView.model.get(index).quickList;
396+ quickList.appId = launcherListView.model.get(index).appId;
397+ quickList.state = "open";
398+ return
399+ }
400+
318 // First/last item do the scrolling at more than 12 degrees
401 // First/last item do the scrolling at more than 12 degrees
319 if (index == 0 || index == launcherListView.count - 1) {
402 if (index == 0 || index == launcherListView.count - 1) {
320 if (clickedItem.angle > 12) {
403 if (clickedItem.angle > 12) {
@@ -508,7 +591,7 @@ Rectangle {
508 id: quickListShape
591 id: quickListShape
509 objectName: "quickListShape"
592 objectName: "quickListShape"
510 anchors.fill: quickList
593 anchors.fill: quickList
511− opacity: quickList.state === "open" ? 0.96 : 0
594+ opacity: quickList.state === "open" ? 0.8 : 0
512 visible: opacity > 0
595 visible: opacity > 0
513 rotation: root.rotation
596 rotation: root.rotation
514
597
@@ -520,15 +603,15 @@ Rectangle {
520
603
521 Image {
604 Image {
522 anchors {
605 anchors {
523− left: parent.left
606+ right: parent.left
524− leftMargin: quickList.item ? (quickList.item.width - units.gu(1)) / 2 - width / 2 : 0
607+ rightMargin: -units.dp(4)
525 verticalCenter: parent.verticalCenter
608 verticalCenter: parent.verticalCenter
526− verticalCenterOffset: (parent.height / 2 + units.dp(3)) * (quickList.offset > 0 ? 1 : -1) * (root.inverted ? 1 : -1)
609+ verticalCenterOffset: -quickList.offset * (root.inverted ? -1 : 1)
527 }
610 }
528 height: units.gu(1)
611 height: units.gu(1)
529 width: units.gu(2)
612 width: units.gu(2)
530 source: "graphics/quicklist_tooltip.png"
613 source: "graphics/quicklist_tooltip.png"
531− rotation: (quickList.offset > 0 ? 0 : 180) + (root.inverted ? 0 : 180)
614+ rotation: 90
532 }
615 }
533
616
534 InverseMouseArea {
617 InverseMouseArea {
@@ -551,11 +634,11 @@ Rectangle {
551 height: quickListColumn.height
634 height: quickListColumn.height
552 visible: quickListShape.visible
635 visible: quickListShape.visible
553 anchors {
636 anchors {
554− left: root.inverted ? undefined : parent.left
637+ left: root.inverted ? undefined : parent.right
555− right: root.inverted ? parent.right : undefined
638+ right: root.inverted ? parent.left : undefined
556 margins: units.gu(1)
639 margins: units.gu(1)
557 }
640 }
558− y: itemCenter + offset
641+ y: itemCenter - (height / 2) + offset
559 rotation: root.rotation
642 rotation: root.rotation
560
643
561 property var model
644 property var model
@@ -564,10 +647,8 @@ Rectangle {
564
647
565 // internal
648 // internal
566 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
649 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
567− property int offset: item ? itemCenter + (item.height/2) + height + units.gu(1) > parent.height ?
650+ property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
568− -(item.height/2) - height - units.gu(.5) :
569− (item.height/2) + units.gu(.5) :
651+ itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
570− 0
571
652
572 Column {
653 Column {
573 id: quickListColumn
654 id: quickListColumn
qml/Launcher/graphics/progressbar-fill@30.pngdeleted

Binary file not shown.

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

Binary file not shown.

qml/Launcher/graphics/progressbar-trough@30.scideleted−5
@@ -1,5 +0,0 @@
1−border.left: 36
2−border.top: 16
3−border.bottom: 26
4−border.right: 36
5−source: progressbar-trough@30.png
qml/OrientedShell.qml+6−3
@@ -21,6 +21,9 @@ import GSettings 1.0
21import "Components"
21import "Components"
22import "Components/UnityInputInfo"
22import "Components/UnityInputInfo"
23import "Rotation"
23import "Rotation"
24+// Backup Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
25+// in case we remove the UnityInputInfo import
26+import Ubuntu.Components 1.2
24
27
25Rectangle {
28Rectangle {
26 id: root
29 id: root
@@ -43,7 +46,7 @@ Rectangle {
43
46
44
47
45 // to be overwritten by tests
48 // to be overwritten by tests
46− property var usageModeSettings: GSettings { schema.id: "com.canonical.Unity8" }
49+ property var unity8Settings: Unity8Settings {}
47 property var oskSettings: GSettings { schema.id: "com.canonical.keyboard.maliit" }
50 property var oskSettings: GSettings { schema.id: "com.canonical.keyboard.maliit" }
48
51
49 property int physicalOrientation: Screen.orientation
52 property int physicalOrientation: Screen.orientation
@@ -160,9 +163,9 @@ Rectangle {
160 // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
163 // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
161 // what's the output device (eg: big TV, desktop monitor, phone display), etc.
164 // what's the output device (eg: big TV, desktop monitor, phone display), etc.
162 usageScenario: {
165 usageScenario: {
163− if (root.usageModeSettings.usageMode === "Windowed") {
166+ if (root.unity8Settings.usageMode === "Windowed") {
164 return "desktop";
167 return "desktop";
165− } else if (root.usageModeSettings.usageMode === "Staged") {
168+ } else if (root.unity8Settings.usageMode === "Staged") {
166 if (deviceConfiguration.category === "phone") {
169 if (deviceConfiguration.category === "phone") {
167 return "phone";
170 return "phone";
168 } else {
171 } else {
qml/Panel/IndicatorItem.qml+3−3
@@ -31,9 +31,9 @@ IndicatorDelegate {
31 property bool selected: false
31 property bool selected: false
32 property real iconHeight: units.gu(2)
32 property real iconHeight: units.gu(2)
33 readonly property color color: {
33 readonly property color color: {
34− if (!expanded) return "#ededed";
34+ if (!expanded) return "#ffffff";
35− if (!selected) return "#4c4c4c";
35+ if (!selected) return "#888888";
36− return "#ededed";
36+ return "#ffffff";
37 }
37 }
38
38
39 signal clicked()
39 signal clicked()
qml/Panel/IndicatorItemRow.qml+1−1
@@ -31,7 +31,7 @@ Item {
31 property real unitProgress: 0.0
31 property real unitProgress: 0.0
32 property real selectionChangeBuffer: units.gu(2)
32 property real selectionChangeBuffer: units.gu(2)
33 property bool enableLateralChanges: false
33 property bool enableLateralChanges: false
34− property color hightlightColor: "#ededed"
34+ property color hightlightColor: "#ffffff"
35
35
36 property real lateralPosition: -1
36 property real lateralPosition: -1
37 onLateralPositionChanged: {
37 onLateralPositionChanged: {
qml/Panel/IndicatorsBar.qml+1−1
@@ -123,7 +123,7 @@ Item {
123 width: parent.width
123 width: parent.width
124 anchors.bottom: parent.bottom
124 anchors.bottom: parent.bottom
125
125
126− color: "#4c4c4c"
126+ color: "#888888"
127 opacity: expanded ? 1.0 : 0.0
127 opacity: expanded ? 1.0 : 0.0
128 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
128 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
129 }
129 }
qml/Panel/IndicatorsMenu.qml+3−1
@@ -36,6 +36,7 @@ Showable {
36 readonly property bool fullyClosed: unitProgress == 0
36 readonly property bool fullyClosed: unitProgress == 0
37 property bool enableHint: true
37 property bool enableHint: true
38 property bool contentEnabled: true
38 property bool contentEnabled: true
39+ property bool showOnClick: true
39 property color panelColor: "black"
40 property color panelColor: "black"
40
41
41 signal showTapped(point position)
42 signal showTapped(point position)
@@ -68,6 +69,7 @@ Showable {
68 // eater
69 // eater
69 MouseArea {
70 MouseArea {
70 anchors.fill: parent
71 anchors.fill: parent
72+ hoverEnabled: true
71 }
73 }
72
74
73 MenuContent {
75 MenuContent {
@@ -168,7 +170,7 @@ Showable {
168 anchors.left: parent.left
170 anchors.left: parent.left
169 anchors.right: parent.right
171 anchors.right: parent.right
170 height: minimizedPanelHeight
172 height: minimizedPanelHeight
171− enabled: __showDragHandle.enabled
173+ enabled: __showDragHandle.enabled && showOnClick
172 onClicked: {
174 onClicked: {
173 bar.selectItemAt(mouseX)
175 bar.selectItemAt(mouseX)
174 root.show()
176 root.show()
qml/Panel/Panel.qml+1
@@ -147,6 +147,7 @@ Item {
147 return root.width
147 return root.width
148 }
148 }
149 enableHint: !callHint.active && !fullscreenMode
149 enableHint: !callHint.active && !fullscreenMode
150+ showOnClick: !callHint.visible
150 panelColor: indicatorAreaBackground.color
151 panelColor: indicatorAreaBackground.color
151
152
152 onShowTapped: {
153 onShowTapped: {
qml/Shell.qml+23−21
@@ -178,6 +178,7 @@ Item {
178
178
179 PhysicalKeysMapper {
179 PhysicalKeysMapper {
180 id: physicalKeysMapper
180 id: physicalKeysMapper
181+ objectName: "physicalKeysMapper"
181
182
182 onPowerKeyLongPressed: dialogs.showPowerDialog()
183 onPowerKeyLongPressed: dialogs.showPowerDialog()
183 onVolumeDownTriggered: volumeControl.volumeDown();
184 onVolumeDownTriggered: volumeControl.volumeDown();
@@ -188,13 +189,6 @@ Item {
188 ScreenGrabber {
189 ScreenGrabber {
189 id: screenGrabber
190 id: screenGrabber
190 z: dialogs.z + 10
191 z: dialogs.z + 10
191− enabled: Powerd.status === Powerd.On
192− }
193−
194− Binding {
195− target: ApplicationManager
196− property: "forceDashActive"
197− value: launcher.shown || launcher.dashSwipe
198 }
192 }
199
193
200 WindowKeysFilter {
194 WindowKeysFilter {
@@ -352,26 +346,40 @@ Item {
352 property: "beingResized"
346 property: "beingResized"
353 value: shell.beingResized
347 value: shell.beingResized
354 }
348 }
349+ Binding {
350+ target: applicationsDisplayLoader.item
351+ property: "keepDashRunning"
352+ value: launcher.shown || launcher.dashSwipe
353+ }
354+ Binding {
355+ target: applicationsDisplayLoader.item
356+ property: "suspended"
357+ value: greeter.shown
358+ }
359+ Binding {
360+ target: applicationsDisplayLoader.item
361+ property: "altTabPressed"
362+ value: physicalKeysMapper.altTabPressed
363+ }
355 }
364 }
356
365
357 Tutorial {
366 Tutorial {
358 id: tutorial
367 id: tutorial
359 objectName: "tutorial"
368 objectName: "tutorial"
360 anchors.fill: parent
369 anchors.fill: parent
361− active: AccountsService.demoEdges
362− paused: LightDM.Greeter.active
363− launcher: launcher
364− panel: panel
365− edgeSize: shell.edgeSize
366
370
367 // EdgeDragAreas don't work with mice. So to avoid trapping the user,
371 // EdgeDragAreas don't work with mice. So to avoid trapping the user,
368− // we'll tell the tutorial to avoid using them on the Desktop. The
372+ // we skip the tutorial on the Desktop to avoid using them. The
369 // Desktop doesn't use the same spread design anyway. The tutorial is
373 // Desktop doesn't use the same spread design anyway. The tutorial is
370 // all a bit of a placeholder on non-phone form factors right now.
374 // all a bit of a placeholder on non-phone form factors right now.
371 // When the design team gives us more guidance, we can do something
375 // When the design team gives us more guidance, we can do something
372 // more clever here.
376 // more clever here.
373− // TODO: use DeviceConfiguration instead of checking source
377+ active: usageScenario != "desktop" && AccountsService.demoEdges
374− useEdgeDragArea: applicationsDisplayLoader.source != Qt.resolvedUrl("Stages/DesktopStage.qml")
378+
379+ paused: LightDM.Greeter.active
380+ launcher: launcher
381+ panel: panel
382+ edgeSize: shell.edgeSize
375
383
376 onFinished: {
384 onFinished: {
377 AccountsService.demoEdges = false;
385 AccountsService.demoEdges = false;
@@ -454,12 +462,6 @@ Item {
454 }
462 }
455
463
456 onEmergencyCall: startLockedApp("dialer-app")
464 onEmergencyCall: startLockedApp("dialer-app")
457−
458− Binding {
459− target: ApplicationManager
460− property: "suspended"
461− value: greeter.shown
462− }
463 }
465 }
464 }
466 }
465
467
qml/Stages/ApplicationWindow.qml+3
@@ -135,6 +135,9 @@ FocusScope {
135 onSurfaceChanged: {
135 onSurfaceChanged: {
136 if (sessionContainer.surface) {
136 if (sessionContainer.surface) {
137 surfaceInitTimer.start();
137 surfaceInitTimer.start();
138+ if (ApplicationManager.focusedApplicationId == application.appId) {
139+ root.forceActiveFocus();
140+ }
138 } else {
141 } else {
139 d.surfaceInitialized = false;
142 d.surfaceInitialized = false;
140 }
143 }
qml/Stages/DecoratedWindow.qml+72−8
@@ -23,21 +23,67 @@ import Unity.Application 0.1
23FocusScope {
23FocusScope {
24 id: root
24 id: root
25
25
26+ property alias window: applicationWindow
26 property alias application: applicationWindow.application
27 property alias application: applicationWindow.application
27 property alias active: decoration.active
28 property alias active: decoration.active
28
29
30+ property bool decorationShown: true
31+ property bool highlightShown: false
32+ property real shadowOpacity: 1
33+
34+ property int windowWidth: width
35+ property int windowHeight: height
36+
29 signal close();
37 signal close();
30 signal maximize();
38 signal maximize();
31 signal minimize();
39 signal minimize();
32
40
41+ state: "normal"
42+ states: [
43+ State {
44+ name: "normal"
45+ PropertyChanges {
46+ target: root
47+ width: windowWidth
48+ height: windowHeight
49+ }
50+ },
51+ State {
52+ name: "transformed"
53+ PropertyChanges {
54+ target: applicationWindow
55+ itemScale: Math.max(root.width / root.windowWidth, root.height / root.windowHeight)
56+ interactive: false
57+ }
58+ PropertyChanges {
59+ target: clipper
60+ clip: true
61+ }
62+ }
63+ ]
64+
33 BorderImage {
65 BorderImage {
34 anchors {
66 anchors {
35 fill: root
67 fill: root
36 margins: -units.gu(2)
68 margins: -units.gu(2)
37 }
69 }
38 source: "graphics/dropshadow2gu.sci"
70 source: "graphics/dropshadow2gu.sci"
39− opacity: .3
71+ opacity: root.shadowOpacity * .3
40− Behavior on opacity { UbuntuNumberAnimation {} }
72+ }
73+
74+ Rectangle {
75+ id: selectionHighlight
76+ anchors.fill: parent
77+ anchors.margins: -units.gu(1)
78+ color: "white"
79+ opacity: highlightShown ? 0.15 : 0
80+ }
81+
82+ Rectangle {
83+ anchors { left: selectionHighlight.left; right: selectionHighlight.right; bottom: selectionHighlight.bottom; }
84+ height: units.dp(2)
85+ color: UbuntuColors.orange
86+ visible: root.highlightShown
41 }
87 }
42
88
43 WindowDecoration {
89 WindowDecoration {
@@ -49,14 +95,32 @@ FocusScope {
49 onClose: root.close();
95 onClose: root.close();
50 onMaximize: root.maximize();
96 onMaximize: root.maximize();
51 onMinimize: root.minimize();
97 onMinimize: root.minimize();
98+ visible: decorationShown
52 }
99 }
53
100
54− ApplicationWindow {
101+ Item {
55− id: applicationWindow
102+ id: clipper
56− objectName: application ? "appWindow_" + application.appId : "appWindow_null"
57 anchors.fill: parent
103 anchors.fill: parent
104+
105+ ApplicationWindow {
106+ id: applicationWindow
107+ objectName: application ? "appWindow_" + application.appId : "appWindow_null"
108+ anchors.top: parent.top
58− anchors.topMargin: units.gu(3)
109+ anchors.topMargin: root.decorationShown ? decoration.height : 0
110+ anchors.left: parent.left
111+ width: root.windowWidth
112+ height: root.windowHeight - (root.decorationShown ? decoration.height : 0)
59− interactive: true
113+ interactive: true
60− focus: true
114+ focus: true
115+
116+ property real itemScale: 1
117+ transform: [
118+ Scale {
119+ origin.x: 0; origin.y: 0
120+ xScale: applicationWindow.itemScale
121+ yScale: applicationWindow.itemScale
122+ }
123+ ]
124+ }
61 }
125 }
62}
126}
qml/Stages/DesktopStage.qml+511−57
@@ -17,13 +17,15 @@
17 */
17 */
18
18
19import QtQuick 2.3
19import QtQuick 2.3
20+import QtQuick.Layouts 1.1
20import Ubuntu.Components 1.1
21import Ubuntu.Components 1.1
21import Unity.Application 0.1
22import Unity.Application 0.1
22import "../Components/PanelState"
23import "../Components/PanelState"
24+import Utils 0.1
25+import Ubuntu.Gestures 0.1
23
26
24Rectangle {
27Rectangle {
25 id: root
28 id: root
26−
27 anchors.fill: parent
29 anchors.fill: parent
28
30
29 // Controls to be set from outside
31 // Controls to be set from outside
@@ -37,6 +39,8 @@ Rectangle {
37 property int shellPrimaryOrientation
39 property int shellPrimaryOrientation
38 property int nativeOrientation
40 property int nativeOrientation
39 property bool beingResized: false
41 property bool beingResized: false
42+ property bool keepDashRunning: true
43+ property bool suspended: false
40
44
41 // functions to be called from outside
45 // functions to be called from outside
42 function updateFocusedAppOrientation() { /* TODO */ }
46 function updateFocusedAppOrientation() { /* TODO */ }
@@ -50,6 +54,7 @@ Rectangle {
50 readonly property bool orientationChangesEnabled: false
54 readonly property bool orientationChangesEnabled: false
51
55
52 property alias background: wallpaper.source
56 property alias background: wallpaper.source
57+ property bool altTabPressed: false
53
58
54 CrossFadeImage {
59 CrossFadeImage {
55 id: wallpaper
60 id: wallpaper
@@ -61,15 +66,18 @@ Rectangle {
61 Connections {
66 Connections {
62 target: ApplicationManager
67 target: ApplicationManager
63 onApplicationAdded: {
68 onApplicationAdded: {
69+ if (root.state == "altTab") {
70+ root.state = "";
71+ }
72+
64− ApplicationManager.requestFocusApplication(ApplicationManager.get(ApplicationManager.count-1).appId)
73+ ApplicationManager.requestFocusApplication(appId)
65 }
74 }
66
75
67 onFocusRequested: {
76 onFocusRequested: {
68 var appIndex = priv.indexOf(appId);
77 var appIndex = priv.indexOf(appId);
69 var appDelegate = appRepeater.itemAt(appIndex);
78 var appDelegate = appRepeater.itemAt(appIndex);
70− if (appDelegate.state === "minimized") {
79+ appDelegate.minimized = false;
71− appDelegate.state = "normal"
80+ appDelegate.focus = true;
72− }
73 ApplicationManager.focusApplication(appId);
81 ApplicationManager.focusApplication(appId);
74 }
82 }
75 }
83 }
@@ -104,8 +112,8 @@ Rectangle {
104 onClose: {
112 onClose: {
105 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
113 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
106 }
114 }
107− onMinimize: appRepeater.itemAt(0).state = "minimized"
115+ onMinimize: appRepeater.itemAt(0).minimize();
108− onMaximize: appRepeater.itemAt(0).state = "normal"
116+ onMaximize: appRepeater.itemAt(0).unmaximize();
109 }
117 }
110
118
111 Binding {
119 Binding {
@@ -114,67 +122,513 @@ Rectangle {
114 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
122 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
115 }
123 }
116
124
117− Repeater {
125+ Rectangle {
118− id: appRepeater
126+ id: spreadBackground
119− model: ApplicationManager
127+ anchors.fill: parent
128+ color: "#55000000"
129+ visible: false
130+ }
120
131
121− delegate: FocusScope {
132+ FocusScope {
122− id: appDelegate
133+ id: appContainer
123− z: ApplicationManager.count - index
134+ objectName: "appContainer"
124− y: units.gu(3)
135+ anchors.fill: parent
125− width: units.gu(60)
126− height: units.gu(50)
127
136
128− onFocusChanged: {
137+ Keys.onPressed: {
129− if (focus) {
138+ switch (event.key) {
130− ApplicationManager.requestFocusApplication(model.appId);
139+ case Qt.Key_Left:
131− }
140+ case Qt.Key_Backtab:
141+ selectPrevious(event.isAutoRepeat)
142+ break;
143+ case Qt.Key_Right:
144+ case Qt.Key_Tab:
145+ selectNext(event.isAutoRepeat)
146+ break;
147+ case Qt.Key_Escape:
148+ appRepeater.highlightedIndex = -1
149+ case Qt.Key_Enter:
150+ case Qt.Key_Return:
151+ case Qt.Key_Space:
152+ root.state = ""
153+ }
154+ }
155+
156+ function selectNext(isAutoRepeat) {
157+ if (isAutoRepeat && appRepeater.highlightedIndex >= ApplicationManager.count -1) {
158+ return; // AutoRepeat is not allowed to wrap around
159+ }
160+
161+ appRepeater.highlightedIndex = (appRepeater.highlightedIndex + 1) % ApplicationManager.count;
162+ var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, appRepeater.highlightedIndex - 3));
163+ if (spreadFlickable.contentX < newContentX || appRepeater.highlightedIndex == 0) {
164+ spreadFlickable.snapTo(newContentX)
165+ }
166+ }
167+
168+ function selectPrevious(isAutoRepeat) {
169+ if (isAutoRepeat && appRepeater.highlightedIndex == 0) {
170+ return; // AutoRepeat is not allowed to wrap around
171+ }
172+
173+ var newIndex = appRepeater.highlightedIndex - 1 >= 0 ? appRepeater.highlightedIndex - 1 : ApplicationManager.count - 1;
174+ appRepeater.highlightedIndex = newIndex;
175+ var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, appRepeater.highlightedIndex - 1));
176+ if (spreadFlickable.contentX > newContentX || newIndex == ApplicationManager.count -1) {
177+ spreadFlickable.snapTo(newContentX)
178+ }
179+ }
180+
181+ function focusSelected() {
182+ if (appRepeater.highlightedIndex != -1) {
183+ appRepeater.itemAt(appRepeater.highlightedIndex).focus = true;
132 }
184 }
185+ }
186+
187+ Repeater {
188+ id: appRepeater
189+ model: ApplicationManager
190+ objectName: "appRepeater"
191+
192+ property int highlightedIndex: -1
193+ property int closingIndex: -1
194+
195+ delegate: FocusScope {
196+ id: appDelegate
197+ z: ApplicationManager.count - index
198+ y: units.gu(3)
199+ width: units.gu(60)
200+ height: units.gu(50)
201+
202+ readonly property int minWidth: units.gu(10)
203+ readonly property int minHeight: units.gu(10)
204+
205+ property bool maximized: false
206+ property bool minimized: false
207+
208+ onFocusChanged: {
209+ if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
210+ ApplicationManager.requestFocusApplication(model.appId);
211+ decoratedWindow.forceActiveFocus();
212+ }
213+ }
214+ Component.onCompleted: {
215+ if (ApplicationManager.focusedApplicationId == model.appId) {
216+ decoratedWindow.forceActiveFocus();
217+ }
218+ }
219+
220+ Binding {
221+ target: ApplicationManager.get(index)
222+ property: "requestedState"
223+ // TODO: figure out some lifecycle policy, like suspending minimized apps
224+ // if running on a tablet or something.
225+ // TODO: If the device has a dozen suspended apps because it was running
226+ // in staged mode, when it switches to Windowed mode it will suddenly
227+ // resume all those apps at once. We might want to avoid that.
228+ value: ApplicationInfoInterface.RequestedRunning // Always running for now
229+ }
230+
231+ function maximize() {
232+ minimized = false;
233+ maximized = true;
234+ }
235+ function minimize() {
236+ maximized = false;
237+ minimized = true;
238+ }
239+ function unmaximize() {
240+ minimized = false;
241+ maximized = false;
242+ }
243+
244+ Behavior on x {
245+ id: closeBehavior
246+ enabled: appRepeater.closingIndex >= 0
247+ UbuntuNumberAnimation {
248+ onRunningChanged: if (!running) appRepeater.closingIndex = -1
249+ }
250+ }
251+
252+ states: [
253+ State {
254+ name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized && root.state !== "altTab"
255+ },
256+ State {
257+ name: "maximized"; when: appDelegate.maximized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
258+ PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
259+ },
260+ State {
261+ name: "minimized"; when: appDelegate.minimized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
262+ PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
263+ },
264+ State {
265+ name: "altTab"; when: root.state == "altTab" && root.workspacesUpdated
266+ PropertyChanges {
267+ target: appDelegate
268+ x: spreadMaths.animatedX
269+ y: spreadMaths.animatedY + (appDelegate.height - decoratedWindow.height)
270+ angle: spreadMaths.animatedAngle
271+ itemScale: spreadMaths.scale
272+ itemScaleOriginY: decoratedWindow.height / 2;
273+ z: index
274+ visible: spreadMaths.itemVisible
275+ }
276+ PropertyChanges {
277+ target: decoratedWindow
278+ decorationShown: false
279+ highlightShown: index == appRepeater.highlightedIndex
280+ state: "transformed"
281+ width: spreadMaths.spreadHeight
282+ height: spreadMaths.spreadHeight
283+ shadowOpacity: spreadMaths.shadowOpacity
284+ }
285+ PropertyChanges {
286+ target: tileInfo
287+ visible: true
288+ opacity: spreadMaths.tileInfoOpacity
289+ }
290+ PropertyChanges {
291+ target: spreadSelectArea
292+ enabled: true
293+ }
294+ PropertyChanges {
295+ target: windowMoveResizeArea
296+ enabled: false
297+ }
298+ }
299+ ]
300+ transitions: [
301+ Transition {
302+ from: "maximized,minimized,normal,"
303+ to: "maximized,minimized,normal,"
304+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
305+ }
306+ ]
307+ property real angle: 0
308+ property real itemScale: 1
309+ property int itemScaleOriginX: 0
310+ property int itemScaleOriginY: 0
311+
312+ SpreadMaths {
313+ id: spreadMaths
314+ flickable: spreadFlickable
315+ itemIndex: index
316+ totalItems: Math.max(6, ApplicationManager.count)
317+ sceneHeight: root.height
318+ itemHeight: appDelegate.height
319+ }
320+
321+ WindowMoveResizeArea {
322+ id: windowMoveResizeArea
323+ target: appDelegate
324+ minWidth: appDelegate.minWidth
325+ minHeight: appDelegate.minHeight
326+ resizeHandleWidth: units.gu(2)
327+ windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
133
328
134− readonly property int minWidth: units.gu(10)
329+ onPressed: appDelegate.focus = true;
135− readonly property int minHeight: units.gu(10)
136−
137− states: [
138− State {
139− name: "normal"
140− },
141− State {
142− name: "maximized"
143− PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
144− },
145− State {
146− name: "minimized"
147− PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
148 }
330 }
331+
332+ DecoratedWindow {
333+ id: decoratedWindow
334+ objectName: "decoratedWindow"
335+ anchors.left: appDelegate.left
336+ anchors.top: appDelegate.top
337+ windowWidth: appDelegate.width
338+ windowHeight: appDelegate.height
339+ application: ApplicationManager.get(index)
340+ active: ApplicationManager.focusedApplicationId === model.appId
341+ focus: false
342+
343+ onClose: ApplicationManager.stopApplication(model.appId)
344+ onMaximize: appDelegate.maximize()
345+ onMinimize: appDelegate.minimize()
346+
347+ transform: [
348+ Scale {
349+ origin.x: itemScaleOriginX
350+ origin.y: itemScaleOriginY
351+ xScale: itemScale
352+ yScale: itemScale
353+ },
354+ Rotation {
355+ origin { x: 0; y: (decoratedWindow.height - (decoratedWindow.height * itemScale / 2)) }
356+ axis { x: 0; y: 1; z: 0 }
357+ angle: appDelegate.angle
358+ }
149− ]
359+ ]
150− transitions: [
360+
151− Transition {
361+ MouseArea {
152− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
362+ id: spreadSelectArea
363+ anchors.fill: parent
364+ anchors.margins: -units.gu(2)
365+ enabled: false
366+ hoverEnabled: enabled
367+
368+ // There is a bug in MouseArea where containsMouse doesn't
369+ // return to false if the MouseArea is disabled while
370+ // containing the mouse. Let's manage the property our own.
371+ property bool upperThirdContainsMouse: false
372+ onContainsMouseChanged: evaluateContainsMouse()
373+ onMouseYChanged: evaluateContainsMouse()
374+ function evaluateContainsMouse() {
375+ if (containsMouse) {
376+ appRepeater.highlightedIndex = index
377+ }
378+
379+ if (containsMouse && mouseY < height / 3) {
380+ spreadSelectArea.upperThirdContainsMouse = true
381+ } else {
382+ spreadSelectArea.upperThirdContainsMouse = false;
383+ }
384+ }
385+ onEnabledChanged: {
386+ if (!enabled) {
387+ spreadSelectArea.upperThirdContainsMouse = false
388+ }
389+ }
390+
391+ onClicked: {
392+ root.state = ""
393+ }
394+ }
395+ }
396+
397+ Image {
398+ id: closeImage
399+ anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
400+ source: "graphics/window-close.svg"
401+ visible: spreadSelectArea.upperThirdContainsMouse
402+ height: units.gu(1.5)
403+ width: height
404+ sourceSize.width: width
405+ sourceSize.height: height
406+ }
407+
408+ MouseArea {
409+ id: closeMouseArea
410+ objectName: "closeMouseArea"
411+ anchors.fill: closeImage
412+ anchors.margins: -units.gu(2)
413+ enabled: spreadSelectArea.upperThirdContainsMouse
414+ onClicked: {
415+ appRepeater.closingIndex = index;
416+ ApplicationManager.stopApplication(model.appId)
417+ }
153 }
418 }
154− ]
155
419
156− WindowMoveResizeArea {
420+ MouseArea {
157− target: appDelegate
421+ id: tileInfo
158− minWidth: appDelegate.minWidth
422+ objectName: "tileInfo"
159− minHeight: appDelegate.minHeight
423+ anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(5) }
160− resizeHandleWidth: units.gu(0.5)
424+ width: units.gu(30)
161− windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
425+ height: titleInfoColumn.height
426+ visible: false
427+ hoverEnabled: true
428+
429+ onContainsMouseChanged: {
430+ if (containsMouse) {
431+ appRepeater.highlightedIndex = index
432+ }
433+ }
434+
435+ onClicked: {
436+ root.state = ""
437+ }
438+
439+ ColumnLayout {
440+ id: titleInfoColumn
441+ anchors { left: parent.left; top: parent.top; right: parent.right }
442+ spacing: units.gu(1)
443+
444+ UbuntuShape {
445+ Layout.preferredHeight: Math.min(units.gu(6), root.height * .05)
446+ Layout.preferredWidth: height * 8 / 7.6
447+ image: Image {
448+ anchors.fill: parent
449+ source: model.icon
450+ }
451+ }
452+ Label {
453+ Layout.fillWidth: true
454+ Layout.preferredHeight: units.gu(6)
455+ text: model.name
456+ wrapMode: Text.WordWrap
457+ maximumLineCount: 2
458+ }
459+ }
460+ }
461+ }
462+ }
463+ }
464+
465+ FloatingFlickable {
466+ id: spreadFlickable
467+ anchors.fill: parent
468+ contentWidth: Math.max(6, ApplicationManager.count) * Math.min(height / 4, width / 5)
469+ enabled: false
470+
471+ function snapTo(contentX) {
472+ snapAnimation.stop();
473+ snapAnimation.to = contentX
474+ snapAnimation.start();
475+ }
476+
477+ UbuntuNumberAnimation {
478+ id: snapAnimation
479+ target: spreadFlickable
480+ property: "contentX"
481+ }
482+ }
483+
484+ Item {
485+ id: workspaceSelector
486+ anchors {
487+ left: parent.left
488+ top: parent.top
489+ right: parent.right
490+ topMargin: units.gu(3.5) // TODO: should be root.panelHeight
491+ }
492+ height: root.height * 0.25
493+ visible: false
494+
495+ RowLayout {
496+ anchors.fill: parent
497+ spacing: units.gu(1)
498+ Item { Layout.fillWidth: true }
499+ Repeater {
500+ model: 1 // TODO: will be a workspacemodel in the future
501+ Item {
502+ Layout.fillHeight: true
503+ Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
504+ Image {
505+ source: root.background
506+ anchors {
507+ left: parent.left
508+ right: parent.right
509+ verticalCenter: parent.verticalCenter
510+ }
511+ height: parent.height * 0.75
512+
513+ // FIXME: This is temporary until we can have multiple Items per surface
514+ ShaderEffect {
515+ anchors.fill: parent
516+
517+ property var source: ShaderEffectSource {
518+ id: shaderEffectSource
519+ live: false
520+ sourceItem: appContainer
521+ Connections { target: root; onUpdateWorkspaces: shaderEffectSource.scheduleUpdate() }
522+ }
523+
524+ fragmentShader: "
525+ varying highp vec2 qt_TexCoord0;
526+ uniform sampler2D source;
527+ void main(void)
528+ {
529+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
530+ gl_FragColor = sourceColor;
531+ }"
532+ }
533+ }
534+
535+ // TODO: This is the bar for the currently selected workspace
536+ // Enable this once the workspace stuff is implemented
537+// Rectangle {
538+// anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
539+// height: units.dp(2)
540+// color: UbuntuColors.orange
541+// visible: index == 0 // TODO: should be active workspace index
542+// }
543+ }
162
544
163− onPressed: appDelegate.focus = true;
164 }
545 }
546+ // TODO: This is the "new workspace" button. Enable this once workspaces are implemented
547+// Item {
548+// Layout.fillHeight: true
549+// Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
550+// Rectangle {
551+// anchors {
552+// left: parent.left
553+// right: parent.right
554+// verticalCenter: parent.verticalCenter
555+// }
556+// height: parent.height * 0.75
557+// color: "#22ffffff"
558+
559+// Label {
560+// anchors.centerIn: parent
561+// font.pixelSize: parent.height / 2
562+// text: "+"
563+// }
564+// }
565+// }
566+ Item { Layout.fillWidth: true }
567+ }
568+ }
165
569
166− DecoratedWindow {
570+ Label {
167− id: decoratedWindow
571+ id: currentSelectedLabel
168− objectName: "decoratedWindow_" + appId
572+ anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
169− anchors.fill: parent
573+ text: appRepeater.highlightedIndex >= 0 ? ApplicationManager.get(appRepeater.highlightedIndex).name : ""
170− application: ApplicationManager.get(index)
574+ visible: false
171− active: ApplicationManager.focusedApplicationId === model.appId
575+ fontSize: "large"
172− focus: true
576+ }
173−
577+
174− onClose: ApplicationManager.stopApplication(model.appId)
578+ states: [
175− onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
579+ State {
176− onMinimize: appDelegate.state = "minimized"
580+ name: "altTab"; when: root.altTabPressed
581+ PropertyChanges { target: workspaceSelector; visible: true }
582+ PropertyChanges { target: spreadFlickable; enabled: true }
583+ PropertyChanges { target: currentSelectedLabel; visible: true }
584+ PropertyChanges { target: spreadBackground; visible: true }
585+ PropertyChanges { target: appContainer; focus: true }
586+ }
587+ ]
588+ signal updateWorkspaces();
589+ property bool workspacesUpdated: false
590+ transitions: [
591+ Transition {
592+ from: "*"
593+ to: "altTab"
594+ SequentialAnimation {
595+ PropertyAction { target: appRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) }
596+ PauseAnimation { duration: 50 }
597+ PropertyAction { target: workspaceSelector; property: "visible" }
598+ ScriptAction { script: root.updateWorkspaces() }
599+ // FIXME: Updating of shaderEffectSource take a bit of time. This is temporary until we can paint multiple items per surface
600+ PauseAnimation { duration: 10 }
601+ PropertyAction { target: root; property: "workspacesUpdated"; value: true }
602+ PropertyAction { target: spreadFlickable; property: "visible" }
603+ PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
604+ PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
177 }
605 }
606+ },
607+ Transition {
608+ from: "*"
609+ to: "*"
610+ PropertyAnimation { property: "opacity" }
611+ PropertyAction { target: root; property: "workspacesUpdated"; value: false }
612+ ScriptAction { script: { appContainer.focusSelected() } }
613+ PropertyAction { target: appRepeater; property: "highlightedIndex"; value: -1 }
178 }
614 }
615+
616+ ]
617+
618+ MouseArea {
619+ anchors {
620+ top: parent.top
621+ right: parent.right
622+ bottom: parent.bottom
623+ }
624+ // TODO: Make this a push to edge thing like the launcher when we can,
625+ // for now, yes, we want 1 pixel, regardless of the scaling
626+ width: 1
627+ hoverEnabled: true
628+ onContainsMouseChanged: {
629+ if (containsMouse) {
630+ root.state = "altTab"
631+ }
632+ }
179 }
633 }
180}
634}
qml/Stages/PhoneStage.qml+10
@@ -36,6 +36,8 @@ Rectangle {
36 property bool altTabEnabled: true
36 property bool altTabEnabled: true
37 property real startScale: 1.1
37 property real startScale: 1.1
38 property real endScale: 0.7
38 property real endScale: 0.7
39+ property bool keepDashRunning: true
40+ property bool suspended: false
39 property int shellOrientationAngle: 0
41 property int shellOrientationAngle: 0
40 property int shellOrientation
42 property int shellOrientation
41 property int shellPrimaryOrientation
43 property int shellPrimaryOrientation
@@ -428,6 +430,14 @@ Rectangle {
428 dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
430 dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
429 focusFirstApp: root.focusFirstApp
431 focusFirstApp: root.focusFirstApp
430
432
433+ Binding {
434+ target: appDelegate.application
435+ property: "requestedState"
436+ value: (isDash && root.keepDashRunning) || (!root.suspended && appDelegate.focus)
437+ ? ApplicationInfoInterface.RequestedRunning
438+ : ApplicationInfoInterface.RequestedSuspended
439+ }
440+
431 readonly property bool isDash: model.appId == "unity8-dash"
441 readonly property bool isDash: model.appId == "unity8-dash"
432
442
433 z: isDash && !spreadView.active ? -1 : behavioredIndex
443 z: isDash && !spreadView.active ? -1 : behavioredIndex
qml/Stages/SpreadMaths.qmlnew file+164
@@ -0,0 +1,164 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import Utils 0.1
20+
21+Item {
22+ id: root
23+ anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
24+
25+ // Information about the transformed item
26+ property int itemIndex: 0
27+ property real itemHeight: units.gu(10)
28+
29+ // Information about the environment
30+ property int totalItems: 0
31+ property Item flickable: null
32+ property int sceneHeight: units.gu(20)
33+
34+ // Spread properties
35+ property real spreadHeight: sceneHeight * 0.4
36+ property int spreadBottomOffset: sceneHeight * 0.18
37+ property int foldingAreaWidth: flickableWidth * 0.2
38+ property int maxVisibleItems: 7
39+ property int margins: flickableWidth * 0.05
40+ property real stackScale: 0.1
41+ property int leftEndFoldedAngle: 70
42+ property int rightEndFoldedAngle: 65
43+ property int unfoldedAngle: 30
44+ property int stackWidth: flickableWidth * 0.01
45+
46+
47+ // Internal
48+ readonly property int flickableWidth: flickable ? flickable.width : 0
49+ readonly property int flickableContentWidth: flickable ? flickable.contentWidth: 0
50+ readonly property real flickableProgress: flickable ? flickable.contentX / (flickable.contentWidth - flickableWidth) : 0
51+
52+ readonly property int contentWidth: flickableWidth - root.margins * 2
53+
54+ readonly property int distance: (flickableContentWidth - (margins * 2) - (foldingAreaWidth * 2)) / (totalItems - 2)
55+ readonly property int startPos: margins + foldingAreaWidth + (itemIndex - 1) * distance
56+ readonly property int linearX: startPos - flickableProgress * (flickableContentWidth - flickableWidth)
57+
58+ readonly property int leftFoldingAreaX: margins + foldingAreaWidth
59+ readonly property int rightFoldingAreaX: flickableWidth - foldingAreaWidth - margins
60+
61+ readonly property real leftFoldingAreaProgress: linearAnimation(leftFoldingAreaX, margins, 0, 1, linearX)
62+ readonly property real rightFoldingAreaProgress: linearAnimation(rightFoldingAreaX, flickableWidth - margins, 0, 1, linearX)
63+
64+ readonly property real limitedLeftProgress: Math.min(2, leftFoldingAreaProgress)
65+ readonly property real limitedRightProgress: Math.min(2, rightFoldingAreaProgress)
66+
67+ readonly property real middleSectionProgress: (linearX - margins - foldingAreaWidth) / (flickableWidth - (margins + foldingAreaWidth) * 2)
68+
69+ // Output
70+ readonly property int animatedX: {
71+ if (leftFoldingAreaProgress > 4) { // Stop it at the edge
72+ return margins;
73+ }
74+ if (leftFoldingAreaProgress > 2) { // move it slowly through the stack
75+ return linearAnimation(2, 4, margins + stackWidth, margins, leftFoldingAreaProgress)
76+ }
77+ if (leftFoldingAreaProgress > 1 && itemIndex == 0) {
78+ // The leftmost runs faster... make it stop before the stack and wait for others
79+ return margins + stackWidth;
80+ }
81+
82+ if (leftFoldingAreaProgress > 0) { // slow it down in a curve
83+ if (itemIndex == 0) { // except if it's the leftmost. that one goes straigt
84+ return linearAnimation(0, 1, leftFoldingAreaX, margins + stackWidth, leftFoldingAreaProgress)
85+ }
86+ return linearAnimation(0, 1, leftFoldingAreaX, margins + stackWidth, leftEasing.value)
87+ }
88+ // same for the right side stack... mostly... don't need to treat the rightmost special...
89+ if (rightFoldingAreaProgress > 4) {
90+ return flickableWidth - margins
91+ }
92+ if (rightFoldingAreaProgress > 2) {
93+ return linearAnimation(2, 4, flickableWidth - margins - stackWidth, flickableWidth - margins, rightFoldingAreaProgress)
94+ }
95+
96+ if (rightFoldingAreaProgress > 0) {
97+ return linearAnimation(0, 1, rightFoldingAreaX, flickableWidth - margins - stackWidth, rightEasing.value);
98+ }
99+
100+ return linearX
101+ }
102+
103+ readonly property int animatedY: sceneHeight - itemHeight - spreadBottomOffset
104+
105+ readonly property real animatedAngle: {
106+ if (limitedLeftProgress > 0) {
107+ // Leftmost is special...
108+ if (index == 0) {
109+ if (limitedLeftProgress < 1) {
110+ return unfoldedAngle;
111+ } else {
112+ return linearAnimation(1, 2, unfoldedAngle, leftEndFoldedAngle, limitedLeftProgress)
113+ }
114+ }
115+ return linearAnimation(0, 2, unfoldedAngle, leftEndFoldedAngle, limitedLeftProgress)
116+ } else if (limitedRightProgress > 0) {
117+ return linearAnimation(0, 1, unfoldedAngle, rightEndFoldedAngle, rightEasing.value)
118+ } else {
119+ return unfoldedAngle
120+ }
121+ }
122+
123+ readonly property real scale: limitedLeftProgress > 0 ?
124+ linearAnimation(0, 1, 1, 1 + stackScale, leftEasing.value)
125+ : limitedRightProgress > 0 ?
126+ linearAnimation(0, 1, 1, 1 + stackScale, rightEasing.value)
127+ : 0.95 + Math.abs(middleSectionProgress - 0.5) * 0.1
128+
129+ readonly property real closeIconOffset: (scale - 1) * (-root.spreadHeight / 2)
130+
131+ readonly property real tileInfoOpacity: leftFoldingAreaProgress > 0 ?
132+ linearAnimation(1, 1.5, 1, 0, leftFoldingAreaProgress)
133+ : rightFoldingAreaProgress > 0 ?
134+ linearAnimation(1, 1.5, 1, 0, rightFoldingAreaProgress)
135+ : 1
136+
137+ readonly property bool itemVisible: itemIndex == totalItems - 1 ? true : leftFoldingAreaProgress < 5 && rightFoldingAreaProgress < 5
138+ readonly property real shadowOpacity: itemIndex == totalItems -1 ?
139+ 1
140+ : leftFoldingAreaProgress > 3 ?
141+ linearAnimation(3, 3.5, 1, 0, leftFoldingAreaProgress)
142+ : rightFoldingAreaProgress > 3 ?
143+ linearAnimation(3, 3.5, 1, 0, rightFoldingAreaProgress)
144+ : 1
145+
146+
147+ // Helpers
148+ function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
149+ // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
150+ return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
151+ }
152+
153+ EasingCurve {
154+ id: leftEasing
155+ type: EasingCurve.OutSine
156+ progress: limitedLeftProgress / 2 // OutSine starts with twice the speed. slow it down.
157+ }
158+
159+ EasingCurve {
160+ id: rightEasing
161+ type: EasingCurve.OutSine
162+ progress: limitedRightProgress / 2 // OutSine starts with twice the speed. slow it down.
163+ }
164+}
qml/Stages/TabletStage.qml+14
@@ -36,6 +36,8 @@ Rectangle {
36 property bool spreadEnabled: true // If false, animations and right edge will be disabled
36 property bool spreadEnabled: true // If false, animations and right edge will be disabled
37
37
38 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
38 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
39+ property bool keepDashRunning: true
40+ property bool suspended: false
39 property int shellOrientationAngle: 0
41 property int shellOrientationAngle: 0
40 property int shellOrientation
42 property int shellOrientation
41 property int shellPrimaryOrientation
43 property int shellPrimaryOrientation
@@ -117,6 +119,7 @@ Rectangle {
117
119
118 QtObject {
120 QtObject {
119 id: priv
121 id: priv
122+ objectName: "stagesPriv"
120
123
121 property string focusedAppId: ApplicationManager.focusedApplicationId
124 property string focusedAppId: ApplicationManager.focusedApplicationId
122 readonly property var focusedAppDelegate: {
125 readonly property var focusedAppDelegate: {
@@ -626,6 +629,17 @@ Rectangle {
626
629
627 readonly property bool isDash: model.appId == "unity8-dash"
630 readonly property bool isDash: model.appId == "unity8-dash"
628
631
632+ Binding {
633+ target: spreadTile.application
634+ property: "requestedState"
635+ value: (spreadTile.isDash && root.keepDashRunning)
636+ ||
637+ (!root.suspended && (model.appId == priv.mainStageAppId
638+ || model.appId == priv.sideStageAppId))
639+ ? ApplicationInfoInterface.RequestedRunning
640+ : ApplicationInfoInterface.RequestedSuspended
641+ }
642+
629 // FIXME: A regular binding doesn't update any more after closing an app.
643 // FIXME: A regular binding doesn't update any more after closing an app.
630 // Using a Binding for now.
644 // Using a Binding for now.
631 Binding {
645 Binding {
qml/Stages/graphics/window-close.svgnew file+64
@@ -0,0 +1,64 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3+
4+<svg
5+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6+ xmlns:cc="http://creativecommons.org/ns#"
7+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8+ xmlns:svg="http://www.w3.org/2000/svg"
9+ xmlns="http://www.w3.org/2000/svg"
10+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12+ width="14.81667mm"
13+ height="14.816668mm"
14+ viewBox="0 0 14.81667 14.816668"
15+ id="svg4171"
16+ version="1.1"
17+ inkscape:version="0.91+devel r"
18+ sodipodi:docname="window-close01.svg">
19+ <defs
20+ id="defs4173" />
21+ <sodipodi:namedview
22+ id="base"
23+ pagecolor="#ffffff"
24+ bordercolor="#666666"
25+ borderopacity="1.0"
26+ inkscape:pageopacity="0.0"
27+ inkscape:pageshadow="2"
28+ inkscape:zoom="11.957142"
29+ inkscape:cx="37.299889"
30+ inkscape:cy="25.219237"
31+ inkscape:document-units="mm"
32+ inkscape:current-layer="layer1"
33+ showgrid="false"
34+ fit-margin-top="0"
35+ fit-margin-left="0"
36+ fit-margin-right="0"
37+ fit-margin-bottom="0" />
38+ <metadata
39+ id="metadata4176">
40+ <rdf:RDF>
41+ <cc:Work
42+ rdf:about="">
43+ <dc:format>image/svg+xml</dc:format>
44+ <dc:type
45+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
46+ <dc:title></dc:title>
47+ </cc:Work>
48+ </rdf:RDF>
49+ </metadata>
50+ <g
51+ inkscape:label="Layer 1"
52+ inkscape:groupmode="layer"
53+ id="layer1"
54+ transform="translate(-134.71071,-3.8416665)">
55+ <path
56+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:16.93333244;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
57+ d="M 142.11905,3.8416665 A 7.4083333,7.4083333 0 0 0 134.71071,11.25 7.4083333,7.4083333 0 0 0 142.11905,18.658334 7.4083333,7.4083333 0 0 0 149.52738,11.25 7.4083333,7.4083333 0 0 0 142.11905,3.8416665 Z"
58+ id="path4141" />
59+ <path
60+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:16.93333244;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
61+ d="m 139.31818,7.7008631 2.80087,2.8008629 2.80086,-2.8008629 0.74827,0.7482734 -2.80086,2.8008635 2.80086,2.800864 -0.74827,0.748273 -2.80086,-2.800864 -2.80087,2.800864 -0.74827,-0.748273 2.80086,-2.800864 -2.80086,-2.8008635 0.74827,-0.7482734 z"
62+ id="path4170" />
63+ </g>
64+</svg>
qml/Tutorial/Tutorial.qml−7
@@ -23,7 +23,6 @@ Item {
23 property alias active: loader.active
23 property alias active: loader.active
24 property bool paused
24 property bool paused
25 property real edgeSize
25 property real edgeSize
26− property bool useEdgeDragArea
27
26
28 property Item launcher
27 property Item launcher
29 property Item panel
28 property Item panel
@@ -59,12 +58,6 @@ Item {
59 value: root.edgeSize
58 value: root.edgeSize
60 }
59 }
61
60
62− Binding {
63− target: loader.item
64− property: "useEdgeDragArea"
65− value: root.useEdgeDragArea
66− }
67−
68 Binding {
61 Binding {
69 target: loader.item
62 target: loader.item
70 property: "launcher"
63 property: "launcher"
qml/Tutorial/TutorialContent.qml+2−9
@@ -32,7 +32,6 @@ Item {
32
32
33 property bool paused: false
33 property bool paused: false
34 property real edgeSize
34 property real edgeSize
35− property bool useEdgeDragArea
36
35
37 signal finished()
36 signal finished()
38
37
@@ -78,17 +77,11 @@ Item {
78 anchors.fill: parent
77 anchors.fill: parent
79 textXOffset: root.launcher.panelWidth
78 textXOffset: root.launcher.panelWidth
80 paused: !shown || root.paused
79 paused: !shown || root.paused
81− text: root.useEdgeDragArea ? i18n.tr("Tap here to continue.") : i18n.tr("Click here to finish.")
80+ text: i18n.tr("Tap here to continue.")
82
81
83 onFinished: {
82 onFinished: {
84 root.launcher.hide();
83 root.launcher.hide();
85− if (root.useEdgeDragArea) {
86− tutorialRight.show();
84+ tutorialRight.show();
87− } else {
88− // Last two screens use EdgeDragArea (right edge spread and
89− // bottom edge). So just end tutorial here.
90− root.finish();
91− }
92 }
85 }
93 }
86 }
94
87
tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+47
@@ -67,6 +67,7 @@ private Q_SLOTS:
67 void rejectingTouchfterItsEnd();
67 void rejectingTouchfterItsEnd();
68 void removeOldUndecidedCandidates();
68 void removeOldUndecidedCandidates();
69 void interimOwnerWontGetUnownedTouchEvents();
69 void interimOwnerWontGetUnownedTouchEvents();
70+ void candidateVanishes();
70
71
71private:
72private:
72 TouchRegistry *touchRegistry;
73 TouchRegistry *touchRegistry;
@@ -817,6 +818,52 @@ void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
817 QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
818 QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
818}
819}
819
820
821+/*
822+ Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473492
823+
824+ Bug caused by candidates getting deleted before ownership resolution
825+ */
826+void tst_TouchRegistry::candidateVanishes()
827+{
828+ DummyCandidate mainCandidate;
829+ DummyCandidate *missingCandidate = new DummyCandidate;
830+
831+ {
832+ QList<QTouchEvent::TouchPoint> touchPoints;
833+ touchPoints.append(QTouchEvent::TouchPoint(0));
834+ touchPoints[0].setState(Qt::TouchPointPressed);
835+ QTouchEvent touchEvent(QEvent::TouchBegin,
836+ 0 /* device */,
837+ Qt::NoModifier,
838+ Qt::TouchPointPressed,
839+ touchPoints);
840+ touchRegistry->update(&touchEvent);
841+ }
842+
843+ touchRegistry->addCandidateOwnerForTouch(0, &mainCandidate);
844+ touchRegistry->addCandidateOwnerForTouch(0, missingCandidate);
845+
846+ {
847+ QList<QTouchEvent::TouchPoint> touchPoints;
848+ touchPoints.append(QTouchEvent::TouchPoint(0));
849+ touchPoints[0].setState(Qt::TouchPointMoved);
850+ QTouchEvent touchEvent(QEvent::TouchUpdate,
851+ 0 /* device */,
852+ Qt::NoModifier,
853+ Qt::TouchPointMoved,
854+ touchPoints);
855+ touchRegistry->update(&touchEvent);
856+ }
857+
858+ // This candidate suddenly disappears without having removed its candidacy.
859+ delete missingCandidate;
860+ missingCandidate = nullptr;
861+
862+ touchRegistry->requestTouchOwnership(0, &mainCandidate);
863+
864+ QVERIFY(mainCandidate.ownedTouches.contains(0));
865+}
866+
820////////////// TouchMemento //////////
867////////////// TouchMemento //////////
821
868
822TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
869TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
tests/mocks/Unity/Application/ApplicationInfo.cpp+40−6
@@ -30,7 +30,7 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
30 : ApplicationInfoInterface(appId, parent)
30 : ApplicationInfoInterface(appId, parent)
31 , m_appId(appId)
31 , m_appId(appId)
32 , m_stage(MainStage)
32 , m_stage(MainStage)
33− , m_state(Starting)
33+ , m_state(Stopped)
34 , m_focused(false)
34 , m_focused(false)
35 , m_fullscreen(false)
35 , m_fullscreen(false)
36 , m_session(0)
36 , m_session(0)
@@ -39,6 +39,7 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
39 Qt::InvertedPortraitOrientation |
39 Qt::InvertedPortraitOrientation |
40 Qt::InvertedLandscapeOrientation)
40 Qt::InvertedLandscapeOrientation)
41 , m_rotatesWindowContents(false)
41 , m_rotatesWindowContents(false)
42+ , m_requestedState(RequestedRunning)
42 , m_manualSurfaceCreation(false)
43 , m_manualSurfaceCreation(false)
43{
44{
44}
45}
@@ -46,7 +47,7 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
46ApplicationInfo::ApplicationInfo(QObject *parent)
47ApplicationInfo::ApplicationInfo(QObject *parent)
47 : ApplicationInfoInterface(QString(), parent)
48 : ApplicationInfoInterface(QString(), parent)
48 , m_stage(MainStage)
49 , m_stage(MainStage)
49− , m_state(Starting)
50+ , m_state(Stopped)
50 , m_focused(false)
51 , m_focused(false)
51 , m_fullscreen(false)
52 , m_fullscreen(false)
52 , m_session(0)
53 , m_session(0)
@@ -55,6 +56,7 @@ ApplicationInfo::ApplicationInfo(QObject *parent)
55 Qt::InvertedPortraitOrientation |
56 Qt::InvertedPortraitOrientation |
56 Qt::InvertedLandscapeOrientation)
57 Qt::InvertedLandscapeOrientation)
57 , m_rotatesWindowContents(false)
58 , m_rotatesWindowContents(false)
59+ , m_requestedState(RequestedRunning)
58 , m_manualSurfaceCreation(false)
60 , m_manualSurfaceCreation(false)
59{
61{
60}
62}
@@ -74,7 +76,6 @@ void ApplicationInfo::createSession()
74
76
75void ApplicationInfo::setSession(Session* session)
77void ApplicationInfo::setSession(Session* session)
76{
78{
77− qDebug() << "Application::setSession - appId=" << appId() << "session=" << session;
78 if (m_session == session)
79 if (m_session == session)
79 return;
80 return;
80
81
@@ -91,6 +92,8 @@ void ApplicationInfo::setSession(Session* session)
91 m_session->setApplication(this);
92 m_session->setApplication(this);
92 m_session->setParent(this);
93 m_session->setParent(this);
93 SessionManager::singleton()->registerSession(m_session);
94 SessionManager::singleton()->registerSession(m_session);
95+ connect(m_session, &Session::surfaceChanged,
96+ this, &ApplicationInfo::onSessionSurfaceChanged);
94
97
95 if (!m_manualSurfaceCreation) {
98 if (!m_manualSurfaceCreation) {
96 QTimer::singleShot(500, m_session, SLOT(createSurface()));
99 QTimer::singleShot(500, m_session, SLOT(createSurface()));
@@ -161,11 +164,12 @@ void ApplicationInfo::setState(State value)
161 m_state = value;
164 m_state = value;
162 Q_EMIT stateChanged(value);
165 Q_EMIT stateChanged(value);
163
166
164− if (!m_manualSurfaceCreation && m_state == ApplicationInfo::Running) {
167+ if (!m_manualSurfaceCreation && !m_session && m_state == ApplicationInfo::Starting) {
165 QTimer::singleShot(500, this, SLOT(createSession()));
168 QTimer::singleShot(500, this, SLOT(createSession()));
166 } else if (m_state == ApplicationInfo::Stopped) {
169 } else if (m_state == ApplicationInfo::Stopped) {
167− delete m_session;
170+ Session *session = m_session;
168− m_session = nullptr;
171+ setSession(nullptr);
172+ delete session;
169 }
173 }
170 }
174 }
171}
175}
@@ -213,3 +217,33 @@ void ApplicationInfo::setRotatesWindowContents(bool value)
213{
217{
214 m_rotatesWindowContents = value;
218 m_rotatesWindowContents = value;
215}
219}
220+
221+ApplicationInfo::RequestedState ApplicationInfo::requestedState() const
222+{
223+ return m_requestedState;
224+}
225+
226+void ApplicationInfo::setRequestedState(RequestedState value)
227+{
228+ if (m_requestedState != value) {
229+ m_requestedState = value;
230+ Q_EMIT requestedStateChanged(m_requestedState);
231+
232+ if (m_requestedState == RequestedRunning && m_state == Suspended) {
233+ setState(Running);
234+ } else if (m_requestedState == RequestedSuspended && m_state == Running) {
235+ setState(Suspended);
236+ }
237+ }
238+}
239+
240+void ApplicationInfo::onSessionSurfaceChanged(MirSurfaceItem* surface)
241+{
242+ if (surface != nullptr && m_state == Starting) {
243+ if (m_requestedState == RequestedRunning) {
244+ setState(Running);
245+ } else {
246+ setState(Suspended);
247+ }
248+ }
249+}
tests/mocks/Unity/Application/ApplicationInfo.h+7
@@ -46,6 +46,9 @@ public:
46 ApplicationInfo(const QString &appId, QObject *parent = nullptr);
46 ApplicationInfo(const QString &appId, QObject *parent = nullptr);
47 ~ApplicationInfo();
47 ~ApplicationInfo();
48
48
49+ RequestedState requestedState() const override;
50+ void setRequestedState(RequestedState) override;
51+
49 void setIconId(const QString &iconId);
52 void setIconId(const QString &iconId);
50 void setScreenshotId(const QString &screenshotId);
53 void setScreenshotId(const QString &screenshotId);
51
54
@@ -101,6 +104,9 @@ Q_SIGNALS:
101public Q_SLOTS:
104public Q_SLOTS:
102 Q_INVOKABLE void createSession();
105 Q_INVOKABLE void createSession();
103
106
107+private Q_SLOTS:
108+ void onSessionSurfaceChanged(MirSurfaceItem*);
109+
104private:
110private:
105 void setIcon(const QUrl &value);
111 void setIcon(const QUrl &value);
106
112
@@ -116,6 +122,7 @@ private:
116 Session* m_session;
122 Session* m_session;
117 Qt::ScreenOrientations m_supportedOrientations;
123 Qt::ScreenOrientations m_supportedOrientations;
118 bool m_rotatesWindowContents;
124 bool m_rotatesWindowContents;
125+ RequestedState m_requestedState;
119
126
120 bool m_manualSurfaceCreation;
127 bool m_manualSurfaceCreation;
121};
128};
tests/mocks/Unity/Application/ApplicationManager.cpp+2−49
@@ -49,8 +49,6 @@ ApplicationManager *ApplicationManager::singleton()
49
49
50ApplicationManager::ApplicationManager(QObject *parent)
50ApplicationManager::ApplicationManager(QObject *parent)
51 : ApplicationManagerInterface(parent)
51 : ApplicationManagerInterface(parent)
52− , m_suspended(false)
53− , m_forceDashActive(false)
54{
52{
55 m_roleNames.insert(RoleSession, "session");
53 m_roleNames.insert(RoleSession, "session");
56 m_roleNames.insert(RoleFullscreen, "fullscreen");
54 m_roleNames.insert(RoleFullscreen, "fullscreen");
@@ -217,7 +215,7 @@ ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
217 && application->stage() == ApplicationInfo::SideStage) {
215 && application->stage() == ApplicationInfo::SideStage) {
218 application->setStage(ApplicationInfo::MainStage);
216 application->setStage(ApplicationInfo::MainStage);
219 }
217 }
220− application->setState(ApplicationInfo::Running);
218+ application->setState(ApplicationInfo::Starting);
221
219
222 return application;
220 return application;
223}
221}
@@ -262,50 +260,6 @@ QString ApplicationManager::focusedApplicationId() const {
262 return QString();
260 return QString();
263}
261}
264
262
265−bool ApplicationManager::suspended() const
266−{
267− return m_suspended;
268−}
269−
270−void ApplicationManager::setSuspended(bool suspended)
271−{
272− ApplicationInfo *focusedApp = findApplication(focusedApplicationId());
273− if (focusedApp) {
274− if (suspended) {
275− focusedApp->setState(ApplicationInfo::Suspended);
276− } else {
277− focusedApp->setState(ApplicationInfo::Running);
278− }
279− }
280− m_suspended = suspended;
281− Q_EMIT suspendedChanged();
282−}
283−
284−bool ApplicationManager::forceDashActive() const
285−{
286− return m_forceDashActive;
287−}
288−
289−void ApplicationManager::setForceDashActive(bool forceDashActive)
290−{
291− if (m_forceDashActive == forceDashActive) {
292− return;
293− }
294−
295− ApplicationInfo *dash = findApplication("unity8-dash");
296− if (dash) {
297− if (forceDashActive) {
298− dash->setState(ApplicationInfo::Running);
299− } else {
300− if (!dash->focused()) {
301− dash->setState(ApplicationInfo::Suspended);
302− }
303− }
304− }
305− m_forceDashActive = forceDashActive;
306− Q_EMIT forceDashActiveChanged();
307−}
308−
309bool ApplicationManager::focusApplication(const QString &appId)
263bool ApplicationManager::focusApplication(const QString &appId)
310{
264{
311 ApplicationInfo *application = findApplication(appId);
265 ApplicationInfo *application = findApplication(appId);
@@ -316,13 +270,11 @@ bool ApplicationManager::focusApplication(const QString &appId)
316 for (ApplicationInfo *app : m_runningApplications) {
270 for (ApplicationInfo *app : m_runningApplications) {
317 if (app->focused()) {
271 if (app->focused()) {
318 app->setFocused(false);
272 app->setFocused(false);
319− app->setState(ApplicationInfo::Suspended);
320 }
273 }
321 }
274 }
322
275
323 // focus this app
276 // focus this app
324 application->setFocused(true);
277 application->setFocused(true);
325− application->setState(ApplicationInfo::Running);
326
278
327 // move app to top of stack
279 // move app to top of stack
328 move(m_runningApplications.indexOf(application), 0);
280 move(m_runningApplications.indexOf(application), 0);
@@ -394,6 +346,7 @@ void ApplicationManager::buildListOfAvailableApplications()
394 application->setScreenshotId("gallery");
346 application->setScreenshotId("gallery");
395 application->setIconId("gallery");
347 application->setIconId("gallery");
396 application->setFullscreen(true);
348 application->setFullscreen(true);
349+ application->setStage(ApplicationInfo::MainStage);
397 m_availableApplications.append(application);
350 m_availableApplications.append(application);
398
351
399 application = new ApplicationInfo(this);
352 application = new ApplicationInfo(this);
tests/mocks/Unity/Application/ApplicationManager.h−7
@@ -69,11 +69,6 @@ class ApplicationManager : public ApplicationManagerInterface {
69 Q_INVOKABLE bool stopApplication(const QString &appId) override;
69 Q_INVOKABLE bool stopApplication(const QString &appId) override;
70
70
71 QString focusedApplicationId() const override;
71 QString focusedApplicationId() const override;
72− bool suspended() const override;
73− void setSuspended(bool suspended) override;
74−
75− bool forceDashActive() const override;
76− void setForceDashActive(bool forceDashActive) override;
77
72
78 // Only for testing
73 // Only for testing
79 QStringList availableApplications();
74 QStringList availableApplications();
@@ -96,8 +91,6 @@ class ApplicationManager : public ApplicationManagerInterface {
96 void remove(ApplicationInfo* application);
91 void remove(ApplicationInfo* application);
97 void buildListOfAvailableApplications();
92 void buildListOfAvailableApplications();
98 void onWindowCreated();
93 void onWindowCreated();
99− bool m_suspended;
100− bool m_forceDashActive;
101 QList<ApplicationInfo*> m_runningApplications;
94 QList<ApplicationInfo*> m_runningApplications;
102 QList<ApplicationInfo*> m_availableApplications;
95 QList<ApplicationInfo*> m_availableApplications;
103 QTimer m_windowCreatedTimer;
96 QTimer m_windowCreatedTimer;
tests/mocks/Unity/CMakeLists.txt+1−1
@@ -7,7 +7,7 @@ add_subdirectory(Notifications)
7add_subdirectory(DashCommunicator)
7add_subdirectory(DashCommunicator)
8
8
9pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
9pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
10−pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=6)
10+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=7)
11
11
12include_directories(
12include_directories(
13 ${CMAKE_CURRENT_BINARY_DIR}
13 ${CMAKE_CURRENT_BINARY_DIR}
tests/mocks/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
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−1
@@ -34,6 +34,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
34 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);
35 item->setProgress(50);
35 item->setProgress(50);
36 item->setCountVisible(true);
36 item->setCountVisible(true);
37+ item->setAlerting(false);
37 m_list.append(item);
38 m_list.append(item);
38 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);
39 m_list.append(item);
40 m_list.append(item);
@@ -43,25 +44,31 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
43 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);
44 item->setCount(1);
45 item->setCount(1);
45 item->setCountVisible(true);
46 item->setCountVisible(true);
47+ item->setRunning(true);
48+ item->setAlerting(false);
46 m_list.append(item);
49 m_list.append(item);
47 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);
48 item->setCount(12);
51 item->setCount(12);
49 item->setCountVisible(true);
52 item->setCountVisible(true);
53+ item->setAlerting(false);
50 item->setPinned(true);
54 item->setPinned(true);
51 m_list.append(item);
55 m_list.append(item);
52 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);
53 item->setCount(123);
57 item->setCount(123);
54 item->setCountVisible(true);
58 item->setCountVisible(true);
59+ item->setAlerting(false);
55 m_list.append(item);
60 m_list.append(item);
56 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);
57 item->setCount(1234567890);
62 item->setCount(1234567890);
58 item->setCountVisible(true);
63 item->setCountVisible(true);
64+ item->setAlerting(false);
59 item->setPinned(true);
65 item->setPinned(true);
60 m_list.append(item);
66 m_list.append(item);
61 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);
62 item->setProgress(50);
68 item->setProgress(50);
63 item->setCount(5);
69 item->setCount(5);
64 item->setCountVisible(true);
70 item->setCountVisible(true);
71+ item->setAlerting(false);
65 item->setFocused(true);
72 item->setFocused(true);
66 item->setPinned(true);
73 item->setPinned(true);
67 m_list.append(item);
74 m_list.append(item);
@@ -105,6 +112,8 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
105 return item->countVisible();
112 return item->countVisible();
106 case RoleFocused:
113 case RoleFocused:
107 return item->focused();
114 return item->focused();
115+ case RoleAlerting:
116+ return item->alerting();
108 }
117 }
109
118
110 return QVariant();
119 return QVariant();
@@ -119,6 +128,18 @@ unity::shell::launcher::LauncherItemInterface *MockLauncherModel::get(int index)
119 return m_list.at(index);
128 return m_list.at(index);
120}
129}
121
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+
122void MockLauncherModel::move(int oldIndex, int newIndex)
143void MockLauncherModel::move(int oldIndex, int newIndex)
123{
144{
124 // Make sure its not moved outside the lists
145 // Make sure its not moved outside the lists
@@ -174,7 +195,9 @@ void MockLauncherModel::requestRemove(const QString &appId)
174 int index = findApp(appId);
195 int index = findApp(appId);
175 if (index >= 0) {
196 if (index >= 0) {
176 beginRemoveRows(QModelIndex(), index, 0);
197 beginRemoveRows(QModelIndex(), index, 0);
177− m_list.takeAt(index)->deleteLater();
198+ MockLauncherItem * item = m_list.takeAt(index);
199+ item->setRunning(false);
200+ item->deleteLater();
178 endRemoveRows();
201 endRemoveRows();
179 }
202 }
180}
203}
@@ -195,6 +218,16 @@ int MockLauncherModel::findApp(const QString &appId)
195 return -1;
218 return -1;
196}
219}
197
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+
198void MockLauncherModel::setUser(const QString &username)
231void MockLauncherModel::setUser(const QString &username)
199{
232{
200 Q_UNUSED(username)
233 Q_UNUSED(username)
@@ -226,6 +259,26 @@ void MockLauncherModel::emitHint()
226 Q_EMIT hint();
259 Q_EMIT hint();
227}
260}
228
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);
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+
229unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
282unity::shell::application::ApplicationManagerInterface *MockLauncherModel::applicationManager() const
230{
283{
231 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/mocks/Unity/fake_scope.cpp+5−3
@@ -181,18 +181,20 @@ void Scope::setNoResultsHint(const QString& str)
181 }
181 }
182}
182}
183
183
184−void Scope::activate(QVariant const& result)
184+void Scope::activate(QVariant const& result, QString const& categoryId)
185{
185{
186− qDebug() << "Called activate on scope" << m_id << "with result" << result;
186+ qDebug() << "Called activate on scope" << m_id << "with result" << result << "and category" << categoryId;
187 if (result.toString() == "Result.2.2") {
187 if (result.toString() == "Result.2.2") {
188 Scopes *scopes = dynamic_cast<Scopes*>(parent());
188 Scopes *scopes = dynamic_cast<Scopes*>(parent());
189 m_openScope = scopes->getScopeFromAll("MockScope9");
189 m_openScope = scopes->getScopeFromAll("MockScope9");
190 scopes->addTempScope(m_openScope);
190 scopes->addTempScope(m_openScope);
191 Q_EMIT openScope(m_openScope);
191 Q_EMIT openScope(m_openScope);
192+ } else {
193+ Q_EMIT previewRequested(result);
192 }
194 }
193}
195}
194
196
195−PreviewStack* Scope::preview(QVariant const& result)
197+PreviewStack* Scope::preview(QVariant const& result, QString const& /*categoryId*/)
196{
198{
197 Q_UNUSED(result);
199 Q_UNUSED(result);
198
200
tests/mocks/Unity/fake_scope.h+2−2
@@ -60,8 +60,8 @@ public:
60 Q_INVOKABLE void setName(const QString &name); // This is not invokable in the Interface, here for testing benefits
60 Q_INVOKABLE void setName(const QString &name); // This is not invokable in the Interface, here for testing benefits
61 Q_INVOKABLE void setSearchInProgress(const bool inProg); // This is not invokable in the Interface, here for testing benefits
61 Q_INVOKABLE void setSearchInProgress(const bool inProg); // This is not invokable in the Interface, here for testing benefits
62
62
63− Q_INVOKABLE void activate(QVariant const& result) override;
63+ Q_INVOKABLE void activate(QVariant const& result, QString const& categoryId) override;
64− Q_INVOKABLE PreviewStack* preview(QVariant const& result) override;
64+ Q_INVOKABLE PreviewStack* preview(QVariant const& result, QString const& categoryId) override;
65 Q_INVOKABLE void cancelActivation() override;
65 Q_INVOKABLE void cancelActivation() override;
66 Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
66 Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
67
67
tests/mocks/Unity/fake_scopesoverview.cpp+1−1
@@ -41,7 +41,7 @@ void ScopesOverview::setSearchQuery(const QString& search_query)
41 Q_EMIT categoriesChanged();
41 Q_EMIT categoriesChanged();
42}
42}
43
43
44−Q_INVOKABLE void ScopesOverview::activate(QVariant const& result)
44+Q_INVOKABLE void ScopesOverview::activate(QVariant const& result, QString const& /*categoryId*/)
45{
45{
46 Scopes *scopes = dynamic_cast<Scopes*>(parent());
46 Scopes *scopes = dynamic_cast<Scopes*>(parent());
47 if (scopes->getScope(result.toString())) {
47 if (scopes->getScope(result.toString())) {
tests/mocks/Unity/fake_scopesoverview.h+1−1
@@ -32,7 +32,7 @@ public:
32 ScopesOverview(Scopes* parent = 0);
32 ScopesOverview(Scopes* parent = 0);
33
33
34 void setSearchQuery(const QString& search_query) override;
34 void setSearchQuery(const QString& search_query) override;
35− Q_INVOKABLE void activate(QVariant const& result) override;
35+ Q_INVOKABLE void activate(QVariant const& result, QString const& categoryId) override;
36
36
37 // This is implementation detail
37 // This is implementation detail
38 void setFavorite(Scope *scope, bool favorite);
38 void setFavorite(Scope *scope, bool favorite);
tests/mocks/Utils/CMakeLists.txt+3−1
@@ -4,12 +4,12 @@ include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_BINARY_DIR}
5 ${CMAKE_CURRENT_BINARY_DIR}
6 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
6 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
7 ${GIO_INCLUDE_DIRS}
8 ${GIO_INCLUDE_DIRS}
8 ${CMAKE_SOURCE_DIR}/plugins/Utils
9 ${CMAKE_SOURCE_DIR}/plugins/Utils
9)
10)
10
11
11set(QMLPLUGIN_SRC
12set(QMLPLUGIN_SRC
12− ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
13 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
13 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
@@ -18,9 +18,11 @@ set(QMLPLUGIN_SRC
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
21 constants.cpp
22 constants.cpp
22 plugin.cpp
23 plugin.cpp
23 windowstatestorage.cpp
24 windowstatestorage.cpp
25+ plugin.cpp
24 )
26 )
25
27
26add_library(FakeUtils-qml SHARED
28add_library(FakeUtils-qml SHARED
tests/mocks/Utils/plugin.cpp+9−1
@@ -23,6 +23,14 @@
23
23
24// local
24// local
25#include "plugin.h"
25#include "plugin.h"
26+#include "inputwatcher.h"
27+#include "qlimitproxymodelqml.h"
28+#include "unitysortfilterproxymodelqml.h"
29+#include "relativetimeformatter.h"
30+#include "timeformatter.h"
31+#include "unitymenumodelpaths.h"
32+#include "windowkeysfilter.h"
33+#include "easingcurve.h"
26#include "windowstatestorage.h"
34#include "windowstatestorage.h"
27#include "constants.h"
35#include "constants.h"
28
36
@@ -53,7 +61,7 @@ static QObject *createConstants(QQmlEngine *engine, QJSEngine *scriptEngine)
53
61
54void FakeUtilsPlugin::registerTypes(const char *uri)
62void FakeUtilsPlugin::registerTypes(const char *uri)
55{
63{
56− Q_ASSERT(uri == QLatin1String("Utils"));;
64+ Q_ASSERT(uri == QLatin1String("Utils"));
57 qmlRegisterType<QAbstractItemModel>();
65 qmlRegisterType<QAbstractItemModel>();
58 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
66 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
59 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
67 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
tests/plugins/CMakeLists.txt+1
@@ -2,6 +2,7 @@ add_subdirectory(AccountsService)
2add_subdirectory(Greeter)
2add_subdirectory(Greeter)
3add_subdirectory(LightDM)
3add_subdirectory(LightDM)
4add_subdirectory(Dash)
4add_subdirectory(Dash)
5+add_subdirectory(ScreenGrabber)
5add_subdirectory(Ubuntu)
6add_subdirectory(Ubuntu)
6add_subdirectory(Unity)
7add_subdirectory(Unity)
7add_subdirectory(Utils)
8add_subdirectory(Utils)
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−2
@@ -1,5 +1,4 @@
1−pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
1+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
3
2
4include_directories(
3include_directories(
5 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
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/ScreenGrabber/CMakeLists.txtnew file+14
@@ -0,0 +1,14 @@
1+include_directories(
2+ ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber
3+ ${CMAKE_CURRENT_BINARY_DIR}
4+ )
5+
6+add_definitions(
7+ -DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
8+ )
9+add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
10+
11+add_executable(ScreenGrabberTestExec ScreenGrabberTest.cpp ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber/screengrabber.cpp)
12+qt5_use_modules(ScreenGrabberTestExec Test Quick)
13+target_link_libraries(ScreenGrabberTestExec ${Qt5Quick_LIBRARIES} ScreenGrabber-qml)
14+add_unity8_uitest(ScreenGrabber ScreenGrabberTestExec DEPENDS ScreenGrabber-qml)
tests/plugins/ScreenGrabber/ScreenGrabberTest.cppnew file+65
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "screengrabber.h"
18+
19+#include <QtTest>
20+#include <QtQuick/QQuickView>
21+#include <QtQuick/QQuickItem>
22+#include <QQmlEngine>
23+
24+class ScreenGrabberTest: public QObject
25+{
26+ Q_OBJECT
27+
28+private Q_SLOTS:
29+
30+ void initTestCase()
31+ {
32+ m_view = new QQuickView();
33+ m_view->engine()->addImportPath(BUILT_PLUGINS_DIR);
34+ m_view->setSource(QUrl::fromLocalFile(CURRENT_SOURCE_DIR "/grabber.qml"));
35+ m_grabber = dynamic_cast<ScreenGrabber*>(m_view->rootObject()->property("grabber").value<QObject*>());
36+ QVERIFY(m_grabber);
37+ m_view->show();
38+ QTest::qWaitForWindowExposed(m_view);
39+ }
40+
41+ void cleanupTestCase()
42+ {
43+ delete m_view;
44+ m_view = nullptr;
45+ QTRY_COMPARE(QThreadPool::globalInstance()->activeThreadCount(), 0);
46+ }
47+
48+ void testGrabScreenshot()
49+ {
50+ QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
51+ m_grabber->captureAndSave();
52+ QTRY_VERIFY(grabberSpy.count() == 1);
53+ const QVariantList args = grabberSpy.takeFirst();
54+ QVERIFY(args.count() == 1); // verify we got a non-empty filename where the screenshot has been saved
55+ QVERIFY(!args.first().toString().isEmpty());
56+ }
57+
58+private:
59+ QQuickView *m_view;
60+ ScreenGrabber *m_grabber = nullptr;
61+};
62+
63+QTEST_MAIN(ScreenGrabberTest)
64+
65+#include "ScreenGrabberTest.moc"
tests/plugins/ScreenGrabber/grabber.qmlnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import ScreenGrabber 0.1
19+
20+Rectangle {
21+ property var grabber: screenGrabber
22+
23+ width: 100
24+ height: 100
25+ color: "green"
26+
27+ ScreenGrabber {
28+ id: screenGrabber
29+ objectName: "screenGrabber"
30+ }
31+}
tests/plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
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+83−11
@@ -39,6 +39,9 @@ class MockApp: public unity::shell::application::ApplicationInfoInterface
39 Q_OBJECT
39 Q_OBJECT
40public:
40public:
41 MockApp(const QString &appId, QObject *parent = 0): ApplicationInfoInterface(appId, parent), m_appId(appId), m_focused(false) { }
41 MockApp(const QString &appId, QObject *parent = 0): ApplicationInfoInterface(appId, parent), m_appId(appId), m_focused(false) { }
42+
43+ RequestedState requestedState() const override { return RequestedRunning; }
44+ void setRequestedState(RequestedState) override {}
42 QString appId() const override { return m_appId; }
45 QString appId() const override { return m_appId; }
43 QString name() const override { return "mock"; }
46 QString name() const override { return "mock"; }
44 QString comment() const override { return "this is a mock"; }
47 QString comment() const override { return "this is a mock"; }
@@ -68,6 +71,7 @@ class MockAppManager: public unity::shell::application::ApplicationManagerInterf
68 Q_OBJECT
71 Q_OBJECT
69public:
72public:
70 MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
73 MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
74+ ~MockAppManager() {}
71 int rowCount(const QModelIndex &) const override { return m_list.count(); }
75 int rowCount(const QModelIndex &) const override { return m_list.count(); }
72 QVariant data(const QModelIndex &, int ) const override { return QVariant(); }
76 QVariant data(const QModelIndex &, int ) const override { return QVariant(); }
73 QString focusedApplicationId() const override {
77 QString focusedApplicationId() const override {
@@ -116,10 +120,6 @@ public:
116 endRemoveRows();
120 endRemoveRows();
117 }
121 }
118 bool requestFocusApplication(const QString &appId) override { Q_UNUSED(appId); return true; }
122 bool requestFocusApplication(const QString &appId) override { Q_UNUSED(appId); return true; }
119− bool suspended() const override { return false; }
120− void setSuspended(bool) override {}
121− bool forceDashActive() const override { return false; }
122− void setForceDashActive(bool) override {}
123
123
124private:
124private:
125 QList<MockApp*> m_list;
125 QList<MockApp*> m_list;
@@ -298,6 +298,22 @@ private Q_SLOTS:
298 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
298 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
299 }
299 }
300
300
301+ void testApplicationRunning() {
302+ launcherModel->pin("abs-icon");
303+ launcherModel->pin("no-icon");
304+
305+ QCOMPARE(launcherModel->get(0)->running(), true);
306+ QCOMPARE(launcherModel->get(1)->running(), true);
307+
308+ appManager->stopApplication("abs-icon");
309+ QCOMPARE(launcherModel->get(0)->running(), false);
310+ QCOMPARE(launcherModel->get(1)->running(), true);
311+
312+ appManager->stopApplication("no-icon");
313+ QCOMPARE(launcherModel->get(0)->running(), false);
314+ QCOMPARE(launcherModel->get(1)->running(), false);
315+ }
316+
301 void testApplicationFocused() {
317 void testApplicationFocused() {
302 // all apps unfocused at beginning...
318 // all apps unfocused at beginning...
303 QCOMPARE(launcherModel->get(0)->focused(), false);
319 QCOMPARE(launcherModel->get(0)->focused(), false);
@@ -325,6 +341,46 @@ private Q_SLOTS:
325 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
341 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
326 }
342 }
327
343
344+ void testQuitMenuItem() {
345+ // we have 2 apps running, both should have the Quit action in its quick list
346+ QCOMPARE(launcherModel->rowCount(), 2);
347+
348+ // stop the second one keeping it pinned so that it doesn't go away
349+ launcherModel->pin("no-icon");
350+ appManager->stopApplication("no-icon");
351+
352+ // find the first Quit item, should be there
353+ QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
354+ int quitActionIndex = -1;
355+ for (int i = 0; i < model->rowCount(); ++i) {
356+ if (model->get(i).actionId() == "stop_item") {
357+ quitActionIndex = i;
358+ break;
359+ }
360+ }
361+ QVERIFY(quitActionIndex >= 0);
362+
363+ // find the second Quit item, should NOT be there, the app is stopped
364+ QuickListModel *model2 = qobject_cast<QuickListModel*>(launcherModel->get(1)->quickList());
365+ int quitActionIndex2 = -1;
366+ for (int i = 0; i < model2->rowCount(); ++i) {
367+ if (model2->get(i).actionId() == "stop_item") {
368+ quitActionIndex2 = i;
369+ break;
370+ }
371+ }
372+ QVERIFY(quitActionIndex2 == -1);
373+
374+ // trigger the first quit item quicklist action
375+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), quitActionIndex);
376+ // first app should be gone...
377+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
378+ // ... the second app (now at index 0) should still be there, pinned and stopped
379+ QCOMPARE(launcherModel->get(0)->appId(), QStringLiteral("no-icon"));
380+ QCOMPARE(launcherModel->get(0)->pinned(), true);
381+ QCOMPARE(launcherModel->get(0)->running(), false);
382+ }
383+
328 void testGetUrlForAppId() {
384 void testGetUrlForAppId() {
329 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
385 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
330 QCOMPARE(launcherModel->getUrlForAppId(""), QString());
386 QCOMPARE(launcherModel->getUrlForAppId(""), QString());
@@ -368,11 +424,18 @@ private Q_SLOTS:
368 }
424 }
369
425
370 void testCountEmblems() {
426 void testCountEmblems() {
427+ QSignalSpy spy(launcherModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
428+
371 // Call GetAll on abs-icon
429 // Call GetAll on abs-icon
372 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
430 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
373 QDBusReply<QVariantMap> reply = interface.call("GetAll");
431 QDBusReply<QVariantMap> reply = interface.call("GetAll");
374 QVariantMap map = reply.value();
432 QVariantMap map = reply.value();
375
433
434+ // Check that the alerting-status is still false, and the item on the upper side of the API
435+ int index = launcherModel->findApplication("abs-icon");
436+ QCOMPARE(index >= 0, true);
437+ QVERIFY(launcherModel->get(index)->alerting() == false);
438+
376 // Make sure GetAll returns a map with count and countVisible props
439 // Make sure GetAll returns a map with count and countVisible props
377 QCOMPARE(map.contains("count"), true);
440 QCOMPARE(map.contains("count"), true);
378 QCOMPARE(map.contains("countVisible"), true);
441 QCOMPARE(map.contains("countVisible"), true);
@@ -393,13 +456,8 @@ private Q_SLOTS:
393 QCOMPARE(map.value("count").toInt(), 55);
456 QCOMPARE(map.value("count").toInt(), 55);
394 QCOMPARE(map.value("countVisible").toBool(), true);
457 QCOMPARE(map.value("countVisible").toBool(), true);
395
458
396− // Now the item on the upper side of the API
459+ // Finally check, that the change to "count" implicitly also set the alerting-state to true
397− int index = launcherModel->findApplication("abs-icon");
398− QCOMPARE(index >= 0, true);
399−
400− // And make sure values have changed there as well
401− QCOMPARE(launcherModel->get(index)->countVisible(), true);
460+ QVERIFY(launcherModel->get(index)->alerting() == true);
402− QCOMPARE(launcherModel->get(index)->count(), 55);
403 }
461 }
404
462
405 void testCountEmblemAddsRemovesItem_data() {
463 void testCountEmblemAddsRemovesItem_data() {
@@ -459,6 +517,20 @@ private Q_SLOTS:
459 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
517 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
460 }
518 }
461
519
520+ void testAlert() {
521+ // Check that the alerting-status is still false
522+ int index = launcherModel->findApplication("abs-icon");
523+ QCOMPARE(index >= 0, true);
524+ QVERIFY(launcherModel->get(index)->alerting() == false);
525+
526+ // Call Alert() on "abs-icon"
527+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "com.canonical.Unity.Launcher.Item");
528+ interface.call("Alert");
529+
530+ // Check that the alerting-status is now true
531+ QVERIFY(launcherModel->get(index)->alerting() == true);
532+ }
533+
462 void testRefreshAfterDeletedDesktopFiles_data() {
534 void testRefreshAfterDeletedDesktopFiles_data() {
463 QTest::addColumn<bool>("deleted");
535 QTest::addColumn<bool>("deleted");
464 QTest::newRow("have .desktop files") << false;
536 QTest::newRow("have .desktop files") << false;
tests/plugins/Unity/Session/sessionbackendtest.cpp+54−8
@@ -24,6 +24,11 @@
24#include <QDBusInterface>
24#include <QDBusInterface>
25#include <QDBusReply>
25#include <QDBusReply>
26#include <QDBusVariant>
26#include <QDBusVariant>
27+#include <QDebug>
28+#include <QDBusObjectPath>
29+
30+#include <unistd.h>
31+#include <sys/types.h>
27
32
28#include "dbusunitysessionservice.h"
33#include "dbusunitysessionservice.h"
29
34
@@ -52,9 +57,10 @@ private Q_SLOTS:
52 QTest::addColumn<QString>("method");
57 QTest::addColumn<QString>("method");
53 QTest::addColumn<QString>("signal");
58 QTest::addColumn<QString>("signal");
54
59
55− QTest::newRow("Logout") << "RequestLogout" << "logoutRequested(bool)";
60+ QTest::newRow("Logout") << "RequestLogout" << "LogoutRequested(bool)";
56− QTest::newRow("Reboot") << "RequestReboot" << "rebootRequested(bool)";
61+ QTest::newRow("Reboot") << "RequestReboot" << "RebootRequested(bool)";
57− QTest::newRow("Shutdown") << "RequestShutdown" << "shutdownRequested(bool)";
62+ QTest::newRow("Shutdown") << "RequestShutdown" << "ShutdownRequested(bool)";
63+ QTest::newRow("PromptLock") << "PromptLock" << "LockRequested()";
58 }
64 }
59
65
60 void testUnitySessionLogoutRequested() {
66 void testUnitySessionLogoutRequested() {
@@ -65,7 +71,7 @@ private Q_SLOTS:
65 QCoreApplication::processEvents(); // to let the service register on DBus
71 QCoreApplication::processEvents(); // to let the service register on DBus
66
72
67 // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
73 // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
68− QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
74+ QSignalSpy spy(&dbusUnitySessionService, qPrintable(signal.prepend(QSIGNAL_CODE)));
69
75
70 QDBusReply<void> reply = dbusUnitySession->call(method);
76 QDBusReply<void> reply = dbusUnitySession->call(method);
71 QCOMPARE(reply.isValid(), true);
77 QCOMPARE(reply.isValid(), true);
@@ -77,9 +83,9 @@ private Q_SLOTS:
77 QTest::addColumn<uint>("method");
83 QTest::addColumn<uint>("method");
78 QTest::addColumn<QString>("signal");
84 QTest::addColumn<QString>("signal");
79
85
80− QTest::newRow("Logout") << (uint)Action::LOGOUT << "logoutRequested(bool)";
86+ QTest::newRow("Logout") << (uint)Action::LOGOUT << "LogoutRequested(bool)";
81− QTest::newRow("Shutdown") << (unsigned)Action::SHUTDOWN << "shutdownRequested(bool)";
87+ QTest::newRow("Shutdown") << (uint)Action::SHUTDOWN << "ShutdownRequested(bool)";
82− QTest::newRow("Reboot") << (unsigned)Action::REBOOT << "rebootRequested(bool)";
88+ QTest::newRow("Reboot") << (uint)Action::REBOOT << "RebootRequested(bool)";
83 }
89 }
84
90
85 void testGnomeSessionWrapper() {
91 void testGnomeSessionWrapper() {
@@ -92,7 +98,7 @@ private Q_SLOTS:
92 // Spy on the given signal on the /com/canonical/Unity/Session object
98 // Spy on the given signal on the /com/canonical/Unity/Session object
93 // as proof we are actually calling the actual method.
99 // 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)"
100 // .. because QSignalSpy checks the signal signature like this: "if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE)"
95− QSignalSpy spy(&dbusUnitySessionService, signal.prepend(QSIGNAL_CODE).toLocal8Bit().constData());
101+ QSignalSpy spy(&dbusUnitySessionService, qPrintable(signal.prepend(QSIGNAL_CODE)));
96
102
97 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
103 DBusGnomeSessionManagerWrapper dbusGnomeSessionManagerWrapper;
98 QCoreApplication::processEvents(); // to let the service register on DBus
104 QCoreApplication::processEvents(); // to let the service register on DBus
@@ -116,6 +122,46 @@ private Q_SLOTS:
116 QCOMPARE(spy.count(), 1);
122 QCOMPARE(spy.count(), 1);
117 }
123 }
118
124
125+ void testUserName() {
126+ DBusUnitySessionService dbusUnitySessionService;
127+ QCoreApplication::processEvents(); // to let the service register on DBus
128+
129+ QProcess * proc = new QProcess(this);
130+ proc->start("id -un", QProcess::ReadOnly);
131+ proc->waitForFinished();
132+ const QByteArray out = proc->readAll().trimmed();
133+
134+ QCOMPARE(dbusUnitySessionService.UserName(), QString::fromUtf8(out));
135+ }
136+
137+ void testRealName() {
138+ DBusUnitySessionService dbusUnitySessionService;
139+ QCoreApplication::processEvents(); // to let the service register on DBus
140+
141+ QDBusInterface accIface("org.freedesktop.Accounts", "/org/freedesktop/Accounts", "org.freedesktop.Accounts", QDBusConnection::systemBus());
142+ if (accIface.isValid()) {
143+ QDBusReply<QDBusObjectPath> userPath = accIface.asyncCall("FindUserById", static_cast<qint64>(geteuid()));
144+ if (userPath.isValid()) {
145+ QDBusInterface userAccIface("org.freedesktop.Accounts", userPath.value().path(), "org.freedesktop.Accounts.User", QDBusConnection::systemBus());
146+ QCOMPARE(dbusUnitySessionService.RealName(), userAccIface.property("RealName").toString());
147+ }
148+ }
149+ }
150+
151+ void testLogin1Capabilities() {
152+ DBusUnitySessionService dbusUnitySessionService;
153+ QDBusInterface login1face("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", QDBusConnection::systemBus());
154+ QCoreApplication::processEvents(); // to let the services register on DBus
155+
156+ if (login1face.isValid()) {
157+ QCOMPARE(dbusUnitySessionService.CanHibernate(), (login1face.call("CanHibernate").arguments().first().toString() != "no"));
158+ QCOMPARE(dbusUnitySessionService.CanSuspend(), (login1face.call("CanSuspend").arguments().first().toString() != "no"));
159+ QCOMPARE(dbusUnitySessionService.CanReboot(), (login1face.call("CanReboot").arguments().first().toString() != "no"));
160+ QCOMPARE(dbusUnitySessionService.CanShutdown(), (login1face.call("CanPowerOff").arguments().first().toString() != "no"));
161+ QCOMPARE(dbusUnitySessionService.CanHybridSleep(), (login1face.call("CanHybridSleep").arguments().first().toString() != "no"));
162+ }
163+ }
164+
119private:
165private:
120 QDBusInterface *dbusUnitySession;
166 QDBusInterface *dbusUnitySession;
121};
167};
tests/qmltests/Components/tst_PhysicalKeysMapper.qml+5
@@ -89,6 +89,11 @@ TestCase {
89 compare(powerSpy.count, 1);
89 compare(powerSpy.count, 1);
90 }
90 }
91
91
92+ function test_screenshotPrtScr() {
93+ physicalKeysMapper.onKeyPressed({ key: Qt.Key_Print })
94+ tryCompare(screenshotSpy, "count", 1) // verify the screenshotTriggered signal has been called, exactly once
95+ }
96+
92 function test_screenshotButtons_data() {
97 function test_screenshotButtons_data() {
93 return [
98 return [
94 { tag: "UpFirst", first: Qt.Key_VolumeUp, second: Qt.Key_VolumeDown },
99 { tag: "UpFirst", first: Qt.Key_VolumeUp, second: Qt.Key_VolumeDown },
tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+146−6
@@ -34,10 +34,86 @@ Rectangle {
34 "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
34 "values": [ [ "Long Label 1", "Value 1"], [ "Label 2", "Long Value 2"], [ "Label 3", "Value 3"], [ "Label 4", "Value 4"], [ "Label 5", "Value 5"] ]
35 }
35 }
36
36
37+ property var actionsData: {
38+ "actions": [ {"label": "Some Label", "id": "someid"} ]
39+ }
40+
41+ property var audioData: {
42+ "tracks": [ { title: "Some track name", length: "30", source: "/not/existing/path/testsound1" } ]
43+ }
44+
45+ property var commentData: {
46+ "author": "Claire Thompson",
47+ "comment": "C.",
48+ "source": "../../graphics/avatars/amanda@12.png"
49+ }
50+
51+ property var commentInputData: {
52+ "submit-label": "TestSubmitLabel"
53+ }
54+
55+ property var galleryData: {
56+ "sources": [
57+ "../../graphics/phone_background.jpg",
58+ "../../graphics/tablet_background.jpg",
59+ "../../graphics/clock@18.png",
60+ "../../graphics/borked"
61+ ]
62+ }
63+
64+ property var headerData: {
65+ "title": "THE TITLE",
66+ "subtitle": "Something catchy"
67+ }
68+
69+ property var iconActionsData: {
70+ "actions": [ {"label": "10", "id": "s", "icon": Qt.resolvedUrl("../artwork/avatar@12.png") } ]
71+ }
72+
73+ property var imageData: {
74+ "source": "../../graphics/phone_background.jpg",
75+ "zoomable": false
76+ }
77+
78+ property var paymentsData: {
79+ "source": { "price" : 0.99, "currency": "USD", "store_item_id": "com.example.package" }
80+ }
81+
82+ property var progressData: {
83+ "type": "progress",
84+ "source": { "dbus-name" : "somename", "dbus-object": "somestring" }
85+ }
86+
87+ property var ratingInputData: {
88+ "visible": "both",
89+ "required": "both"
90+ }
91+
92+ property var ratingEditData: {
93+ "visible": "both",
94+ "required": "both",
95+ author: "Some dude",
96+ rating: 4.5,
97+ review: "Very cool app"
98+ }
99+
100+ property var reviewsData: {
101+ "reviews": [ { author: "Some dude", rating: 4.5, review: "Very cool app" } ]
102+ }
103+
104+ property var videoData: {
105+ "source": "",
106+ "screenshot": "../../../tests/qmltests/Components/tst_LazyImage/square.png"
107+ }
108+
37 ListModel {
109 ListModel {
38 id: widgetsModel
110 id: widgetsModel
39 }
111 }
40
112
113+ ListModel {
114+ id: allWidgetsModel
115+ }
116+
41 property var widgetData: {
117 property var widgetData: {
42 "title": "Title here",
118 "title": "Title here",
43 "collapsed-widgets": 2,
119 "collapsed-widgets": 2,
@@ -58,11 +134,35 @@ Rectangle {
58 "expanded": false
134 "expanded": false
59 }
135 }
60
136
137+ property var allWidgetsData: {
138+ "title": "Title here",
139+ "collapsed-widgets": 0,
140+ "widgets": allWidgetsModel
141+ }
142+
61 Component.onCompleted: {
143 Component.onCompleted: {
62 widgetsModel.append({"type": "text", "widgetId": "text1", "properties": { "text": longText }});
144 widgetsModel.append({"type": "text", "widgetId": "text1", "properties": { "text": longText }});
63− widgetsModel.append({"type": "text", "widgetId": "table1", "properties": { "text": tableData }});
145+ widgetsModel.append({"type": "table", "widgetId": "table1", "properties": tableData });
64 widgetsModel.append({"type": "text", "widgetId": "text3", "properties": { "text": shortText }});
146 widgetsModel.append({"type": "text", "widgetId": "text3", "properties": { "text": shortText }});
65− widgetsModel.append({"type": "text", "widgetId": "text4", "properties": { "text": longText }});
147+ widgetsModel.append({"type": "text", "widgetId": "text4", "properties": { "text": longText2 }});
148+
149+ allWidgetsModel.append({"type": "actions", "widgetId": "actions1", "properties": actionsData });
150+ allWidgetsModel.append({"type": "audio", "widgetId": "audio1", "properties": audioData });
151+ allWidgetsModel.append({"type": "comment", "widgetId": "comment1", "properties": commentData });
152+ allWidgetsModel.append({"type": "comment-input", "widgetId": "comment-input1", "properties": commentInputData });
153+ // "expandable" For now we're not testing inception of expandables
154+ allWidgetsModel.append({"type": "gallery", "widgetId": "gallery1", "properties": galleryData });
155+ allWidgetsModel.append({"type": "header", "widgetId": "header1", "properties": headerData });
156+ allWidgetsModel.append({"type": "icon-actions", "widgetId": "icon-actions1", "properties": iconActionsData } );
157+ allWidgetsModel.append({"type": "image", "widgetId": "image1", "properties": imageData });
158+ allWidgetsModel.append({"type": "payments", "widgetId": "payments1", "properties": paymentsData });
159+ allWidgetsModel.append({"type": "progress", "widgetId": "progress1", "properties": progressData });
160+ allWidgetsModel.append({"type": "rating-input", "widgetId": "rating-input1", "properties": ratingInputData });
161+ allWidgetsModel.append({"type": "rating-edit", "widgetId": "rating-edit1", "properties": ratingEditData });
162+ allWidgetsModel.append({"type": "reviews", "widgetId": "reviews1", "properties": reviewsData });
163+ allWidgetsModel.append({"type": "table", "widgetId": "table1", "properties": tableData });
164+ allWidgetsModel.append({"type": "text", "widgetId": "text1", "properties": { "text": longText }});
165+ allWidgetsModel.append({"type": "video", "widgetId": "video1", "properties": videoData });
66 }
166 }
67
167
68 PreviewWidgetFactory {
168 PreviewWidgetFactory {
@@ -72,6 +172,12 @@ Rectangle {
72 widgetData: root.widgetData
172 widgetData: root.widgetData
73 }
173 }
74
174
175+ PreviewWidgetFactory {
176+ id: previewWidgetFactory
177+ anchors { left: parent.left; right: parent.right; top: previewExpandable.bottom }
178+ opacity: 0
179+ }
180+
75 UT.UnityTestCase {
181 UT.UnityTestCase {
76 name: "PreviewExpandableTest"
182 name: "PreviewExpandableTest"
77 when: windowShown
183 when: windowShown
@@ -92,13 +198,10 @@ Rectangle {
92 }
198 }
93
199
94 function init() {
200 function init() {
201+ previewExpandable.widgetData = widgetData;
95 checkInitialState();
202 checkInitialState();
96 }
203 }
97
204
98− function test_collapsed_by_default() {
99− // Nothing init does this already
100− }
101−
102 function test_expand_collapse() {
205 function test_expand_collapse() {
103 var expandButton = findChild(previewExpandable, "expandButton")
206 var expandButton = findChild(previewExpandable, "expandButton")
104 mouseClick(expandButton);
207 mouseClick(expandButton);
@@ -151,5 +254,42 @@ Rectangle {
151 compare (repeater.itemAt(2).expanded, false);
254 compare (repeater.itemAt(2).expanded, false);
152 compare (repeater.itemAt(3).expanded, false);
255 compare (repeater.itemAt(3).expanded, false);
153 }
256 }
257+
258+ function test_all_widgets_height() {
259+ previewExpandable.widgetData = allWidgetsData;
260+
261+ var repeater = findChild(previewExpandable, "repeater")
262+ for (var i = 0; i < repeater.count; ++i) {
263+ tryCompare(repeater.itemAt(i), "height", 0);
264+ }
265+
266+ var expandButton = findChild(previewExpandable, "expandButton")
267+ mouseClick(expandButton);
268+
269+ var repeater = findChild(previewExpandable, "repeater")
270+ for (var i = 0; i < repeater.count; ++i) {
271+ previewWidgetFactory.active = false;
272+ wait(0); // spin the event loop otherwise we get warnings because the previous item from the
273+ // widget factory has still not been deleted and we change the widgetData
274+ previewWidgetFactory.widgetData = allWidgetsModel.get(i).properties;
275+ previewWidgetFactory.widgetType = allWidgetsModel.get(i).type;
276+ previewWidgetFactory.active = true;
277+
278+ // Wait for the height ot settle by waiting twice the time of the
279+ // longest of the height behaviour animations
280+ wait(UbuntuAnimation.SnapDuration * 2);
281+
282+ // Check the item inside the expandable has the same height
283+ // as the one straight from the factory
284+ verify(repeater.itemAt(i).height > 0);
285+ tryCompare(repeater.itemAt(i), "height", previewWidgetFactory.height);
286+ }
287+
288+ mouseClick(expandButton);
289+ compare(previewExpandable.expanded, false);
290+ for (var i = 0; i < repeater.count; ++i) {
291+ tryCompare(repeater.itemAt(i), "height", 0);
292+ }
293+ }
154 }
294 }
155}
295}
tests/qmltests/Dash/tst_PageHeader.qml+16−12
@@ -33,13 +33,18 @@ Item {
33
33
34 property alias searchEnabled : pageHeader.searchEntryEnabled
34 property alias searchEnabled : pageHeader.searchEntryEnabled
35 property alias searchQuery : pageHeader.searchQuery
35 property alias searchQuery : pageHeader.searchQuery
36+ property var headerContainer: findChild(pageHeader, "headerContainer")
36
37
37 function doTypeString(text) {
38 function doTypeString(text) {
38− var headerContainer = findChild(pageHeader, "headerContainer");
39 tryCompare(headerContainer, "contentY", 0);
39 tryCompare(headerContainer, "contentY", 0);
40 typeString(text);
40 typeString(text);
41 }
41 }
42
42
43+ function doResetSearch() {
44+ pageHeader.resetSearch();
45+ tryCompare(headerContainer, "contentY", headerContainer.height);
46+ }
47+
43 function init() {
48 function init() {
44 searchEnabled = true;
49 searchEnabled = true;
45
50
@@ -49,14 +54,13 @@ Item {
49 pageHeader.searchHistory.clear();
54 pageHeader.searchHistory.clear();
50
55
51 // Check initial state
56 // Check initial state
52− var headerContainer = findChild(pageHeader, "headerContainer");
53 tryCompareFunction(function() { return headerContainer.popover === null; }, true);
57 tryCompareFunction(function() { return headerContainer.popover === null; }, true);
54 compare(pageHeader.searchHistory.count, 0);
58 compare(pageHeader.searchHistory.count, 0);
55 }
59 }
56
60
57 function test_search_disabled() {
61 function test_search_disabled() {
58 searchEnabled = false
62 searchEnabled = false
59− pageHeader.resetSearch()
63+ doResetSearch();
60
64
61 pageHeader.triggerSearch()
65 pageHeader.triggerSearch()
62 keyClick(Qt.Key_S)
66 keyClick(Qt.Key_S)
@@ -66,7 +70,7 @@ Item {
66
70
67 function test_search_enable() {
71 function test_search_enable() {
68 searchEnabled = true
72 searchEnabled = true
69− pageHeader.resetSearch()
73+ doResetSearch();
70
74
71 pageHeader.triggerSearch()
75 pageHeader.triggerSearch()
72 doTypeString("test")
76 doTypeString("test")
@@ -84,42 +88,42 @@ Item {
84
88
85 function test_reset_search() {
89 function test_reset_search() {
86 searchEnabled = true
90 searchEnabled = true
87− pageHeader.resetSearch()
91+ doResetSearch();
88
92
89 pageHeader.triggerSearch()
93 pageHeader.triggerSearch()
90 keyClick(Qt.Key_S)
94 keyClick(Qt.Key_S)
91
95
92 compare(searchQuery, "s", "Could not type in TextField.")
96 compare(searchQuery, "s", "Could not type in TextField.")
93
97
94− pageHeader.resetSearch()
98+ doResetSearch();
95 compare(searchQuery, "", "Reset search did not reset searchQuery correctly.")
99 compare(searchQuery, "", "Reset search did not reset searchQuery correctly.")
96 }
100 }
97
101
98 function test_history() {
102 function test_history() {
99 pageHeader.triggerSearch()
103 pageHeader.triggerSearch()
100 doTypeString("humppa1")
104 doTypeString("humppa1")
101− pageHeader.resetSearch()
105+ doResetSearch();
102
106
103 tryCompare(pageHeader.searchHistory, "count", 1)
107 tryCompare(pageHeader.searchHistory, "count", 1)
104 compare(pageHeader.searchHistory.get(0).query, "humppa1")
108 compare(pageHeader.searchHistory.get(0).query, "humppa1")
105
109
106 pageHeader.triggerSearch()
110 pageHeader.triggerSearch()
107 doTypeString("humppa2")
111 doTypeString("humppa2")
108− pageHeader.resetSearch()
112+ doResetSearch();
109
113
110 compare(pageHeader.searchHistory.count, 2)
114 compare(pageHeader.searchHistory.count, 2)
111 compare(pageHeader.searchHistory.get(0).query, "humppa2")
115 compare(pageHeader.searchHistory.get(0).query, "humppa2")
112
116
113 pageHeader.triggerSearch()
117 pageHeader.triggerSearch()
114 doTypeString("humppa3")
118 doTypeString("humppa3")
115− pageHeader.resetSearch()
119+ doResetSearch();
116
120
117 compare(pageHeader.searchHistory.count, 3)
121 compare(pageHeader.searchHistory.count, 3)
118 compare(pageHeader.searchHistory.get(0).query, "humppa3")
122 compare(pageHeader.searchHistory.get(0).query, "humppa3")
119
123
120 pageHeader.triggerSearch()
124 pageHeader.triggerSearch()
121 doTypeString("humppa4")
125 doTypeString("humppa4")
122− pageHeader.resetSearch()
126+ doResetSearch();
123
127
124 compare(pageHeader.searchHistory.count, 3)
128 compare(pageHeader.searchHistory.count, 3)
125 compare(pageHeader.searchHistory.get(0).query, "humppa4")
129 compare(pageHeader.searchHistory.get(0).query, "humppa4")
@@ -139,7 +143,7 @@ Item {
139 }
143 }
140
144
141 function cleanup() {
145 function cleanup() {
142− pageHeader.resetSearch();
146+ doResetSearch();
143 }
147 }
144
148
145 function test_popover() {
149 function test_popover() {
@@ -223,7 +227,7 @@ Item {
223 tryCompare(headerContainer, "showSearch", !data.hideSearch);
227 tryCompare(headerContainer, "showSearch", !data.hideSearch);
224 tryCompareFunction(function() { return headerContainer.popover === null; }, true);
228 tryCompareFunction(function() { return headerContainer.popover === null; }, true);
225
229
226− pageHeader.resetSearch();
230+ doResetSearch();
227 }
231 }
228
232
229 function test_search_change_shows_search() {
233 function test_search_change_shows_search() {
tests/qmltests/Launcher/tst_Launcher.qml+246−12
@@ -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
@@ -321,6 +382,15 @@ Item {
321 }
382 }
322 }
383 }
323
384
385+ function test_runningHighlight() {
386+ dragLauncherIntoView();
387+ var launcherListView = findChild(launcher, "launcherListView");
388+ for (var i = 0; i < launcherListView.count; ++i) {
389+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
390+ compare(findChild(delegate, "runningHighlight").visible, LauncherModel.get(i).running)
391+ }
392+ }
393+
324 function test_focusedHighlight() {
394 function test_focusedHighlight() {
325 dragLauncherIntoView();
395 dragLauncherIntoView();
326 var launcherListView = findChild(launcher, "launcherListView");
396 var launcherListView = findChild(launcher, "launcherListView");
@@ -459,7 +529,14 @@ Item {
459 waitUntilLauncherDisappears();
529 waitUntilLauncherDisappears();
460 }
530 }
461
531
532+ function test_dragndrop_cancel_data() {
533+ return [
534+ {tag: "by mouse", mouse: true},
535+ {tag: "by touch", mouse: false}
536+ ]
537+ }
538+
462− function test_dragndrop_cancel() {
539+ function test_dragndrop_cancel(data) {
463 dragLauncherIntoView();
540 dragLauncherIntoView();
464 var draggedItem = findChild(launcher, "launcherDelegate4")
541 var draggedItem = findChild(launcher, "launcherDelegate4")
465 var item0 = findChild(launcher, "launcherDelegate0")
542 var item0 = findChild(launcher, "launcherDelegate0")
@@ -468,22 +545,37 @@ Item {
468 // Doing longpress
545 // Doing longpress
469 var currentMouseX = draggedItem.width / 2
546 var currentMouseX = draggedItem.width / 2
470 var currentMouseY = draggedItem.height / 2
547 var currentMouseY = draggedItem.height / 2
548+
549+ if(data.mouse) {
471− mousePress(draggedItem, currentMouseX, currentMouseY)
550+ mousePress(draggedItem, currentMouseX, currentMouseY)
551+ } else {
552+ touchPress(draggedItem, currentMouseX, currentMouseY)
553+ }
554+
472 // DraggedItem needs to hide and fakeDragItem become visible
555 // DraggedItem needs to hide and fakeDragItem become visible
473 tryCompare(draggedItem, "itemOpacity", 0)
556 tryCompare(draggedItem, "itemOpacity", 0)
474 tryCompare(fakeDragItem, "visible", true)
557 tryCompare(fakeDragItem, "visible", true)
475
558
476 // Dragging
559 // Dragging
477 currentMouseX -= units.gu(20)
560 currentMouseX -= units.gu(20)
561+
562+ if(data.mouse) {
478− mouseMove(draggedItem, currentMouseX, currentMouseY)
563+ mouseMove(draggedItem, currentMouseX, currentMouseY)
564+ } else {
565+ touchMove(draggedItem, currentMouseX, currentMouseY)
566+ }
479
567
480 // Make sure we're in the dragging state
568 // Make sure we're in the dragging state
481 var dndArea = findChild(launcher, "dndArea");
569 var dndArea = findChild(launcher, "dndArea");
482 tryCompare(draggedItem, "dragging", true)
570 tryCompare(draggedItem, "dragging", true)
483 tryCompare(dndArea, "draggedIndex", 4)
571 tryCompare(dndArea, "draggedIndex", 4)
484
572
485− // Tap somewhere in the middle of the screen to close/hide the launcher
573+ // Click/Tap somewhere in the middle of the screen to close/hide the launcher
574+ if(data.mouse) {
575+ mouseClick(root)
576+ } else {
486− tap(root)
577+ tap(root)
578+ }
487
579
488 // Make sure the dnd operation has been stopped
580 // Make sure the dnd operation has been stopped
489 tryCompare(draggedItem, "dragging", false)
581 tryCompare(draggedItem, "dragging", false)
@@ -538,7 +630,7 @@ Item {
538
630
539 // Doing longpress
631 // Doing longpress
540 mousePress(draggedItem);
632 mousePress(draggedItem);
541− tryCompare(quickListShape, "opacity", 0.96);
633+ tryCompare(quickListShape, "opacity", 0.8);
542 mouseRelease(draggedItem);
634 mouseRelease(draggedItem);
543
635
544 verify(quickList.y >= units.gu(1));
636 verify(quickList.y >= units.gu(1));
@@ -623,9 +715,31 @@ Item {
623 tryCompare(quickList, "state", "");
715 tryCompare(quickList, "state", "");
624 }
716 }
625
717
718+ function test_quickListMenuOnRMB() {
719+ dragLauncherIntoView();
720+ var clickedItem = findChild(launcher, "launcherDelegate5")
721+ var quickList = findChild(launcher, "quickList")
722+ var quickListShape = findChild(launcher, "quickListShape")
723+ var dndArea = findChild(launcher, "dndArea");
724+
725+ // Initial state
726+ tryCompare(quickListShape, "visible", false)
727+
728+ // Doing RMB click
729+ mouseClick(clickedItem, clickedItem.width / 2, clickedItem.height / 2, Qt.RightButton)
730+ tryCompare(quickListShape, "visible", true)
731+ verify(quickList, "state", "open")
732+ verify(dndArea, "dragging", false)
733+
734+ // Click somewhere in the empty space to dismiss the quicklist
735+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
736+ tryCompare(quickListShape, "visible", false);
737+ verify(quickList, "state", "")
738+ }
739+
626 function test_revealByHover() {
740 function test_revealByHover() {
627 var panel = findChild(launcher, "launcherPanel");
741 var panel = findChild(launcher, "launcherPanel");
628− verify(panel != undefined);
742+ verify(!!panel);
629
743
630 revealByHover();
744 revealByHover();
631 tryCompare(launcher, "state", "visibleTemporary");
745 tryCompare(launcher, "state", "visibleTemporary");
@@ -636,5 +750,125 @@ Item {
636 tryCompare(launcher, "state", "", 1000, "Launcher didn't hide after moving mouse away from it");
750 tryCompare(launcher, "state", "", 1000, "Launcher didn't hide after moving mouse away from it");
637 waitUntilLauncherDisappears();
751 waitUntilLauncherDisappears();
638 }
752 }
753+
754+ function test_progressChangeViaModel() {
755+ dragLauncherIntoView();
756+ var item = findChild(launcher, "launcherDelegate0")
757+ verify(item != undefined)
758+ LauncherModel.setProgress(LauncherModel.get(0).appId, -1)
759+ compare(findChild(item, "progressOverlay").visible, false)
760+ LauncherModel.setProgress(LauncherModel.get(0).appId, 20)
761+ compare(findChild(item, "progressOverlay").visible, true)
762+ LauncherModel.setProgress(LauncherModel.get(0).appId, 0)
763+ }
764+
765+ function test_alertPeekingIcon() {
766+ var listView = findChild(launcher, "launcherListView")
767+ verify(listView != undefined)
768+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
769+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
770+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
771+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
772+ }
773+
774+ function test_alertHidingIcon() {
775+ var listView = findChild(launcher, "launcherListView")
776+ verify(listView != undefined)
777+ var appIcon6 = findChild(launcher, "launcherDelegate6")
778+ verify(appIcon6 != undefined)
779+ LauncherModel.setAlerting(LauncherModel.get(6).appId, true)
780+ waitForWiggleToStart(appIcon6)
781+ LauncherModel.setAlerting(LauncherModel.get(6).appId, false)
782+ waitForWiggleToStop(appIcon6)
783+ tryCompare(appIcon6, "x", 0, 1000, "x-value of appId #6 should not be non-zero")
784+ waitForRendering(listView)
785+ }
786+
787+ function test_alertIgnoreFocusedApp() {
788+ LauncherModel.setAlerting(LauncherModel.get(0).appId, true)
789+ compare(LauncherModel.get(0).alerting, false, "Focused app should not have the alert-state set")
790+ }
791+
792+ function test_alertOnlyOnePeekingIcon() {
793+ var listView = findChild(launcher, "launcherListView")
794+ verify(listView != undefined)
795+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
796+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
797+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
798+ tryCompare(listView, "peekingIndex", 3, 1000, "Wrong appId set as peeking-index")
799+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
800+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
801+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
802+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
803+ waitForRendering(listView)
804+ }
805+
806+ function test_alertMultipleApps() {
807+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
808+ LauncherModel.setAlerting(LauncherModel.get(3).appId, true)
809+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
810+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
811+ compare(LauncherModel.get(1).alerting, true, "Alert-state of appId #1 should not be false")
812+ compare(LauncherModel.get(3).alerting, true, "Alert-state of appId #3 should not be false")
813+ compare(LauncherModel.get(5).alerting, true, "Alert-state of appId #5 should not be false")
814+ compare(LauncherModel.get(7).alerting, true, "Alert-state of appId #7 should not be false")
815+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
816+ LauncherModel.setAlerting(LauncherModel.get(3).appId, false)
817+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
818+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
819+ compare(LauncherModel.get(1).alerting, false, "Alert-state of appId #1 should not be true")
820+ compare(LauncherModel.get(3).alerting, false, "Alert-state of appId #1 should not be true")
821+ compare(LauncherModel.get(5).alerting, false, "Alert-state of appId #1 should not be true")
822+ compare(LauncherModel.get(7).alerting, false, "Alert-state of appId #1 should not be true")
823+ }
824+
825+ function test_alertMoveIconIntoView() {
826+ dragLauncherIntoView();
827+ var appIcon1 = findChild(launcher, "launcherDelegate1");
828+ var appIcon7 = findChild(launcher, "launcherDelegate7");
829+ LauncherModel.setAlerting(LauncherModel.get(1).appId, true)
830+ tryCompare(appIcon1, "angle", 0, 1000, "angle of appId #1 should not be non-zero")
831+ waitForWiggleToStart(appIcon1)
832+ LauncherModel.setAlerting(LauncherModel.get(7).appId, true)
833+ tryCompare(appIcon7, "angle", 0, 1000, "angle of appId #7 should not be non-zero")
834+ waitForWiggleToStart(appIcon7)
835+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
836+ waitForWiggleToStop(appIcon1)
837+ LauncherModel.setAlerting(LauncherModel.get(7).appId, false)
838+ waitForWiggleToStop(appIcon7)
839+ }
840+
841+ function test_alertWigglePeekDrag() {
842+ var appIcon5 = findChild(launcher, "launcherDelegate5");
843+ var listView = findChild(launcher, "launcherListView")
844+ verify(listView != undefined)
845+ LauncherModel.setAlerting(LauncherModel.get(5).appId, true)
846+ tryCompare(listView, "peekingIndex", 5, 1000, "Wrong appId set as peeking-index")
847+ waitForWiggleToStart(appIcon5)
848+ tryCompare(appIcon5, "wiggling", true, 1000, "appId #6 should not be still")
849+ dragLauncherIntoView();
850+ tryCompare(listView, "peekingIndex", -1, 1000, "peeking-index should be -1")
851+ LauncherModel.setAlerting(LauncherModel.get(5).appId, false)
852+ waitForWiggleToStop(appIcon5)
853+ tryCompare(appIcon5, "wiggling", false, 1000, "appId #1 should not be wiggling")
854+ }
855+
856+ function test_alertViaCountAndCountVisible() {
857+ dragLauncherIntoView();
858+ var appIcon1 = findChild(launcher, "launcherDelegate1")
859+ var oldCount = LauncherModel.get(1).count
860+ LauncherModel.setCount(LauncherModel.get(1).appId, 42)
861+ tryCompare(appIcon1, "wiggling", false, 1000, "appId #1 should be still")
862+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 1)
863+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
864+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
865+ waitForWiggleToStop(appIcon1)
866+ LauncherModel.setCount(LauncherModel.get(1).appId, 4711)
867+ tryCompare(appIcon1, "wiggling", true, 1000, "appId #1 should not be still")
868+ LauncherModel.setAlerting(LauncherModel.get(1).appId, false)
869+ waitForWiggleToStop(appIcon1)
870+ LauncherModel.setCountVisible(LauncherModel.get(1).appId, 0)
871+ LauncherModel.setCount(LauncherModel.get(1).appId, oldCount)
872+ }
639 }
873 }
640}
874}
tests/qmltests/Panel/tst_IndicatorItem.qml+12−12
@@ -181,25 +181,25 @@ Rectangle {
181 }
181 }
182
182
183 function test_select() {
183 function test_select() {
184− tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
184+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ffffff");
185 tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
185 tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
186− tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
186+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ffffff");
187− tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
187+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ffffff");
188− tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
188+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ffffff");
189
189
190 indicatorItem.expanded = true;
190 indicatorItem.expanded = true;
191− tryCompare(findChild(indicatorItem, "icon0"), "color", "#4c4c4c");
191+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#888888");
192 tryCompare(findChild(indicatorItem, "icon0"), "opacity", 0.6);
192 tryCompare(findChild(indicatorItem, "icon0"), "opacity", 0.6);
193− tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#4c4c4c");
193+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#888888");
194− tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#4c4c4c");
194+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#888888");
195− tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#4c4c4c");
195+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#888888");
196
196
197 indicatorItem.selected = true;
197 indicatorItem.selected = true;
198− tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
198+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ffffff");
199 tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
199 tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
200− tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
200+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ffffff");
201− tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
201+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ffffff");
202− tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
202+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ffffff");
203 }
203 }
204 }
204 }
205}
205}
tests/qmltests/Panel/tst_Panel.qml+18
@@ -19,6 +19,7 @@ import QtQuick.Layouts 1.1
19import QtTest 1.0
19import QtTest 1.0
20import Unity.Test 0.1
20import Unity.Test 0.1
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22+import Unity.Application 0.1
22import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
23import Ubuntu.Telephony 0.1 as Telephony
24import Ubuntu.Telephony 0.1 as Telephony
24import "../../../qml/Panel"
25import "../../../qml/Panel"
@@ -421,6 +422,23 @@ IndicatorTest {
421 verify(panel.indicators.fullyClosed);
422 verify(panel.indicators.fullyClosed);
422 }
423 }
423
424
425+ function test_tapToReturnCallDoesntExpandIndicators() {
426+ compare(panel.indicators.shown, false);
427+ verify(panel.indicators.fullyClosed);
428+
429+ callManager.foregroundCall = phoneCall;
430+
431+ ApplicationManager.focusApplication("unity8-dash");
432+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
433+
434+ mouseClick(panel.indicators,
435+ panel.indicators.width / 2,
436+ panel.indicators.minimizedPanelHeight / 2);
437+
438+ compare(panel.indicators.shown, false);
439+ verify(panel.indicators.fullyClosed);
440+ }
441+
424 function test_openAndClosePanelWithMouseClicks() {
442 function test_openAndClosePanelWithMouseClicks() {
425 compare(panel.indicators.shown, false);
443 compare(panel.indicators.shown, false);
426 verify(panel.indicators.fullyClosed);
444 verify(panel.indicators.fullyClosed);
tests/qmltests/Stages/ApplicationCheckBox.qml+21
@@ -86,8 +86,29 @@ RowLayout {
86 }
86 }
87 }
87 }
88 Label {
88 Label {
89+ id: appIdLabel
89 text: root.appId
90 text: root.appId
90 color: "white"
91 color: "white"
91 anchors.verticalCenter: parent.verticalCenter
92 anchors.verticalCenter: parent.verticalCenter
92 }
93 }
94+ Rectangle {
95+ color: {
96+ if (d.application) {
97+ if (d.application.state === ApplicationInfoInterface.Starting) {
98+ return "yellow";
99+ } else if (d.application.state === ApplicationInfoInterface.Running) {
100+ return "green";
101+ } else if (d.application.state === ApplicationInfoInterface.Suspended) {
102+ return "blue";
103+ } else {
104+ return "darkred";
105+ }
106+ } else {
107+ return "darkred";
108+ }
109+ }
110+ width: height
111+ height: appIdLabel.height * 0.7
112+ anchors.verticalCenter: parent.verticalCenter
113+ }
93}
114}
tests/qmltests/Stages/tst_DesktopStage.qml+2−2
@@ -24,9 +24,8 @@ import Utils 0.1
24
24
25import "../../../qml/Stages"
25import "../../../qml/Stages"
26
26
27−Rectangle {
27+Item {
28 id: root
28 id: root
29− color: "darkblue"
30 width: desktopStageLoader.width + controls.width
29 width: desktopStageLoader.width + controls.width
31 height: desktopStageLoader.height
30 height: desktopStageLoader.height
32
31
@@ -57,6 +56,7 @@ Rectangle {
57 property bool itemDestroyed: false
56 property bool itemDestroyed: false
58 sourceComponent: Component {
57 sourceComponent: Component {
59 DesktopStage {
58 DesktopStage {
59+ color: "darkblue"
60 anchors.fill: parent
60 anchors.fill: parent
61 Component.onDestruction: {
61 Component.onDestruction: {
62 desktopStageLoader.itemDestroyed = true;
62 desktopStageLoader.itemDestroyed = true;
tests/qmltests/Stages/tst_PhoneStage.qml+76−3
@@ -295,22 +295,23 @@ Item {
295 tryCompare(ApplicationManager, "count", oldCount - 1)
295 tryCompare(ApplicationManager, "count", oldCount - 1)
296 }
296 }
297 phoneStage.shellOrientationAngle = 0;
297 phoneStage.shellOrientationAngle = 0;
298+ phoneStage.select(ApplicationManager.get(0).appId);
298 }
299 }
299
300
300 function test_focusNewTopMostAppAfterFocusedOneClosesItself() {
301 function test_focusNewTopMostAppAfterFocusedOneClosesItself() {
301 addApps(2);
302 addApps(2);
302
303
303 var secondApp = ApplicationManager.get(0);
304 var secondApp = ApplicationManager.get(0);
304− tryCompare(secondApp, "state", ApplicationInfoInterface.Running);
305+ tryCompare(secondApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
305 tryCompare(secondApp, "focused", true);
306 tryCompare(secondApp, "focused", true);
306
307
307 var firstApp = ApplicationManager.get(1);
308 var firstApp = ApplicationManager.get(1);
308− tryCompare(firstApp, "state", ApplicationInfoInterface.Suspended);
309+ tryCompare(firstApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
309 tryCompare(firstApp, "focused", false);
310 tryCompare(firstApp, "focused", false);
310
311
311 ApplicationManager.stopApplication(secondApp.appId);
312 ApplicationManager.stopApplication(secondApp.appId);
312
313
313− tryCompare(firstApp, "state", ApplicationInfoInterface.Running);
314+ tryCompare(firstApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
314 tryCompare(firstApp, "focused", true);
315 tryCompare(firstApp, "focused", true);
315 }
316 }
316
317
@@ -388,5 +389,77 @@ Item {
388
389
389 tryCompare(focusedDelegate, "x", 0);
390 tryCompare(focusedDelegate, "x", 0);
390 }
391 }
392+
393+ function test_focusedAppIsTheOnlyRunningApp() {
394+ addApps(2);
395+
396+ var delegateA = findChild(phoneStage, "appDelegate0");
397+ verify(delegateA);
398+ var delegateB = findChild(phoneStage, "appDelegate1");
399+ verify(delegateB);
400+
401+ // A is focused and running, B is unfocused and suspended
402+ compare(delegateA.focus, true);
403+ compare(delegateA.application.requestedState, ApplicationInfoInterface.RequestedRunning);
404+ compare(delegateB.focus, false);
405+ compare(delegateB.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
406+
407+ // Switch foreground/focused appp from A to B
408+ goToSpread();
409+ phoneStage.select(delegateB.application.appId);
410+
411+ // Now it's the other way round
412+ // A is unfocused and suspended, B is focused and running
413+ tryCompare(delegateA, "focus", false);
414+ tryCompare(delegateA.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
415+ tryCompare(delegateB, "focus", true);
416+ tryCompare(delegateB.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
417+ }
418+
419+ function test_dashRemainsRunningIfStageIsToldSo() {
420+ addApps(1);
421+
422+ var delegateDash = findChild(phoneStage, "appDelegate1");
423+ verify(delegateDash);
424+ compare(delegateDash.application.appId, "unity8-dash");
425+
426+ var delegateOther = findChild(phoneStage, "appDelegate0");
427+ verify(delegateOther);
428+
429+ goToSpread();
430+ phoneStage.select("unity8-dash");
431+
432+ tryCompare(delegateDash, "focus", true);
433+ tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
434+ compare(delegateOther.focus, false);
435+ compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
436+
437+ goToSpread();
438+ phoneStage.select(delegateOther.application.appId);
439+
440+ // The other app gets focused and running but dash is kept running despite being unfocused
441+ tryCompare(delegateDash, "focus", false);
442+ tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
443+ compare(delegateOther.focus, true);
444+ compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedRunning);
445+ }
446+
447+ function test_foregroundAppIsSuspendedWhenStageIsSuspended() {
448+ addApps(1);
449+
450+ var delegate = findChild(phoneStage, "appDelegate0");
451+ verify(delegate);
452+
453+ compare(delegate.focus, true);
454+ compare(delegate.application.requestedState, ApplicationInfoInterface.RequestedRunning);
455+
456+ phoneStage.suspended = true;
457+
458+ tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
459+
460+ phoneStage.suspended = false;
461+
462+ tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
463+ }
391 }
464 }
392}
465}
tests/qmltests/Stages/tst_TabletStage.qml+161−1
@@ -76,10 +76,18 @@ Rectangle {
76 id: webbrowserCheckBox
76 id: webbrowserCheckBox
77 appId: "webbrowser-app"
77 appId: "webbrowser-app"
78 }
78 }
79+ ApplicationCheckBox {
80+ id: galleryCheckBox
81+ appId: "gallery-app"
82+ }
79 ApplicationCheckBox {
83 ApplicationCheckBox {
80 id: dialerCheckBox
84 id: dialerCheckBox
81 appId: "dialer-app"
85 appId: "dialer-app"
82 }
86 }
87+ ApplicationCheckBox {
88+ id: facebookCheckBox
89+ appId: "facebook-webapp"
90+ }
83 }
91 }
84 }
92 }
85
93
@@ -93,6 +101,19 @@ Rectangle {
93 function init() {
101 function init() {
94 tabletStageLoader.active = true;
102 tabletStageLoader.active = true;
95 tryCompare(tabletStageLoader, "status", Loader.Ready);
103 tryCompare(tabletStageLoader, "status", Loader.Ready);
104+
105+ // this is very strange, but sometimes the test starts without
106+ // TabletStage components having finished loading themselves
107+ var appWindow = null;
108+ while (!appWindow) {
109+ appWindow = findChild(tabletStage, "appWindow_unity8-dash");
110+ if (!appWindow) {
111+ console.log("didn't find appWindow_unity8-dash in " + tabletStage + ". Trying again...");
112+ wait(50);
113+ }
114+ }
115+
116+ waitUntilAppSurfaceShowsUp("unity8-dash");
96 }
117 }
97
118
98 function cleanup() {
119 function cleanup() {
@@ -109,7 +130,9 @@ Rectangle {
109
130
110 // kill all (fake) running apps
131 // kill all (fake) running apps
111 webbrowserCheckBox.checked = false;
132 webbrowserCheckBox.checked = false;
133+ galleryCheckBox.checked = false;
112 dialerCheckBox.checked = false;
134 dialerCheckBox.checked = false;
135+ facebookCheckBox.checked = false;
113 }
136 }
114
137
115 function waitUntilAppSurfaceShowsUp(appId) {
138 function waitUntilAppSurfaceShowsUp(appId) {
@@ -120,6 +143,34 @@ Rectangle {
120 tryCompare(appWindowStates, "state", "surface");
143 tryCompare(appWindowStates, "state", "surface");
121 }
144 }
122
145
146+ function switchToApp(targetAppId) {
147+ touchFlick(tabletStage,
148+ tabletStage.width - (tabletStage.dragAreaWidth / 2), tabletStage.height / 2,
149+ tabletStage.x + 1, tabletStage.height / 2);
150+
151+ var spreadView = findChild(tabletStage, "spreadView");
152+ verify(spreadView);
153+ tryCompare(spreadView, "phase", 2);
154+ tryCompare(spreadView, "flicking", false);
155+ tryCompare(spreadView, "moving", false);
156+
157+ waitUntilAppDelegateStopsMoving(targetAppId);
158+
159+ var targetAppWindow = findChild(tabletStage, "appWindow_" + targetAppId);
160+ tap(targetAppWindow, 10, 10);
161+ }
162+
163+ function waitUntilAppDelegateStopsMoving(targetAppId)
164+ {
165+ var targetAppDelegate = findChild(tabletStage, "tabletSpreadDelegate_" + targetAppId);
166+ verify(targetAppDelegate);
167+ var lastValue = undefined;
168+ do {
169+ lastValue = targetAppDelegate.animatedProgress;
170+ wait(30);
171+ } while (lastValue != targetAppDelegate.animatedProgress);
172+ }
173+
123 function test_tappingSwitchesFocusBetweenStages() {
174 function test_tappingSwitchesFocusBetweenStages() {
124 webbrowserCheckBox.checked = true;
175 webbrowserCheckBox.checked = true;
125 waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId);
176 waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId);
@@ -155,7 +206,6 @@ Rectangle {
155 tryCompare(webbrowserApp.session.surface, "activeFocus", false);
206 tryCompare(webbrowserApp.session.surface, "activeFocus", false);
156 }
207 }
157
208
158−
159 function test_closeAppInSideStage() {
209 function test_closeAppInSideStage() {
160 dialerCheckBox.checked = true;
210 dialerCheckBox.checked = true;
161 waitUntilAppSurfaceShowsUp(dialerCheckBox.appId);
211 waitUntilAppSurfaceShowsUp(dialerCheckBox.appId);
@@ -200,6 +250,116 @@ Rectangle {
200 appWindow.width / 2, appWindow.height / 2,
250 appWindow.width / 2, appWindow.height / 2,
201 appWindow.width / 2, -appWindow.height / 2);
251 appWindow.width / 2, -appWindow.height / 2);
202 }
252 }
253+
254+ function test_suspendsAndResumesAppsInMainStage() {
255+ webbrowserCheckBox.checked = true;
256+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
257+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
258+
259+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
260+
261+ galleryCheckBox.checked = true;
262+ var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
263+ compare(galleryApp.stage, ApplicationInfoInterface.MainStage);
264+
265+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
266+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
267+
268+ switchToApp(webbrowserApp.appId);
269+
270+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Suspended);
271+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
272+
273+ switchToApp(galleryApp.appId);
274+
275+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
276+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
277+ }
278+
279+
280+ function test_foregroundMainAndSideStageAppsAreKeptRunning() {
281+
282+ var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
283+ verify(stagesPriv);
284+
285+ // launch two main stage apps
286+ // gallery will be on foreground and webbrowser on background
287+
288+ webbrowserCheckBox.checked = true;
289+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId)
290+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
291+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
292+
293+ galleryCheckBox.checked = true;
294+ waitUntilAppSurfaceShowsUp(galleryCheckBox.appId)
295+ var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
296+ compare(galleryApp.stage, ApplicationInfoInterface.MainStage);
297+
298+ compare(stagesPriv.mainStageAppId, galleryCheckBox.appId);
299+
300+ // then launch two side stage apps
301+ // facebook will be on foreground and dialer on background
302+
303+ dialerCheckBox.checked = true;
304+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId)
305+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
306+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
307+
308+ facebookCheckBox.checked = true;
309+ waitUntilAppSurfaceShowsUp(facebookCheckBox.appId)
310+ var facebookApp = ApplicationManager.findApplication(facebookCheckBox.appId);
311+ compare(facebookApp.stage, ApplicationInfoInterface.SideStage);
312+
313+ compare(stagesPriv.sideStageAppId, facebookCheckBox.appId);
314+
315+ // Now check that the foreground apps are running and that the background ones
316+ // are suspended
317+
318+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
319+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
320+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Running);
321+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Suspended);
322+
323+ switchToApp(dialerCheckBox.appId);
324+
325+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
326+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
327+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Suspended);
328+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Running);
329+
330+ switchToApp(webbrowserCheckBox.appId);
331+
332+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Suspended);
333+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
334+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Suspended);
335+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Running);
336+ }
337+
338+ function test_foregroundAppsAreSuspendedWhenStageIsSuspended() {
339+ webbrowserCheckBox.checked = true;
340+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId)
341+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
342+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
343+
344+ dialerCheckBox.checked = true;
345+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId)
346+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
347+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
348+
349+
350+ compare(webbrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
351+ compare(dialerApp.requestedState, ApplicationInfoInterface.RequestedRunning);
352+
353+ tabletStage.suspended = true;
354+
355+ tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
356+ tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
357+
358+ tabletStage.suspended = false;
359+
360+ tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
361+ tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
362+ }
203 }
363 }
204
364
205}
365}
tests/qmltests/Tutorial/tst_Tutorial.qml+7−5
@@ -263,12 +263,14 @@ Item {
263 goToPage(null);
263 goToPage(null);
264 }
264 }
265
265
266− function test_walkthroughOnDesktop() {
266+ function test_skipOnDesktop() {
267+ var tutorial = findChild(shell, "tutorial");
268+ tryCompare(tutorial, "active", true);
269+ tryCompare(tutorial, "running", true);
270+
267 shell.usageScenario = "desktop";
271 shell.usageScenario = "desktop";
268− var page = goToPage("tutorialLeftFinish");
272+ tryCompare(tutorial, "active", false);
269− var tick = findChild(page, "tick");
273+ tryCompare(tutorial, "running", false);
270− tap(tick);
271− checkFinished();
272 }
274 }
273
275
274 function test_launcherShortDrag() {
276 function test_launcherShortDrag() {
tests/qmltests/tst_OrientedShell.qml+3−3
@@ -46,7 +46,7 @@ Rectangle {
46 }
46 }
47
47
48 QtObject {
48 QtObject {
49− id: mockUsageModeSettings
49+ id: mockUnity8Settings
50 property string usageMode: usageModeSelector.model[usageModeSelector.selectedIndex]
50 property string usageMode: usageModeSelector.model[usageModeSelector.selectedIndex]
51 }
51 }
52
52
@@ -125,7 +125,7 @@ Rectangle {
125 sourceComponent: Component {
125 sourceComponent: Component {
126 OrientedShell {
126 OrientedShell {
127 anchors.fill: parent
127 anchors.fill: parent
128− usageModeSettings: mockUsageModeSettings
128+ unity8Settings: mockUnity8Settings
129 oskSettings: mockOskSettings
129 oskSettings: mockOskSettings
130 physicalOrientation: root.physicalOrientation0
130 physicalOrientation: root.physicalOrientation0
131 orientationLocked: orientationLockedCheckBox.checked
131 orientationLocked: orientationLockedCheckBox.checked
@@ -926,7 +926,7 @@ Rectangle {
926
926
927 function test_attachRemoveInputDevices() {
927 function test_attachRemoveInputDevices() {
928 usageModeSelector.selectedIndex = 2;
928 usageModeSelector.selectedIndex = 2;
929− tryCompare(mockUsageModeSettings, "usageMode", "Automatic")
929+ tryCompare(mockUnity8Settings, "usageMode", "Automatic")
930
930
931 loadShell("mako")
931 loadShell("mako")
932 var shell = findChild(orientedShell, "shell");
932 var shell = findChild(orientedShell, "shell");
tests/qmltests/tst_Shell.qml+390−23
@@ -47,6 +47,7 @@ Rectangle {
47 }
47 }
48
48
49 Item {
49 Item {
50+ id: shellContainer
50 anchors.left: root.left
51 anchors.left: root.left
51 anchors.right: controls.left
52 anchors.right: controls.left
52 anchors.top: root.top
53 anchors.top: root.top
@@ -74,6 +75,18 @@ Rectangle {
74 width: units.gu(100)
75 width: units.gu(100)
75 height: units.gu(71)
76 height: units.gu(71)
76 }
77 }
78+ },
79+ State {
80+ name: "desktop"
81+ PropertyChanges {
82+ target: shellLoader
83+ width: shellContainer.width
84+ height: shellContainer.height
85+ }
86+ PropertyChanges {
87+ target: mouseEmulation
88+ checked: false
89+ }
77 }
90 }
78 ]
91 ]
79
92
@@ -81,6 +94,7 @@ Rectangle {
81 property bool itemDestroyed: false
94 property bool itemDestroyed: false
82 sourceComponent: Component {
95 sourceComponent: Component {
83 Shell {
96 Shell {
97+ id: __shell
84 usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
98 usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
85 orientation: Qt.PortraitOrientation
99 orientation: Qt.PortraitOrientation
86 primaryOrientation: Qt.PortraitOrientation
100 primaryOrientation: Qt.PortraitOrientation
@@ -88,6 +102,10 @@ Rectangle {
88 Component.onDestruction: {
102 Component.onDestruction: {
89 shellLoader.itemDestroyed = true;
103 shellLoader.itemDestroyed = true;
90 }
104 }
105+ Component.onCompleted: {
106+ var keyMapper = testCase.findChild(__shell, "physicalKeysMapper");
107+ keyMapper.controlInsteadOfAlt = true;
108+ }
91 }
109 }
92 }
110 }
93 }
111 }
@@ -112,16 +130,32 @@ Rectangle {
112 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
130 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
113 spacing: units.gu(1)
131 spacing: units.gu(1)
114
132
133+ Row {
134+ spacing: units.gu(1)
115− Button {
135+ Button {
116− text: "Show Greeter"
136+ text: "Show Greeter"
117− activeFocusOnPress: false
137+ activeFocusOnPress: false
118− onClicked: {
138+ onClicked: {
119− if (shellLoader.status !== Loader.Ready)
139+ if (shellLoader.status !== Loader.Ready)
120− return;
140+ return;
121−
141+
122− var greeter = testCase.findChild(shellLoader.item, "greeter");
142+ var greeter = testCase.findChild(shellLoader.item, "greeter");
123− if (!greeter.shown) {
143+ if (!greeter.shown) {
124− LightDM.Greeter.showGreeter();
144+ LightDM.Greeter.showGreeter();
145+ }
146+ }
147+ }
148+ Button {
149+ text: "Hide Greeter"
150+ activeFocusOnPress: false
151+ onClicked: {
152+ if (shellLoader.status !== Loader.Ready)
153+ return;
154+
155+ var greeter = testCase.findChild(shellLoader.item, "greeter");
156+ if (greeter.shown) {
157+ greeter.hide()
158+ }
125 }
159 }
126 }
160 }
127 }
161 }
@@ -141,7 +175,7 @@ Rectangle {
141 anchors { left: parent.left; right: parent.right }
175 anchors { left: parent.left; right: parent.right }
142 activeFocusOnPress: false
176 activeFocusOnPress: false
143 text: "Size"
177 text: "Size"
144− model: ["phone", "tablet"]
178+ model: ["phone", "tablet", "desktop"]
145 onSelectedIndexChanged: {
179 onSelectedIndexChanged: {
146 shellLoader.active = false;
180 shellLoader.active = false;
147 shellLoader.state = model[selectedIndex];
181 shellLoader.state = model[selectedIndex];
@@ -163,6 +197,17 @@ Rectangle {
163
197
164 Label { text: "Applications"; font.bold: true }
198 Label { text: "Applications"; font.bold: true }
165
199
200+ Button {
201+ text: "Start all apps"
202+ width: parent.width
203+ onClicked: {
204+ for (var i = 0; i < ApplicationManager.availableApplications.length; i++) {
205+ var appId = ApplicationManager.availableApplications[i];
206+ ApplicationManager.startApplication(appId)
207+ }
208+ }
209+ }
210+
166 Repeater {
211 Repeater {
167 id: appRepeater
212 id: appRepeater
168 model: ApplicationManager.availableApplications
213 model: ApplicationManager.availableApplications
@@ -226,6 +271,12 @@ Rectangle {
226 signalName: "actionInvoked"
271 signalName: "actionInvoked"
227 }
272 }
228
273
274+ SignalSpy {
275+ id: appRemovedSpy
276+ target: ApplicationManager
277+ signalName: "applicationRemoved"
278+ }
279+
229 Telephony.CallEntry {
280 Telephony.CallEntry {
230 id: phoneCall
281 id: phoneCall
231 phoneNumber: "+447812221111"
282 phoneNumber: "+447812221111"
@@ -248,9 +299,9 @@ Rectangle {
248 }
299 }
249
300
250 function cleanup() {
301 function cleanup() {
302+ mouseEmulation.checked = true;
251 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
303 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
252 tearDown();
304 tearDown();
253− GSettingsController.setUsageMode("Staged");
254 }
305 }
255
306
256 function loadShell(formFactor) {
307 function loadShell(formFactor) {
@@ -269,6 +320,20 @@ Rectangle {
269 waitForGreeterToStabilize();
320 waitForGreeterToStabilize();
270 }
321 }
271
322
323+ function loadDesktopShellWithApps() {
324+ loadShell("desktop");
325+ shell.usageScenario = "desktop"
326+ waitForRendering(root)
327+ var app1 = ApplicationManager.startApplication("dialer-app")
328+ var app2 = ApplicationManager.startApplication("webbrowser-app")
329+ var app3 = ApplicationManager.startApplication("camera-app")
330+ var app4 = ApplicationManager.startApplication("facebook-webapp")
331+ var app5 = ApplicationManager.startApplication("calendar-app")
332+ var app6 = ApplicationManager.startApplication("gallery-app")
333+ var app7 = ApplicationManager.startApplication("camera-app")
334+ waitUntilAppWindowIsFullyLoaded(app7);
335+ }
336+
272 function waitForGreeterToStabilize() {
337 function waitForGreeterToStabilize() {
273 var greeter = findChild(shell, "greeter");
338 var greeter = findChild(shell, "greeter");
274 verify(greeter);
339 verify(greeter);
@@ -472,6 +537,41 @@ Rectangle {
472 confirmLoggedIn(data.loggedIn)
537 confirmLoggedIn(data.loggedIn)
473 }
538 }
474
539
540+ function test_longLeftEdgeSwipeTakesToAppsAndResetSearchString() {
541+ loadShell("phone");
542+ swipeAwayGreeter();
543+ dragLauncherIntoView();
544+ dashCommunicatorSpy.clear();
545+
546+ tapOnAppIconInLauncher();
547+ waitUntilApplicationWindowIsFullyVisible();
548+
549+ verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
550+
551+ //Long left swipe
552+ swipeFromLeftEdge(units.gu(30));
553+
554+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
555+
556+ compare(dashCommunicatorSpy.count, 1);
557+ }
558+
559+ function test_ClickUbuntuIconInLauncherTakesToAppsAndResetSearchString() {
560+ loadShell("phone");
561+ swipeAwayGreeter();
562+ dragLauncherIntoView();
563+ dashCommunicatorSpy.clear();
564+
565+ var launcher = findChild(shell, "launcher");
566+ var dashIcon = findChild(launcher, "dashItem");
567+ verify(dashIcon != undefined);
568+ mouseClick(dashIcon);
569+
570+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
571+
572+ compare(dashCommunicatorSpy.count, 1);
573+ }
574+
475 function test_suspend() {
575 function test_suspend() {
476 loadShell("phone");
576 loadShell("phone");
477 swipeAwayGreeter();
577 swipeAwayGreeter();
@@ -486,9 +586,9 @@ Rectangle {
486 verify(mainAppId != "");
586 verify(mainAppId != "");
487 var mainApp = ApplicationManager.findApplication(mainAppId);
587 var mainApp = ApplicationManager.findApplication(mainAppId);
488 verify(mainApp);
588 verify(mainApp);
489− tryCompare(mainApp, "state", ApplicationInfoInterface.Running);
589+ tryCompare(mainApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
490
590
491− // Suspend while call is active...
591+ // Display off while call is active...
492 callManager.foregroundCall = phoneCall;
592 callManager.foregroundCall = phoneCall;
493 Powerd.status = Powerd.Off;
593 Powerd.status = Powerd.Off;
494 tryCompare(greeter, "shown", false);
594 tryCompare(greeter, "shown", false);
@@ -499,8 +599,7 @@ Rectangle {
499 Powerd.status = Powerd.Off;
599 Powerd.status = Powerd.Off;
500 tryCompare(greeter, "fullyShown", true);
600 tryCompare(greeter, "fullyShown", true);
501
601
502− tryCompare(ApplicationManager, "suspended", true);
503− compare(mainApp.state, ApplicationInfoInterface.Suspended);
602+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
504
603
505 // And wake up
604 // And wake up
506 Powerd.status = Powerd.On;
605 Powerd.status = Powerd.On;
@@ -516,8 +615,7 @@ Rectangle {
516 removeTimeConstraintsFromDirectionalDragAreas(greeter);
615 removeTimeConstraintsFromDirectionalDragAreas(greeter);
517 swipeAwayGreeter();
616 swipeAwayGreeter();
518
617
519− tryCompare(ApplicationManager, "suspended", false);
520− compare(mainApp.state, ApplicationInfoInterface.Running);
618+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedRunning);
521 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
619 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
522 }
620 }
523
621
@@ -688,15 +786,15 @@ Rectangle {
688
786
689 function test_launchedAppHasActiveFocus_data() {
787 function test_launchedAppHasActiveFocus_data() {
690 return [
788 return [
691− {tag: "phone", formFactor: "phone", usageMode: "Staged"},
789+ {tag: "phone", formFactor: "phone", usageScenario: "phone"},
692− {tag: "tablet", formFactor: "tablet", usageMode: "Staged"},
790+ {tag: "tablet", formFactor: "tablet", usageScenario: "tablet"},
693− {tag: "desktop", formFactor: "tablet", usageMode: "Windowed"}
791+ {tag: "desktop", formFactor: "tablet", usageScenario: "desktop"}
694 ]
792 ]
695 }
793 }
696
794
697 function test_launchedAppHasActiveFocus(data) {
795 function test_launchedAppHasActiveFocus(data) {
698− GSettingsController.setUsageMode(data.usageMode);
699 loadShell(data.formFactor);
796 loadShell(data.formFactor);
797+ shell.usageScenario = data.usageScenario;
700 swipeAwayGreeter();
798 swipeAwayGreeter();
701
799
702 var webApp = ApplicationManager.startApplication("webbrowser-app");
800 var webApp = ApplicationManager.startApplication("webbrowser-app");
@@ -724,7 +822,7 @@ Rectangle {
724
822
725 // check that the desktop stage and window have been loaded
823 // check that the desktop stage and window have been loaded
726 {
824 {
727− var desktopWindow = findChild(shell, "decoratedWindow_webbrowser-app");
825+ var desktopWindow = findChild(shell, "appWindow_webbrowser-app");
728 verify(desktopWindow);
826 verify(desktopWindow);
729 }
827 }
730
828
@@ -1082,6 +1180,8 @@ Rectangle {
1082 }
1180 }
1083
1181
1084 function test_tapUbuntuIconInLauncherOverAppSpread() {
1182 function test_tapUbuntuIconInLauncherOverAppSpread() {
1183+ launcherShowDashHomeSpy.clear();
1184+
1085 loadShell("phone");
1185 loadShell("phone");
1086 swipeAwayGreeter();
1186 swipeAwayGreeter();
1087
1187
@@ -1228,5 +1328,272 @@ Rectangle {
1228 var launcher = findChild(shell, "launcher");
1328 var launcher = findChild(shell, "launcher");
1229 compare(launcher.inverted, data.launcherInverted);
1329 compare(launcher.inverted, data.launcherInverted);
1230 }
1330 }
1331+
1332+ function test_unfocusedAppsGetSuspendedAfterEnteringStagedMode() {
1333+ loadShell("tablet");
1334+ shell.usageScenario = "desktop";
1335+
1336+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1337+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1338+
1339+ var galleryApp = ApplicationManager.startApplication("gallery-app");
1340+ waitUntilAppWindowIsFullyLoaded(galleryApp);
1341+
1342+ ApplicationManager.requestFocusApplication("unity8-dash");
1343+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1344+
1345+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1346+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1347+
1348+ shell.usageScenario = "tablet";
1349+
1350+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1351+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1352+ }
1353+
1354+ function test_unfocusedAppsAreResumedWhenEnteringWindowedMode() {
1355+ loadShell("tablet");
1356+ shell.usageScenario = "tablet";
1357+
1358+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1359+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1360+
1361+ var galleryApp = ApplicationManager.startApplication("gallery-app");
1362+ waitUntilAppWindowIsFullyLoaded(galleryApp);
1363+
1364+ ApplicationManager.requestFocusApplication("unity8-dash");
1365+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1366+
1367+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1368+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1369+
1370+ shell.usageScenario = "desktop";
1371+
1372+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1373+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1374+ }
1375+
1376+ function test_altTabSwitchesFocus() {
1377+ loadShell("desktop");
1378+ shell.usageScenario = "desktop"
1379+ waitForRendering(root)
1380+
1381+ var desktopStage = findChild(shell, "stage");
1382+ verify(desktopStage != null)
1383+
1384+ var app1 = ApplicationManager.startApplication("dialer-app")
1385+ waitUntilAppWindowIsFullyLoaded(app1);
1386+ var app2 = ApplicationManager.startApplication("webbrowser-app")
1387+ waitUntilAppWindowIsFullyLoaded(app2);
1388+ var app3 = ApplicationManager.startApplication("camera-app")
1389+ waitUntilAppWindowIsFullyLoaded(app3);
1390+
1391+ // Do a quick alt-tab and see if focus changes
1392+ tryCompare(app3.session.surface, "activeFocus", true)
1393+ keyClick(Qt.Key_Tab, Qt.ControlModifier)
1394+ tryCompare(app2.session.surface, "activeFocus", true)
1395+
1396+
1397+ tryCompare(desktopStage, "state", "")
1398+
1399+ // Just press Alt, make sure the spread comes up
1400+ keyPress(Qt.Key_Control);
1401+ keyClick(Qt.Key_Tab);
1402+ tryCompare(desktopStage, "state", "altTab")
1403+
1404+ // Release control, check if spread disappears
1405+ keyRelease(Qt.Key_Control)
1406+ tryCompare(desktopStage, "state", "")
1407+
1408+ // Focus should have switched back now
1409+ tryCompare(app3.session.surface, "activeFocus", true)
1410+ }
1411+
1412+ function test_altTabWrapAround() {
1413+ loadDesktopShellWithApps();
1414+
1415+ var desktopStage = findChild(shell, "stage");
1416+ verify(desktopStage !== null)
1417+
1418+ var appContainer = findInvisibleChild(shell, "appContainer")
1419+ verify(appContainer !== null)
1420+
1421+ var appRepeater = findInvisibleChild(shell, "appRepeater")
1422+ verify(appRepeater !== null)
1423+
1424+ // remember the focused appId
1425+ var focused = ApplicationManager.get(ApplicationManager.findApplication(ApplicationManager.focusedApplicationId));
1426+
1427+ tryCompare(desktopStage, "state", "")
1428+
1429+ // Just press Alt, make sure the spread comes up
1430+ keyPress(Qt.Key_Control);
1431+ keyClick(Qt.Key_Tab);
1432+ tryCompare(desktopStage, "state", "altTab")
1433+ tryCompare(appRepeater, "highlightedIndex", 1)
1434+ waitForRendering(shell)
1435+
1436+ // Now press and hold Tab, make sure the highlight moves all the way but stops at the last one
1437+ // We can't simulate a pressed key with keyPress() currently, so let's inject the events
1438+ // at API level. Jump for 10 times, verify that it's still at the last one and didn't wrap around.
1439+ for (var i = 0; i < 10; i++) {
1440+ appContainer.selectNext(true); // true == isAutoRepeat
1441+ wait(0); // Trigger the event loop to make sure all the things happen
1442+ }
1443+ tryCompare(appRepeater, "highlightedIndex", 6)
1444+
1445+ // Now release it once, and verify that it does wrap around with an additional Tab press
1446+ keyRelease(Qt.Key_Tab);
1447+ keyClick(Qt.Key_Tab);
1448+ tryCompare(appRepeater, "highlightedIndex", 0)
1449+
1450+ // Release control, check if spread disappears
1451+ keyRelease(Qt.Key_Control)
1452+ tryCompare(desktopStage, "state", "")
1453+
1454+ // Make sure that after wrapping around once, we have the same one focused as at the beginning
1455+ tryCompare(focused.session.surface, "activeFocus", true)
1456+ }
1457+
1458+ function test_altBackTabNavigation() {
1459+ loadDesktopShellWithApps();
1460+
1461+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1462+ verify(appRepeater !== null);
1463+
1464+ keyPress(Qt.Key_Control)
1465+ keyClick(Qt.Key_Tab);
1466+ tryCompare(appRepeater, "highlightedIndex", 1);
1467+
1468+ keyClick(Qt.Key_Tab);
1469+ tryCompare(appRepeater, "highlightedIndex", 2);
1470+
1471+ keyClick(Qt.Key_Tab);
1472+ tryCompare(appRepeater, "highlightedIndex", 3);
1473+
1474+ keyClick(Qt.Key_Tab);
1475+ tryCompare(appRepeater, "highlightedIndex", 4);
1476+
1477+ keyClick(Qt.Key_Backtab);
1478+ tryCompare(appRepeater, "highlightedIndex", 3);
1479+
1480+ keyClick(Qt.Key_Backtab);
1481+ tryCompare(appRepeater, "highlightedIndex", 2);
1482+
1483+ keyClick(Qt.Key_Backtab);
1484+ tryCompare(appRepeater, "highlightedIndex", 1);
1485+
1486+ keyRelease(Qt.Key_Control);
1487+ }
1488+
1489+ function test_highlightFollowsMouse() {
1490+ loadDesktopShellWithApps()
1491+
1492+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1493+ verify(appRepeater !== null);
1494+
1495+ keyPress(Qt.Key_Control)
1496+ keyClick(Qt.Key_Tab);
1497+
1498+ tryCompare(appRepeater, "highlightedIndex", 1);
1499+
1500+ var x = 0;
1501+ var y = shell.height * .75;
1502+ mouseMove(shell, x, y)
1503+
1504+ for (var i = 0; i < 7; i++) {
1505+ while (appRepeater.highlightedIndex != i && x <= 4000) {
1506+ x+=10;
1507+ mouseMove(shell, x, y)
1508+ wait(0); // spin the loop so bindings get evaluated
1509+ }
1510+ }
1511+
1512+ verify(y < 4000);
1513+
1514+ keyRelease(Qt.Key_Control);
1515+ }
1516+
1517+ function test_closeFromSpread() {
1518+ loadDesktopShellWithApps()
1519+
1520+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1521+ verify(appRepeater !== null);
1522+
1523+ keyPress(Qt.Key_Control)
1524+ keyClick(Qt.Key_Tab);
1525+
1526+ appRemovedSpy.clear();
1527+
1528+ var closedAppId = ApplicationManager.get(2).appId;
1529+ var appDelegate2 = appRepeater.itemAt(2);
1530+ var closeMouseArea = findChild(appDelegate2, "closeMouseArea");
1531+
1532+ // Move the mosue over tile 2 and verify the close button becomes visible
1533+ var x = 0;
1534+ var y = shell.height * .5;
1535+ mouseMove(shell, x, y)
1536+ while (appRepeater.highlightedIndex !== 2 && x <= 4000) {
1537+ x+=10;
1538+ mouseMove(shell, x, y)
1539+ wait(0); // spin the loop so bindings get evaluated
1540+ }
1541+ tryCompare(closeMouseArea, "enabled", true)
1542+
1543+ // Close the app using the close button
1544+ mouseClick(closeMouseArea, closeMouseArea.width / 2, closeMouseArea.height / 2)
1545+
1546+ // Verify applicationRemoved has been emitted correctly
1547+ tryCompare(appRemovedSpy, "count", 1)
1548+ compare(appRemovedSpy.signalArguments[0][0], closedAppId);
1549+
1550+ keyRelease(Qt.Key_Control);
1551+ }
1552+
1553+ function test_selectFromSpreadWithMouse_data() {
1554+ return [
1555+ {tag: "click on tileInfo", tileInfo: true },
1556+ {tag: "click on surface", tileInfo: false },
1557+ ]
1558+ }
1559+
1560+ function test_selectFromSpreadWithMouse(data) {
1561+ loadDesktopShellWithApps()
1562+
1563+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1564+ verify(appRepeater !== null);
1565+
1566+ keyPress(Qt.Key_Control)
1567+ keyClick(Qt.Key_Tab);
1568+
1569+ appRemovedSpy.clear();
1570+
1571+ var focusAppId = ApplicationManager.get(2).appId;
1572+ var appDelegate2 = appRepeater.itemAt(2);
1573+ var decoratedWindow = findChild(appDelegate2, "decoratedWindow");
1574+ var stage = findChild(shell, "stage");
1575+
1576+ tryCompare(stage, "state", "altTab");
1577+
1578+ // Move the mouse over tile 2 and verify the highlight becomes visible
1579+ var x = 0;
1580+ var y = shell.height * (data.tileInfo ? .5 : 0.95)
1581+ mouseMove(shell, x, y)
1582+ while (appRepeater.highlightedIndex !== 2 && x <= 4000) {
1583+ x+=10;
1584+ mouseMove(shell, x, y)
1585+ wait(0); // spin the loop so bindings get evaluated
1586+ }
1587+ tryCompare(decoratedWindow, "highlightShown", true);
1588+
1589+ // Click the tile
1590+ mouseClick(decoratedWindow, decoratedWindow.width / 2, decoratedWindow.height / 2)
1591+
1592+ // Verify that we left the spread and app2 is the focused one now
1593+ tryCompare(stage, "state", "");
1594+ tryCompare(ApplicationManager, "focusedApplicationId", focusAppId);
1595+
1596+ keyRelease(Qt.Key_Control);
1597+ }
1231 }
1598 }
1232}
1599}
tests/qmltests/tst_ShellWithPin.qml+5−4
@@ -498,12 +498,13 @@ Item {
498
498
499 function test_suspend() {
499 function test_suspend() {
500 var greeter = findChild(shell, "greeter");
500 var greeter = findChild(shell, "greeter");
501+ var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader")
501
502
502 // Put it to sleep
503 // Put it to sleep
503 Powerd.status = Powerd.Off;
504 Powerd.status = Powerd.Off;
504
505
505− // If locked, ApplicationManager.suspended should be true
506+ // If locked, applicationsDisplayLoader.item.suspended should be true
506− tryCompare(ApplicationManager, "suspended", true);
507+ tryCompare(applicationsDisplayLoader.item, "suspended", true);
507
508
508 // And wake up
509 // And wake up
509 Powerd.status = Powerd.On;
510 Powerd.status = Powerd.On;
@@ -513,12 +514,12 @@ Item {
513 swipeAwayGreeter(true);
514 swipeAwayGreeter(true);
514
515
515 // We have a lockscreen, make sure we're still suspended
516 // We have a lockscreen, make sure we're still suspended
516− tryCompare(ApplicationManager, "suspended", true);
517+ tryCompare(applicationsDisplayLoader.item, "suspended", true);
517
518
518 enterPin("1234")
519 enterPin("1234")
519
520
520 // Now that the lockscreen has gone too, make sure we're waking up
521 // Now that the lockscreen has gone too, make sure we're waking up
521− tryCompare(ApplicationManager, "suspended", false);
522+ tryCompare(applicationsDisplayLoader.item, "suspended", false);
522
523
523 }
524 }
524
525
tests/scripts/READMEnew file+9
@@ -0,0 +1,9 @@
1+This directory holds a few handy shell-scripts to simplify runtime-testing of DBus-APIs (requires qdbus-qt5 to be installed).
2+
3+launcher-icon related:
4+ * list-launcher-icons.sh - get all icons of unity8-launcher
5+ * alert-launcher-icon.sh - trigger alert/wiggle of unfocused launcher-icon
6+ * get-progress.sh - get progress-value of a launcher-icon
7+ * set-progress.sh - set progress-value of a launcher-icon
8+ * set-count.sh - set the count of a launcher-icon
9+ * set-count-visible.sh - set the count-visible flag of a launcher-icon
tests/scripts/alert-launcher-icon.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 com.canonical.Unity.Launcher.Item.Alert
tests/scripts/get-progress.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Get com.canonical.Unity.Launcher.Item progress
tests/scripts/list-launcher-icons.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity | grep Launcher | cut -f6 -d/
tests/scripts/set-count-visible.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item countVisible $2
tests/scripts/set-count.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item count $2
tests/scripts/set-progress.shnew file+3
@@ -0,0 +1,3 @@
1+#!/bin/sh
2+
3+qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item progress $2
tests/utils/modules/Unity/Test/UnityTestCase.qml+9
@@ -351,6 +351,15 @@ TestCase {
351 event.commit()
351 event.commit()
352 }
352 }
353
353
354+ function touchMove(item, tox, toy) {
355+ var root = fetchRootItem(item)
356+ var rootPoint = item.mapToItem(root, tox, toy)
357+
358+ var event = touchEvent(item);
359+ event.move(0 /* touchId */, rootPoint.x, rootPoint.y);
360+ event.commit();
361+ }
362+
354 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
363 function touchPinch(item, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End) {
355 // Make sure the item is rendered
364 // Make sure the item is rendered
356 waitForRendering(item);
365 waitForRendering(item);