on xenial help sign in
Merge from simple-lightdm-mock
commitcdf24a38c7299638a16b33e9f75971160120f8fb authorMichael Terry <mike@mterry.name> dateFri 6 Jan 2017, 09:42 (10 years ago) parent2d4064cce57554
171 files changed+3891−1965
CMakeLists.txt+1−1 debian/changelog+7 debian/control+3−3 plugins/LightDM/CMakeLists.txt−1 plugins/LightDM/Greeter.cpp−6 plugins/LightDM/Greeter.h−4 plugins/LightDM/SessionsModel.cpp−5 plugins/LightDM/SessionsModel.h−4 plugins/LightDM/UsersModel.cpp−7 plugins/LightDM/UsersModel.h+1−5 plugins/Utils/windowstatestorage.cpp+23 plugins/Utils/windowstatestorage.h+5 plugins/WindowManager/CMakeLists.txt+3−1 plugins/WindowManager/TopLevelSurfaceList.cpp−481 plugins/WindowManager/TopLevelSurfaceList.h−223 plugins/WindowManager/TopLevelWindowModel.cpp+668 plugins/WindowManager/TopLevelWindowModel.h+260 plugins/WindowManager/Window.cpp+237 plugins/WindowManager/Window.h+161 plugins/WindowManager/WindowManagerPlugin.cpp+5−2 po/aa.po+3−3 po/am.po+3−3 po/ar.po+3−3 po/ast.po+13−13 po/az.po+3−3 po/be.po+3−3 po/bg.po+3−3 po/bn.po+3−3 po/br.po+8−6 po/bs.po+3−3 po/ca.po+3−3 po/ca@valencia.po+3−3 po/ce.po+3−3 po/ckb.po+3−3 po/co.po+3−3 po/cs.po+3−3 po/cy.po+3−3 po/da.po+3−3 po/de.po+3−3 po/el.po+3−3 po/en_AU.po+3−3 po/en_GB.po+3−3 po/eo.po+3−3 po/es.po+3−3 po/et.po+932 po/eu.po+3−3 po/fa.po+3−3 po/fi.po+3−3 po/fo.po+3−3 po/fr.po+14−14 po/ga.po+3−3 po/gd.po+3−3 po/gl.po+13−13 po/gv.po+3−3 po/he.po+3−3 po/hi.po+3−3 po/hr.po+3−3 po/hu.po+3−3 po/hy.po+3−3 po/ia.po+3−3 po/id.po+3−3 po/is.po+3−3 po/it.po+3−3 po/ja.po+3−3 po/km.po+3−3 po/kn.po+3−3 po/ko.po+3−3 po/ku.po+3−3 po/ky.po+3−3 po/lo.po+3−3 po/lt.po+3−3 po/lv.po+3−3 po/ml.po+3−3 po/mr.po+3−3 po/ms.po+76−72 po/my.po+3−3 po/nb.po+3−3 po/ne.po+3−3 po/nl.po+3−3 po/nn.po+3−3 po/oc.po+3−3 po/or.po+3−3 po/pa.po+3−3 po/pl.po+3−3 po/pt.po+3−3 po/pt_BR.po+3−3 po/ro.po+3−3 po/ru.po+3−3 po/shn.po+3−3 po/si.po+3−3 po/sk.po+3−3 po/sl.po+3−3 po/sq.po+3−3 po/sr.po+3−3 po/sv.po+3−3 po/sw.po+3−3 po/ta.po+3−3 po/te.po+3−3 po/th.po+3−3 po/tr.po+3−3 po/ug.po+3−3 po/uk.po+3−3 po/unity8.pot+1−1 po/ur.po+3−3 po/uz.po+3−3 po/vi.po+3−3 po/wo.po+3−3 po/xh.po+3−3 po/zh_CN.po+3−3 po/zh_HK.po+3−3 po/zh_TW.po+3−3 qml/Components/InputMethod.qml+5−4 qml/Components/KeymapSwitcher.qml+4−1 qml/Shell.qml+14−6 qml/Stage/FakeMaximizeDelegate.qml+7−7 qml/Stage/MoveHandler.qml+1−1 qml/Stage/Stage.qml+127−145 qml/Stage/StagedFullscreenPolicy.qml+4−4 qml/Stage/TopLevelSurfaceRepeater.qml−67 qml/Stage/WindowControlsOverlay.qml+1−1 qml/Stage/WindowedFullscreenPolicy.qml+1−1 tests/mocks/CMakeLists.txt+1 tests/mocks/LightDMController/CMakeLists.txt+15 tests/mocks/LightDMController/plugin.cpp+34 tests/mocks/LightDMController/plugin.h+29 tests/mocks/LightDMController/qmldir+2 tests/mocks/Unity/Application/ApplicationInfo.cpp+48−24 tests/mocks/Unity/Application/ApplicationInfo.h+2−2 tests/mocks/Unity/Application/ApplicationManager.cpp+88−59 tests/mocks/Unity/Application/ApplicationManager.h+31−4 tests/mocks/Unity/Application/CMakeLists.txt+1−1 tests/mocks/Unity/Application/MirSurface.cpp+81−94 tests/mocks/Unity/Application/MirSurface.h+29−27 tests/mocks/Unity/Application/MirSurfaceItem.cpp+4−4 tests/mocks/Unity/Application/MirSurfaceItem.h+1−2 tests/mocks/Unity/Application/MirSurfaceListModel.cpp+11−33 tests/mocks/Unity/Application/MirSurfaceListModel.h+2−7 tests/mocks/Unity/Application/SurfaceManager.cpp+170−24 tests/mocks/Unity/Application/SurfaceManager.h+28−10 tests/mocks/Unity/Application/plugin.cpp+3−33 tests/mocks/Utils/windowstatestorage.cpp+23 tests/mocks/Utils/windowstatestorage.h+5 tests/mocks/liblightdm/MockController.cpp+8−9 tests/mocks/liblightdm/MockGreeter.cpp−5 tests/mocks/liblightdm/MockGreeter.h−4 tests/mocks/liblightdm/MockSessionsModel.cpp+1−5 tests/mocks/liblightdm/MockSessionsModel.h−4 tests/mocks/liblightdm/MockUsersModel.cpp−5 tests/mocks/liblightdm/MockUsersModel.h−4 tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt+1 tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp+3−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+6 tests/qmltests/Dash/tst_DashShell.qml+2−1 tests/qmltests/Greeter/tst_Greeter.qml+5−4 tests/qmltests/Greeter/tst_NarrowView.qml+2−1 tests/qmltests/Greeter/tst_WideView.qml+18−20 tests/qmltests/Panel/tst_ActiveCallHint.qml+16−2 tests/qmltests/Panel/tst_Panel.qml+8−1 tests/qmltests/Stage/ApplicationCheckBox.qml+1−1 tests/qmltests/Stage/tst_ApplicationWindow.qml+10−10 tests/qmltests/Stage/tst_DesktopStage.qml+53−80 tests/qmltests/Stage/tst_PhoneStage.qml+18−12 tests/qmltests/Stage/tst_SurfaceContainer.qml+4 tests/qmltests/Stage/tst_TabletStage.qml+16−10 tests/qmltests/Tutorial/tst_Tutorial.qml+32−9 tests/qmltests/tst_OrientedShell.qml+7−1 tests/qmltests/tst_Shell.qml+131−85 tests/qmltests/tst_ShellWithPin.qml+20−10 tests/utils/modules/Unity/Test/StageTestCase.qml+68 tests/utils/modules/Unity/Test/UnityTestCase.qml+4−5 tests/utils/modules/Unity/Test/qmldir+2−1
CMakeLists.txt+1−1
@@ -70,7 +70,7 @@ find_package(Qt5DBus 5.4 REQUIRED)
7070find_package(Qt5Concurrent 5.4 REQUIRED)
7171find_package(Qt5Sql 5.4 REQUIRED)
7272
73−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=22)
73+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=23)
7474pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
7575pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
7676pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
debian/changelog+7
@@ -1,3 +1,10 @@
1+unity8 (8.15+17.04.20161215-0ubuntu1) zesty; urgency=medium
2+
3+ * Let the model deal with some window management decisions (LP:
4+ #1346633)
5+
6+ -- Daniel d'Andrada <daniel.dandrada@canonical.com> Thu, 15 Dec 2016 16:17:29 +0000
7+
18unity8 (8.15+17.04.20161207.1-0ubuntu1) zesty; urgency=medium
29
310 [ Albert Astals Cid ]
debian/control+3−3
@@ -38,7 +38,7 @@ Build-Depends: cmake,
3838 libubuntugestures5-private-dev (>= 1.3.2030),
3939 libudev-dev,
4040 libudm-common-dev,
41− libunity-api-dev (>= 7.121),
41+ libunity-api-dev (>= 8.0),
4242 libusermetricsoutput1-dev,
4343# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
4444 libx11-dev[!arm64 !armhf],
@@ -162,7 +162,7 @@ Depends: qml-module-qtquick-layouts,
162162 qttranslations5-l10n,
163163 ubuntu-thumbnailer-impl-0,
164164 ubuntu-wallpapers,
165− unity-application-impl-22,
165+ unity-application-impl-23,
166166 unity-notifications-impl-3,
167167 unity-plugin-scopes | unity-scopes-impl,
168168 unity-scopes-impl-12,
@@ -208,7 +208,7 @@ Pre-Depends: ${misc:Pre-Depends},
208208Depends: ${misc:Depends},
209209 ${shlibs:Depends},
210210Provides: unity-application-impl,
211− unity-application-impl-22,
211+ unity-application-impl-23,
212212Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
213213Description: Fake environment for running Unity 8 shell
214214 Provides fake implementations of some QML modules used by Unity 8 shell
plugins/LightDM/CMakeLists.txt−1
@@ -7,7 +7,6 @@ include_directories(
77 ${CMAKE_CURRENT_SOURCE_DIR}
88 ${CMAKE_CURRENT_BINARY_DIR}
99 ${CMAKE_SOURCE_DIR}/plugins/Utils
10− ${CMAKE_BINARY_DIR}/tests/mocks/LightDM/IntegratedLightDM
1110 ${libunity8-private_SOURCE_DIR}
1211 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
1312)
plugins/LightDM/Greeter.cpp−6
@@ -177,9 +177,3 @@ void Greeter::authenticationCompleteFilter()
177177 Q_EMIT isAuthenticatedChanged();
178178 Q_EMIT authenticationComplete();
179179}
180−
181−QObject *Greeter::mock()
182−{
183− Q_D(Greeter);
184− return d->m_greeter->property("mock").value<QObject*>();
185−}
plugins/LightDM/Greeter.h−4
@@ -39,8 +39,6 @@ class Greeter : public QObject
3939 Q_PROPERTY(bool promptless READ promptless NOTIFY promptlessChanged)
4040 Q_PROPERTY(QString selectUser READ selectUser CONSTANT)
4141
42− Q_PROPERTY(QObject *mock READ mock CONSTANT) // for testing
43−
4442public:
4543 static Greeter *instance();
4644 virtual ~Greeter();
@@ -53,8 +51,6 @@ public:
5351 QString selectUser() const;
5452 bool hasGuestAccount() const;
5553
56− QObject *mock();
57−
5854public Q_SLOTS:
5955 void authenticate(const QString &username=QString());
6056 void respond(const QString &response);
plugins/LightDM/SessionsModel.cpp−5
@@ -111,11 +111,6 @@ QVariant SessionsModel::data(const QModelIndex& index, int role) const
111111 }
112112}
113113
114−QObject *SessionsModel::mock()
115−{
116− return m_model->property("mock").value<QObject*>();
117−}
118−
119114SessionsModel::SessionsModel(QObject* parent)
120115 : UnitySortFilterProxyModelQML(parent)
121116{
plugins/LightDM/SessionsModel.h−4
@@ -32,8 +32,6 @@ class SessionsModel : public UnitySortFilterProxyModelQML
3232 Q_PROPERTY(QList<QUrl> iconSearchDirectories READ iconSearchDirectories
3333 WRITE setIconSearchDirectories NOTIFY iconSearchDirectoriesChanged)
3434
35− Q_PROPERTY(QObject *mock READ mock CONSTANT) // for testing
36−
3735Q_SIGNALS:
3836 void iconSearchDirectoriesChanged();
3937
@@ -58,8 +56,6 @@ public:
5856
5957 void setIconSearchDirectories(const QList<QUrl> searchDirectories);
6058
61− QObject *mock();
62−
6359private:
6460 QLightDM::SessionsModel* m_model;
6561 QHash<int, QByteArray> m_roleNames;
plugins/LightDM/UsersModel.cpp−7
@@ -109,11 +109,4 @@ QVariant UsersModel::data(const QModelIndex &index, int role) const
109109 return QSortFilterProxyModel::data(index, role);
110110}
111111
112−QObject *UsersModel::mock()
113−{
114− // get through MangleModel down to QLightDM::UsersModel
115− MangleModel *mangleModel = static_cast<MangleModel*>(sourceModel());
116− return mangleModel->sourceModel()->property("mock").value<QObject*>();
117−}
118−
119112#include "UsersModel.moc"
plugins/LightDM/UsersModel.h+1−5
@@ -21,14 +21,12 @@
2121#pragma once
2222
2323#include <unitysortfilterproxymodelqml.h>
24−#include <QtCore/QObject>
24+#include <QObject>
2525
2626class UsersModel : public UnitySortFilterProxyModelQML
2727{
2828 Q_OBJECT
2929
30− Q_PROPERTY(QObject *mock READ mock CONSTANT) // for testing
31−
3230public:
3331 explicit UsersModel(QObject* parent=0);
3432
@@ -36,8 +34,6 @@ public:
3634 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
3735 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
3836
39− QObject *mock();
40−
4137private:
4238 int guestRow() const;
4339};
plugins/Utils/windowstatestorage.cpp+23
@@ -189,3 +189,26 @@ QSqlQuery WindowStateStorage::getValue(const QString &queryString) const
189189 }
190190 return query;
191191}
192+
193+Mir::State WindowStateStorage::toMirState(WindowState state) const
194+{
195+ // assumes a single state (not an OR of several)
196+ switch (state) {
197+ case WindowStateMaximized: return Mir::MaximizedState;
198+ case WindowStateMinimized: return Mir::MinimizedState;
199+ case WindowStateFullscreen: return Mir::FullscreenState;
200+ case WindowStateMaximizedLeft: return Mir::MaximizedLeftState;
201+ case WindowStateMaximizedRight: return Mir::MaximizedRightState;
202+ case WindowStateMaximizedHorizontally: return Mir::HorizMaximizedState;
203+ case WindowStateMaximizedVertically: return Mir::VertMaximizedState;
204+ case WindowStateMaximizedTopLeft: return Mir::MaximizedTopLeftState;
205+ case WindowStateMaximizedTopRight: return Mir::MaximizedTopRightState;
206+ case WindowStateMaximizedBottomLeft: return Mir::MaximizedBottomLeftState;
207+ case WindowStateMaximizedBottomRight: return Mir::MaximizedBottomRightState;
208+
209+ case WindowStateNormal:
210+ case WindowStateRestored:
211+ default:
212+ return Mir::RestoredState;
213+ }
214+}
plugins/Utils/windowstatestorage.h+5
@@ -19,6 +19,9 @@
1919#include <QMutex>
2020#include <QFuture>
2121
22+// unity-api
23+#include <unity/shell/application/Mir.h>
24+
2225class WindowStateStorage: public QObject
2326{
2427 Q_OBJECT
@@ -56,6 +59,8 @@ public:
5659 Q_INVOKABLE void saveStage(const QString &appId, int stage);
5760 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
5861
62+ Q_INVOKABLE Mir::State toMirState(WindowState state) const;
63+
5964private:
6065 void initdb();
6166
plugins/WindowManager/CMakeLists.txt+3−1
@@ -1,10 +1,12 @@
11set(WINDOWMANAGER_SRC
2− TopLevelSurfaceList.cpp
2+ TopLevelWindowModel.cpp
3+ Window.cpp
34 WindowManagerPlugin.cpp
45 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
56 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
67 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
78 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
9+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
810 )
911
1012add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
plugins/WindowManager/TopLevelSurfaceList.cppdeleted−481
@@ -1,481 +0,0 @@
1−/*
2− * Copyright (C) 2016 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 "TopLevelSurfaceList.h"
18−
19−// unity-api
20−#include <unity/shell/application/ApplicationInfoInterface.h>
21−#include <unity/shell/application/MirSurfaceInterface.h>
22−#include <unity/shell/application/MirSurfaceListInterface.h>
23−
24−#include <QMetaObject>
25−
26−Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist", QtDebugMsg)
27−
28−#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__
29−
30−using namespace unity::shell::application;
31−
32−TopLevelSurfaceList::TopLevelSurfaceList(QObject *parent) :
33− QAbstractListModel(parent)
34−{
35− DEBUG_MSG << "()";
36−}
37−
38−TopLevelSurfaceList::~TopLevelSurfaceList()
39−{
40− DEBUG_MSG << "()";
41−}
42−
43−int TopLevelSurfaceList::rowCount(const QModelIndex &parent) const
44−{
45− return !parent.isValid() ? m_surfaceList.size() : 0;
46−}
47−
48−QVariant TopLevelSurfaceList::data(const QModelIndex& index, int role) const
49−{
50− if (index.row() < 0 || index.row() >= m_surfaceList.size())
51− return QVariant();
52−
53− if (role == SurfaceRole) {
54− MirSurfaceInterface *surface = m_surfaceList.at(index.row()).surface;
55− return QVariant::fromValue(surface);
56− } else if (role == ApplicationRole) {
57− return QVariant::fromValue(m_surfaceList.at(index.row()).application);
58− } else if (role == IdRole) {
59− return QVariant::fromValue(m_surfaceList.at(index.row()).id);
60− } else {
61− return QVariant();
62− }
63−}
64−
65−void TopLevelSurfaceList::raise(MirSurfaceInterface *surface)
66−{
67− if (!surface)
68− return;
69−
70− DEBUG_MSG << "(MirSurface[" << (void*)surface << "])";
71−
72− int i = indexOf(surface);
73− if (i != -1) {
74− raiseId(m_surfaceList.at(i).id);
75− }
76−}
77−
78−void TopLevelSurfaceList::appendPlaceholder(ApplicationInfoInterface *application)
79−{
80− DEBUG_MSG << "(" << application->appId() << ")";
81−
82− appendSurfaceHelper(nullptr, application);
83−}
84−
85−void TopLevelSurfaceList::appendSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
86−{
87− Q_ASSERT(surface != nullptr);
88−
89− bool filledPlaceholder = false;
90− for (int i = 0; i < m_surfaceList.count() && !filledPlaceholder; ++i) {
91− ModelEntry &entry = m_surfaceList[i];
92− if (entry.application == application && entry.surface == nullptr) {
93− entry.surface = surface;
94− connectSurface(surface);
95− DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface
96− << ", filling out placeholder. after: " << toString();
97− Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
98− filledPlaceholder = true;
99− }
100− }
101−
102− if (!filledPlaceholder) {
103− DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
104− appendSurfaceHelper(surface, application);
105− }
106−}
107−
108−void TopLevelSurfaceList::appendSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
109−{
110− if (m_modelState == IdleState) {
111− m_modelState = InsertingState;
112− beginInsertRows(QModelIndex(), m_surfaceList.size() /*first*/, m_surfaceList.size() /*last*/);
113− } else {
114− Q_ASSERT(m_modelState == ResettingState);
115− // No point in signaling anything if we're resetting the whole model
116− }
117−
118− int id = generateId();
119− m_surfaceList.append(ModelEntry(surface, application, id));
120− if (surface) {
121− connectSurface(surface);
122− }
123−
124− if (m_modelState == InsertingState) {
125− endInsertRows();
126− Q_EMIT countChanged();
127− Q_EMIT listChanged();
128− m_modelState = IdleState;
129− }
130−
131− DEBUG_MSG << " after " << toString();
132−}
133−
134−void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
135−{
136− connect(surface, &MirSurfaceInterface::liveChanged, this, [this, surface](bool live){
137− if (!live) {
138− onSurfaceDied(surface);
139− }
140− });
141− connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); });
142−}
143−
144−void TopLevelSurfaceList::onSurfaceDied(MirSurfaceInterface *surface)
145−{
146− int i = indexOf(surface);
147− if (i == -1) {
148− return;
149− }
150−
151− auto application = m_surfaceList[i].application;
152−
153− // can't be starting if it already has a surface
154− Q_ASSERT(application->state() != ApplicationInfoInterface::Starting);
155−
156− if (application->state() == ApplicationInfoInterface::Running) {
157− m_surfaceList[i].removeOnceSurfaceDestroyed = true;
158− } else {
159− // assume it got killed by the out-of-memory daemon.
160− //
161− // So leave entry in the model and only remove its surface, so shell can display a screenshot
162− // in its place.
163− m_surfaceList[i].removeOnceSurfaceDestroyed = false;
164− }
165−}
166−
167−void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface)
168−{
169− int i = indexOf(surface);
170− if (i == -1) {
171− return;
172− }
173−
174− if (m_surfaceList[i].removeOnceSurfaceDestroyed) {
175− removeAt(i);
176− } else {
177− m_surfaceList[i].surface = nullptr;
178− Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
179− DEBUG_MSG << " Removed surface from entry. After: " << toString();
180− }
181−}
182−
183−void TopLevelSurfaceList::removeAt(int index)
184−{
185− if (m_modelState == IdleState) {
186− beginRemoveRows(QModelIndex(), index, index);
187− m_modelState = RemovingState;
188− } else {
189− Q_ASSERT(m_modelState == ResettingState);
190− // No point in signaling anything if we're resetting the whole model
191− }
192−
193− m_surfaceList.removeAt(index);
194−
195− if (m_modelState == RemovingState) {
196− endRemoveRows();
197− Q_EMIT countChanged();
198− Q_EMIT listChanged();
199− m_modelState = IdleState;
200− }
201−
202− DEBUG_MSG << " after " << toString();
203−}
204−
205−int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface)
206−{
207− for (int i = 0; i < m_surfaceList.count(); ++i) {
208− if (m_surfaceList.at(i).surface == surface) {
209− return i;
210− }
211− }
212− return -1;
213−}
214−
215−void TopLevelSurfaceList::move(int from, int to)
216−{
217− if (from == to) return;
218− DEBUG_MSG << " from=" << from << " to=" << to;
219−
220− if (from >= 0 && from < m_surfaceList.size() && to >= 0 && to < m_surfaceList.size()) {
221− QModelIndex parent;
222− /* When moving an item down, the destination index needs to be incremented
223− by one, as explained in the documentation:
224− http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
225−
226− Q_ASSERT(m_modelState == IdleState);
227− m_modelState = MovingState;
228−
229− beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
230− m_surfaceList.move(from, to);
231− endMoveRows();
232− Q_EMIT listChanged();
233−
234− m_modelState = IdleState;
235−
236− DEBUG_MSG << " after " << toString();
237− }
238−}
239−
240−MirSurfaceInterface *TopLevelSurfaceList::surfaceAt(int index) const
241−{
242− if (index >=0 && index < m_surfaceList.count()) {
243− return m_surfaceList[index].surface;
244− } else {
245− return nullptr;
246− }
247−}
248−
249−ApplicationInfoInterface *TopLevelSurfaceList::applicationAt(int index) const
250−{
251− if (index >=0 && index < m_surfaceList.count()) {
252− return m_surfaceList[index].application;
253− } else {
254− return nullptr;
255− }
256−}
257−
258−int TopLevelSurfaceList::idAt(int index) const
259−{
260− if (index >=0 && index < m_surfaceList.count()) {
261− return m_surfaceList[index].id;
262− } else {
263− return 0;
264− }
265−}
266−
267−int TopLevelSurfaceList::indexForId(int id) const
268−{
269− for (int i = 0; i < m_surfaceList.count(); ++i) {
270− if (m_surfaceList[i].id == id) {
271− return i;
272− }
273− }
274− return -1;
275−}
276−
277−void TopLevelSurfaceList::doRaiseId(int id)
278−{
279− int fromIndex = indexForId(id);
280− if (fromIndex != -1) {
281− move(fromIndex, 0 /* toIndex */);
282− }
283−}
284−
285−void TopLevelSurfaceList::raiseId(int id)
286−{
287− if (m_modelState == IdleState) {
288− DEBUG_MSG << "(id=" << id << ") - do it now.";
289− doRaiseId(id);
290− } else {
291− DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop.";
292− // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts
293− // if we perform yet another model change straight away.
294− //
295− // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says
296− // the index is definitely within bounds.
297− QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id));
298− }
299−}
300−
301−int TopLevelSurfaceList::generateId()
302−{
303− int id = m_nextId;
304− m_nextId = nextFreeId(m_nextId + 1);
305− Q_EMIT nextIdChanged();
306− return id;
307−}
308−
309−int TopLevelSurfaceList::nextFreeId(int candidateId)
310−{
311− if (candidateId > m_maxId) {
312− return nextFreeId(1);
313− } else {
314− if (indexForId(candidateId) == -1) {
315− // it's indeed free
316− return candidateId;
317− } else {
318− return nextFreeId(candidateId + 1);
319− }
320− }
321−}
322−
323−QString TopLevelSurfaceList::toString()
324−{
325− QString str;
326− for (int i = 0; i < m_surfaceList.count(); ++i) {
327− auto item = m_surfaceList.at(i);
328−
329− QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)")
330− .arg(i)
331− .arg(item.application->appId())
332− .arg((qintptr)item.surface, 0, 16)
333− .arg(item.id);
334−
335− if (i > 0) {
336− str.append(",");
337− }
338− str.append(itemStr);
339− }
340− return str;
341−}
342−
343−void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application)
344−{
345− DEBUG_MSG << "(" << application->appId() << ")";
346− Q_ASSERT(!m_applications.contains(application));
347− m_applications.append(application);
348−
349− MirSurfaceListInterface *surfaceList = application->surfaceList();
350−
351− if (application->state() != ApplicationInfoInterface::Stopped) {
352− if (surfaceList->count() == 0) {
353− appendPlaceholder(application);
354− } else {
355− for (int i = 0; i < surfaceList->count(); ++i) {
356− appendSurface(surfaceList->get(i), application);
357− }
358− }
359− }
360−
361− connect(surfaceList, &QAbstractItemModel::rowsInserted, this,
362− [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last)
363− {
364− for (int i = last; i >= first; --i) {
365− this->appendSurface(surfaceList->get(i), application);
366− }
367− });
368−}
369−
370−void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *application)
371−{
372− DEBUG_MSG << "(" << application->appId() << ")";
373− Q_ASSERT(m_applications.contains(application));
374−
375− MirSurfaceListInterface *surfaceList = application->surfaceList();
376−
377− disconnect(surfaceList, 0, this, 0);
378−
379− Q_ASSERT(m_modelState == IdleState);
380− m_modelState = RemovingState;
381−
382− int i = 0;
383− while (i < m_surfaceList.count()) {
384− if (m_surfaceList.at(i).application == application) {
385− beginRemoveRows(QModelIndex(), i, i);
386− m_surfaceList.removeAt(i);
387− endRemoveRows();
388− Q_EMIT countChanged();
389− Q_EMIT listChanged();
390− } else {
391− ++i;
392− }
393− }
394−
395− m_modelState = IdleState;
396−
397− DEBUG_MSG << " after " << toString();
398−
399− m_applications.removeAll(application);
400−}
401−
402−QAbstractListModel *TopLevelSurfaceList::applicationsModel() const
403−{
404− return m_applicationsModel;
405−}
406−
407−void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
408−{
409− if (m_applicationsModel == value) {
410− return;
411− }
412−
413− DEBUG_MSG << "(" << value << ")";
414−
415− Q_ASSERT(m_modelState == IdleState);
416− m_modelState = ResettingState;
417−
418− beginResetModel();
419−
420− if (m_applicationsModel) {
421− m_surfaceList.clear();
422− m_applications.clear();
423− disconnect(m_applicationsModel, 0, this, 0);
424− }
425−
426− m_applicationsModel = value;
427−
428− if (m_applicationsModel) {
429− findApplicationRole();
430−
431− connect(m_applicationsModel, &QAbstractItemModel::rowsInserted,
432− this, [this](const QModelIndex &/*parent*/, int first, int last) {
433− for (int i = first; i <= last; ++i) {
434− auto application = getApplicationFromModelAt(i);
435− addApplication(application);
436− }
437− });
438−
439− connect(m_applicationsModel, &QAbstractItemModel::rowsAboutToBeRemoved,
440− this, [this](const QModelIndex &/*parent*/, int first, int last) {
441− for (int i = first; i <= last; ++i) {
442− auto application = getApplicationFromModelAt(i);
443− removeApplication(application);
444− }
445− });
446−
447− for (int i = 0; i < m_applicationsModel->rowCount(); ++i) {
448− auto application = getApplicationFromModelAt(i);
449− addApplication(application);
450− }
451− }
452−
453− endResetModel();
454− m_modelState = IdleState;
455−}
456−
457−ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index)
458−{
459− QModelIndex modelIndex = m_applicationsModel->index(index);
460−
461− QVariant variant = m_applicationsModel->data(modelIndex, m_applicationRole);
462−
463− // variant.value<ApplicationInfoInterface*>() returns null for some reason.
464− return static_cast<ApplicationInfoInterface*>(variant.value<QObject*>());
465−}
466−
467−void TopLevelSurfaceList::findApplicationRole()
468−{
469− QHash<int, QByteArray> namesHash = m_applicationsModel->roleNames();
470−
471− m_applicationRole = -1;
472− for (auto i = namesHash.begin(); i != namesHash.end() && m_applicationRole == -1; ++i) {
473− if (i.value() == "application") {
474− m_applicationRole = i.key();
475− }
476− }
477−
478− if (m_applicationRole == -1) {
479− qFatal("TopLevelSurfaceList: applicationsModel must have a \"application\" role.");
480− }
481−}
plugins/WindowManager/TopLevelSurfaceList.hdeleted−223
@@ -1,223 +0,0 @@
1−/*
2− * Copyright (C) 2016 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−#ifndef TOPLEVELSURFACELIST_H
18−#define TOPLEVELSURFACELIST_H
19−
20−#include <QAbstractListModel>
21−#include <QList>
22−#include <QLoggingCategory>
23−
24−Q_DECLARE_LOGGING_CATEGORY(UNITY_TOPSURFACELIST)
25−
26−namespace unity {
27− namespace shell {
28− namespace application {
29− class ApplicationInfoInterface;
30− class MirSurfaceInterface;
31− }
32− }
33−}
34−
35−/**
36− * @brief A model of top-level surfaces
37− *
38− * It's an abstraction of top-level application windows.
39− *
40− * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is
41− * still starting up. A shell should then display a splash screen or saved screenshot of the application
42− * until its surface comes up.
43− *
44− * As applications can have multiple surfaces and you can also have entries without surfaces at all,
45− * the only way to unambiguously refer to an entry in this model is through its id.
46− */
47−class TopLevelSurfaceList : public QAbstractListModel
48−{
49−
50− Q_OBJECT
51−
52− /**
53− * @brief A list model of applications.
54− *
55− * It's expected to have a role called "application" which returns a ApplicationInfoInterface
56− */
57− Q_PROPERTY(QAbstractListModel* applicationsModel READ applicationsModel
58− WRITE setApplicationsModel
59− NOTIFY applicationsModelChanged)
60−
61− /**
62− * @brief Number of top-level surfaces in this model
63− *
64− * This is the same as rowCount, added in order to keep compatibility with QML ListModels.
65− */
66− Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
67−
68− /**
69− The id to be used on the next entry created
70− Useful for tests
71− */
72− Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged)
73−public:
74−
75− /**
76− * @brief The Roles supported by the model
77− *
78− * SurfaceRole - A MirSurfaceInterface. It will be null if the application is still starting up
79− * ApplicationRole - An ApplicationInfoInterface
80− * IdRole - A unique identifier for this entry. Useful to unambiguosly track elements as they move around in the list
81− */
82− enum Roles {
83− SurfaceRole = Qt::UserRole,
84− ApplicationRole = Qt::UserRole + 1,
85− IdRole = Qt::UserRole + 2,
86− };
87−
88− explicit TopLevelSurfaceList(QObject *parent = nullptr);
89− virtual ~TopLevelSurfaceList();
90−
91− // QAbstractItemModel methods
92− int rowCount(const QModelIndex &parent = QModelIndex()) const override;
93− QVariant data(const QModelIndex& index, int role) const override;
94− QHash<int, QByteArray> roleNames() const override {
95− QHash<int, QByteArray> roleNames { {SurfaceRole, "surface"},
96− {ApplicationRole, "application"},
97− {IdRole, "id"} };
98− return roleNames;
99− }
100−
101− int nextId() const { return m_nextId; }
102−
103− QAbstractListModel *applicationsModel() const;
104− void setApplicationsModel(QAbstractListModel*);
105−
106−public Q_SLOTS:
107− /**
108− * @brief Returns the surface at the given index
109− *
110− * It will be a nullptr if the application is still starting up and thus hasn't yet created
111− * and drawn into a surface.
112− */
113− unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const;
114−
115− /**
116− * @brief Returns the application at the given index
117− */
118− unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const;
119−
120− /**
121− * @brief Returns the unique id of the element at the given index
122− */
123− int idAt(int index) const;
124−
125− /**
126− * @brief Returns the index where the row with the given id is located
127− *
128− * Returns -1 if there's no row with the given id.
129− */
130− int indexForId(int id) const;
131−
132− /**
133− * @brief Raises the row with the given id to index 0
134− */
135− void raiseId(int id);
136−
137− void doRaiseId(int id);
138−
139−Q_SIGNALS:
140− void countChanged();
141−
142− /**
143− * @brief Emitted when the list changes
144− *
145− * Emitted when model gains an element, loses an element or when elements exchange positions.
146− */
147− void listChanged();
148−
149− void nextIdChanged();
150−
151− void applicationsModelChanged();
152−
153−private:
154− void addApplication(unity::shell::application::ApplicationInfoInterface *application);
155− void removeApplication(unity::shell::application::ApplicationInfoInterface *application);
156−
157− int indexOf(unity::shell::application::MirSurfaceInterface *surface);
158− void raise(unity::shell::application::MirSurfaceInterface *surface);
159− void move(int from, int to);
160− void appendSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
161− unity::shell::application::ApplicationInfoInterface *application);
162− void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
163− int generateId();
164− int nextFreeId(int candidateId);
165− QString toString();
166− void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface);
167− void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface);
168− void removeAt(int index);
169− void findApplicationRole();
170−
171− unity::shell::application::ApplicationInfoInterface *getApplicationFromModelAt(int index);
172−
173− /*
174− Placeholder for a future surface from a starting or running application.
175− Enables shell to give immediate feedback to the user by showing, eg,
176− a splash screen.
177−
178− It's a model row containing a null surface and the given application.
179− */
180− void appendPlaceholder(unity::shell::application::ApplicationInfoInterface *application);
181−
182− /*
183− Adds a model row with the given surface and application
184−
185− Alternatively, if a placeholder exists for the given application it's
186− filled with the given surface instead.
187− */
188− void appendSurface(unity::shell::application::MirSurfaceInterface *surface,
189− unity::shell::application::ApplicationInfoInterface *application);
190−
191− struct ModelEntry {
192− ModelEntry(unity::shell::application::MirSurfaceInterface *surface, unity::shell::application::ApplicationInfoInterface *application, int id)
193− : surface(surface), application(application), id(id) {}
194− unity::shell::application::MirSurfaceInterface *surface;
195− unity::shell::application::ApplicationInfoInterface *application;
196− int id;
197− bool removeOnceSurfaceDestroyed{false};
198− };
199−
200− QList<ModelEntry> m_surfaceList;
201− int m_nextId{1};
202− static const int m_maxId{1000000};
203−
204− // applications that are being monitored
205− QList<unity::shell::application::ApplicationInfoInterface *> m_applications;
206−
207− QAbstractListModel* m_applicationsModel{nullptr};
208− int m_applicationRole{-1};
209−
210− enum ModelState {
211− IdleState,
212− InsertingState,
213− RemovingState,
214− MovingState,
215− ResettingState
216− };
217− ModelState m_modelState{IdleState};
218−};
219−
220−Q_DECLARE_METATYPE(TopLevelSurfaceList*)
221−Q_DECLARE_METATYPE(QAbstractListModel*)
222−
223−#endif // TOPLEVELSURFACELIST_H
plugins/WindowManager/TopLevelWindowModel.cppnew file+668
@@ -0,0 +1,668 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "TopLevelWindowModel.h"
18+
19+// unity-api
20+#include <unity/shell/application/ApplicationInfoInterface.h>
21+#include <unity/shell/application/ApplicationManagerInterface.h>
22+#include <unity/shell/application/MirSurfaceInterface.h>
23+#include <unity/shell/application/MirSurfaceListInterface.h>
24+#include <unity/shell/application/SurfaceManagerInterface.h>
25+
26+// Qt
27+#include <QGuiApplication>
28+#include <QDebug>
29+
30+// local
31+#include "Window.h"
32+
33+Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg)
34+
35+#define DEBUG_MSG qCDebug(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__
36+#define INFO_MSG qCInfo(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__
37+
38+namespace unityapi = unity::shell::application;
39+
40+TopLevelWindowModel::TopLevelWindowModel()
41+{
42+}
43+
44+void TopLevelWindowModel::setApplicationManager(unityapi::ApplicationManagerInterface* value)
45+{
46+ if (m_applicationManager == value) {
47+ return;
48+ }
49+
50+ DEBUG_MSG << "(" << value << ")";
51+
52+ Q_ASSERT(m_modelState == IdleState);
53+ m_modelState = ResettingState;
54+
55+ beginResetModel();
56+
57+ if (m_applicationManager) {
58+ m_windowModel.clear();
59+ disconnect(m_applicationManager, 0, this, 0);
60+ }
61+
62+ m_applicationManager = value;
63+
64+ if (m_applicationManager) {
65+ connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
66+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
67+ for (int i = first; i <= last; ++i) {
68+ auto application = m_applicationManager->get(i);
69+ addApplication(application);
70+ }
71+ });
72+
73+ connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
74+ this, [this](const QModelIndex &/*parent*/, int first, int last) {
75+ for (int i = first; i <= last; ++i) {
76+ auto application = m_applicationManager->get(i);
77+ removeApplication(application);
78+ }
79+ });
80+
81+ for (int i = 0; i < m_applicationManager->rowCount(); ++i) {
82+ auto application = m_applicationManager->get(i);
83+ addApplication(application);
84+ }
85+ }
86+
87+ endResetModel();
88+ m_modelState = IdleState;
89+}
90+
91+void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *surfaceManager)
92+{
93+ if (surfaceManager == m_surfaceManager) {
94+ return;
95+ }
96+
97+ DEBUG_MSG << "(" << surfaceManager << ")";
98+
99+ if (m_surfaceManager) {
100+ disconnect(m_surfaceManager, 0, this, 0);
101+ }
102+
103+ m_surfaceManager = surfaceManager;
104+
105+ if (m_surfaceManager) {
106+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfaceCreated, this, &TopLevelWindowModel::onSurfaceCreated);
107+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised);
108+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted);
109+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded);
110+ }
111+
112+ Q_EMIT surfaceManagerChanged(m_surfaceManager);
113+}
114+
115+void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application)
116+{
117+ DEBUG_MSG << "(" << application->appId() << ")";
118+
119+ if (application->state() != unityapi::ApplicationInfoInterface::Stopped && application->surfaceList()->count() == 0) {
120+ prependPlaceholder(application);
121+ }
122+}
123+
124+void TopLevelWindowModel::removeApplication(unityapi::ApplicationInfoInterface *application)
125+{
126+ DEBUG_MSG << "(" << application->appId() << ")";
127+
128+ Q_ASSERT(m_modelState == IdleState);
129+
130+ int i = 0;
131+ while (i < m_windowModel.count()) {
132+ if (m_windowModel.at(i).application == application) {
133+ removeAt(i);
134+ } else {
135+ ++i;
136+ }
137+ }
138+}
139+
140+void TopLevelWindowModel::prependPlaceholder(unityapi::ApplicationInfoInterface *application)
141+{
142+ INFO_MSG << "(" << application->appId() << ")";
143+
144+ prependSurfaceHelper(nullptr, application);
145+}
146+
147+void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application)
148+{
149+ Q_ASSERT(surface != nullptr);
150+
151+ bool filledPlaceholder = false;
152+ for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) {
153+ ModelEntry &entry = m_windowModel[i];
154+ if (entry.application == application && entry.window->surface() == nullptr) {
155+ entry.window->setSurface(surface);
156+ connectSurface(surface);
157+ INFO_MSG << " appId=" << application->appId() << " surface=" << surface
158+ << ", filling out placeholder. after: " << toString();
159+ filledPlaceholder = true;
160+ }
161+ }
162+
163+ if (!filledPlaceholder) {
164+ INFO_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
165+ prependSurfaceHelper(surface, application);
166+ }
167+}
168+
169+void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application)
170+{
171+ if (m_modelState == IdleState) {
172+ m_modelState = InsertingState;
173+ beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
174+ } else {
175+ Q_ASSERT(m_modelState == ResettingState);
176+ // No point in signaling anything if we're resetting the whole model
177+ }
178+
179+ int id = generateId();
180+ Window *window = new Window(id, this);
181+ if (surface) {
182+ window->setSurface(surface);
183+ }
184+ m_windowModel.prepend(ModelEntry(window, application));
185+ if (surface) {
186+ connectSurface(surface);
187+ }
188+
189+ connectWindow(window);
190+
191+ if (m_modelState == InsertingState) {
192+ endInsertRows();
193+ Q_EMIT countChanged();
194+ Q_EMIT listChanged();
195+ m_modelState = IdleState;
196+ }
197+
198+ if (!surface) {
199+ activateEmptyWindow(window);
200+ }
201+
202+ INFO_MSG << " after " << toString();
203+}
204+
205+void TopLevelWindowModel::connectWindow(Window *window)
206+{
207+ connect(window, &Window::focusRequested, this, [this, window]() {
208+ if (!window->surface()) {
209+ activateEmptyWindow(window);
210+ }
211+ });
212+
213+ connect(window, &Window::focusedChanged, this, [this, window](bool focused) {
214+ if (window->surface()) {
215+ // Condense changes to the focused window
216+ // eg: Do focusedWindow=A to focusedWindow=B instead of
217+ // focusedWindow=A to focusedWindow=null to focusedWindow=B
218+ if (focused) {
219+ Q_ASSERT(m_newlyFocusedWindow == nullptr);
220+ m_focusedWindowChanged = true;
221+ m_newlyFocusedWindow = window;
222+ } else if (m_focusedWindow == window) {
223+ m_focusedWindowChanged = true;
224+ } else {
225+ // don't clear the focused window if you were not there in the first place
226+ // happens when a filled window gets replaced with an empty one (no surface) as the focused window.
227+ }
228+ }
229+ });
230+
231+ connect(window, &Window::closeRequested, this, [this, window]() {
232+ if (!window->surface()) {
233+ // do things ourselves as miral doesn't know about this window
234+ int id = window->id();
235+ int index = indexForId(id);
236+ bool focusOther = false;
237+ Q_ASSERT(index >= 0);
238+ if (window->focused()) {
239+ focusOther = true;
240+ }
241+ m_windowModel[index].application->close();
242+ if (focusOther) {
243+ activateTopMostWindowWithoutId(id);
244+ }
245+ }
246+ });
247+
248+ connect(window, &Window::emptyWindowActivated, this, [this, window]() {
249+ activateEmptyWindow(window);
250+ });
251+}
252+
253+void TopLevelWindowModel::activateEmptyWindow(Window *window)
254+{
255+ Q_ASSERT(!window->surface());
256+ DEBUG_MSG << "(" << window << ")";
257+
258+ // miral doesn't know about empty windows (ie, windows that are not backed up by MirSurfaces)
259+ // So we have to activate them ourselves (instead of asking SurfaceManager to do it for us).
260+
261+ window->setFocused(true);
262+ raiseId(window->id());
263+ Window *previousWindow = m_focusedWindow;
264+ setFocusedWindow(window);
265+ if (previousWindow && previousWindow->surface() && previousWindow->surface()->focused()) {
266+ m_surfaceManager->activate(nullptr);
267+ }
268+}
269+
270+void TopLevelWindowModel::connectSurface(unityapi::MirSurfaceInterface *surface)
271+{
272+ connect(surface, &unityapi::MirSurfaceInterface::liveChanged, this, [this, surface](bool live){
273+ if (!live) {
274+ onSurfaceDied(surface);
275+ }
276+ });
277+ connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); });
278+}
279+
280+void TopLevelWindowModel::onSurfaceDied(unityapi::MirSurfaceInterface *surface)
281+{
282+ if (surface->type() == Mir::InputMethodType) {
283+ removeInputMethodWindow();
284+ return;
285+ }
286+
287+ int i = indexOf(surface);
288+ if (i == -1) {
289+ return;
290+ }
291+
292+ auto application = m_windowModel[i].application;
293+
294+ // can't be starting if it already has a surface
295+ Q_ASSERT(application->state() != unityapi::ApplicationInfoInterface::Starting);
296+
297+ if (application->state() == unityapi::ApplicationInfoInterface::Running) {
298+ m_windowModel[i].removeOnceSurfaceDestroyed = true;
299+ } else {
300+ // assume it got killed by the out-of-memory daemon.
301+ //
302+ // So leave entry in the model and only remove its surface, so shell can display a screenshot
303+ // in its place.
304+ m_windowModel[i].removeOnceSurfaceDestroyed = false;
305+ }
306+}
307+
308+void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surface)
309+{
310+ int i = indexOf(surface);
311+ if (i == -1) {
312+ return;
313+ }
314+
315+ if (m_windowModel[i].removeOnceSurfaceDestroyed) {
316+ removeAt(i);
317+ } else {
318+ auto window = m_windowModel[i].window;
319+ window->setSurface(nullptr);
320+ window->setFocused(false);
321+ INFO_MSG << " Removed surface from entry. After: " << toString();
322+ }
323+}
324+
325+void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surface)
326+{
327+ DEBUG_MSG << "(" << surface << ")";
328+ if (surface->type() == Mir::InputMethodType) {
329+ int id = generateId();
330+ Window *qmlWindow = new Window(id, this);
331+ connectWindow(qmlWindow);
332+ qmlWindow->setSurface(surface);
333+ setInputMethodWindow(qmlWindow);
334+ } else {
335+ auto application = m_applicationManager->findApplicationWithSurface(surface);
336+ if (application) {
337+ prependSurface(surface, application);
338+ } else {
339+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
340+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
341+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
342+ int id = generateId();
343+ Window *promptWindow = new Window(id, this);
344+ connectWindow(promptWindow);
345+ promptWindow->setSurface(surface);
346+ connect(surface, &QObject::destroyed, promptWindow, [=](){
347+ promptWindow->setSurface(nullptr);
348+ promptWindow->deleteLater();
349+ });
350+ }
351+ }
352+ // TODO: handle surfaces that are neither top-level windows nor input method. eg: child dialogs, popups, menus
353+}
354+
355+void TopLevelWindowModel::removeAt(int index)
356+{
357+ if (m_modelState == IdleState) {
358+ beginRemoveRows(QModelIndex(), index, index);
359+ m_modelState = RemovingState;
360+ } else {
361+ Q_ASSERT(m_modelState == ResettingState);
362+ // No point in signaling anything if we're resetting the whole model
363+ }
364+
365+ auto window = m_windowModel[index].window;
366+
367+ window->setSurface(nullptr);
368+ window->setFocused(false);
369+
370+ m_windowModel.removeAt(index);
371+
372+ if (m_modelState == RemovingState) {
373+ endRemoveRows();
374+ Q_EMIT countChanged();
375+ Q_EMIT listChanged();
376+ m_modelState = IdleState;
377+ }
378+
379+ disconnect(window, 0, this, 0);
380+ if (m_focusedWindow == window) {
381+ setFocusedWindow(nullptr);
382+ }
383+ delete window;
384+
385+ INFO_MSG << " after " << toString();
386+}
387+
388+void TopLevelWindowModel::setInputMethodWindow(Window *window)
389+{
390+ if (m_inputMethodWindow) {
391+ qWarning("Multiple Input Method Surfaces created, removing the old one!");
392+ delete m_inputMethodWindow;
393+ }
394+ m_inputMethodWindow = window;
395+ Q_EMIT inputMethodSurfaceChanged(m_inputMethodWindow->surface());
396+}
397+
398+void TopLevelWindowModel::removeInputMethodWindow()
399+{
400+ if (m_inputMethodWindow) {
401+ delete m_inputMethodWindow;
402+ m_inputMethodWindow = nullptr;
403+ Q_EMIT inputMethodSurfaceChanged(nullptr);
404+ }
405+}
406+
407+void TopLevelWindowModel::onSurfacesRaised(const QVector<unityapi::MirSurfaceInterface*> &surfaces)
408+{
409+ DEBUG_MSG << "(" << surfaces << ")";
410+ const int raiseCount = surfaces.size();
411+ for (int i = 0; i < raiseCount; i++) {
412+ int fromIndex = findIndexOf(surfaces[i]);
413+ if (fromIndex != -1) {
414+ move(fromIndex, 0);
415+ }
416+ }
417+}
418+
419+int TopLevelWindowModel::rowCount(const QModelIndex &/*parent*/) const
420+{
421+ return m_windowModel.count();
422+}
423+
424+QVariant TopLevelWindowModel::data(const QModelIndex& index, int role) const
425+{
426+ if (index.row() < 0 || index.row() >= m_windowModel.size())
427+ return QVariant();
428+
429+ if (role == WindowRole) {
430+ Window *window = m_windowModel.at(index.row()).window;
431+ return QVariant::fromValue(window);
432+ } else if (role == ApplicationRole) {
433+ return QVariant::fromValue(m_windowModel.at(index.row()).application);
434+ } else {
435+ return QVariant();
436+ }
437+}
438+
439+int TopLevelWindowModel::findIndexOf(const unityapi::MirSurfaceInterface *surface) const
440+{
441+ for (int i=0; i<m_windowModel.count(); i++) {
442+ if (m_windowModel[i].window->surface() == surface) {
443+ return i;
444+ }
445+ }
446+ return -1;
447+}
448+
449+int TopLevelWindowModel::generateId()
450+{
451+ int id = m_nextId;
452+ m_nextId = nextFreeId(nextId(id), id);
453+ return id;
454+}
455+
456+int TopLevelWindowModel::nextId(int id) const
457+{
458+ if (id == m_maxId) {
459+ return id = 1;
460+ } else {
461+ return id + 1;
462+ }
463+}
464+
465+int TopLevelWindowModel::nextFreeId(int candidateId, const int latestId)
466+{
467+ int firstCandidateId = candidateId;
468+
469+ while (indexForId(candidateId) != -1 || candidateId == latestId) {
470+ candidateId = nextId(candidateId);
471+
472+ if (candidateId == firstCandidateId) {
473+ qFatal("TopLevelWindowModel: run out of window ids.");
474+ }
475+ }
476+
477+ return candidateId;
478+}
479+
480+QString TopLevelWindowModel::toString()
481+{
482+ QString str;
483+ for (int i = 0; i < m_windowModel.count(); ++i) {
484+ auto item = m_windowModel.at(i);
485+
486+ QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)")
487+ .arg(QString::number(i),
488+ item.application->appId(),
489+ QString::number((qintptr)item.window->surface(), 16),
490+ QString::number(item.window->id()));
491+
492+ if (i > 0) {
493+ str.append(",");
494+ }
495+ str.append(itemStr);
496+ }
497+ return str;
498+}
499+
500+int TopLevelWindowModel::indexOf(unityapi::MirSurfaceInterface *surface)
501+{
502+ for (int i = 0; i < m_windowModel.count(); ++i) {
503+ if (m_windowModel.at(i).window->surface() == surface) {
504+ return i;
505+ }
506+ }
507+ return -1;
508+}
509+
510+int TopLevelWindowModel::indexForId(int id) const
511+{
512+ for (int i = 0; i < m_windowModel.count(); ++i) {
513+ if (m_windowModel[i].window->id() == id) {
514+ return i;
515+ }
516+ }
517+ return -1;
518+}
519+
520+Window *TopLevelWindowModel::windowAt(int index) const
521+{
522+ if (index >=0 && index < m_windowModel.count()) {
523+ return m_windowModel[index].window;
524+ } else {
525+ return nullptr;
526+ }
527+}
528+
529+unityapi::MirSurfaceInterface *TopLevelWindowModel::surfaceAt(int index) const
530+{
531+ if (index >=0 && index < m_windowModel.count()) {
532+ return m_windowModel[index].window->surface();
533+ } else {
534+ return nullptr;
535+ }
536+}
537+
538+unityapi::ApplicationInfoInterface *TopLevelWindowModel::applicationAt(int index) const
539+{
540+ if (index >=0 && index < m_windowModel.count()) {
541+ return m_windowModel[index].application;
542+ } else {
543+ return nullptr;
544+ }
545+}
546+
547+int TopLevelWindowModel::idAt(int index) const
548+{
549+ if (index >=0 && index < m_windowModel.count()) {
550+ return m_windowModel[index].window->id();
551+ } else {
552+ return 0;
553+ }
554+}
555+
556+void TopLevelWindowModel::raiseId(int id)
557+{
558+ if (m_modelState == IdleState) {
559+ DEBUG_MSG << "(id=" << id << ") - do it now.";
560+ doRaiseId(id);
561+ } else {
562+ DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop.";
563+ // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts
564+ // if we perform yet another model change straight away.
565+ //
566+ // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says
567+ // the index is definitely within bounds.
568+ QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id));
569+ }
570+}
571+
572+void TopLevelWindowModel::doRaiseId(int id)
573+{
574+ int fromIndex = indexForId(id);
575+ // can't raise something that doesn't exist or that it's already on top
576+ if (fromIndex != -1 && fromIndex != 0) {
577+ auto surface = m_windowModel[fromIndex].window->surface();
578+ if (surface) {
579+ m_surfaceManager->raise(surface);
580+ } else {
581+ // move it ourselves. Since there's no mir::scene::Surface/miral::Window, there's nothing
582+ // miral can do about it.
583+ move(fromIndex, 0);
584+ }
585+ }
586+}
587+
588+void TopLevelWindowModel::setFocusedWindow(Window *window)
589+{
590+ if (window != m_focusedWindow) {
591+ INFO_MSG << "(" << window << ")";
592+
593+ Window* previousWindow = m_focusedWindow;
594+
595+ m_focusedWindow = window;
596+ Q_EMIT focusedWindowChanged(m_focusedWindow);
597+
598+ if (previousWindow && previousWindow->focused() && !previousWindow->surface()) {
599+ // do it ourselves. miral doesn't know about this window
600+ previousWindow->setFocused(false);
601+ }
602+ }
603+}
604+
605+unityapi::MirSurfaceInterface* TopLevelWindowModel::inputMethodSurface() const
606+{
607+ return m_inputMethodWindow ? m_inputMethodWindow->surface() : nullptr;
608+}
609+
610+Window* TopLevelWindowModel::focusedWindow() const
611+{
612+ return m_focusedWindow;
613+}
614+
615+void TopLevelWindowModel::move(int from, int to)
616+{
617+ if (from == to) return;
618+ DEBUG_MSG << " from=" << from << " to=" << to;
619+
620+ if (from >= 0 && from < m_windowModel.size() && to >= 0 && to < m_windowModel.size()) {
621+ QModelIndex parent;
622+ /* When moving an item down, the destination index needs to be incremented
623+ by one, as explained in the documentation:
624+ http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
625+
626+ Q_ASSERT(m_modelState == IdleState);
627+ m_modelState = MovingState;
628+
629+ beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
630+#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
631+ const auto &window = m_windowModel.takeAt(from);
632+ m_windowModel.insert(to, window);
633+#else
634+ m_windowModel.move(from, to);
635+#endif
636+ endMoveRows();
637+
638+ Q_EMIT listChanged();
639+ m_modelState = IdleState;
640+
641+ INFO_MSG << " after " << toString();
642+ }
643+}
644+void TopLevelWindowModel::onModificationsStarted()
645+{
646+}
647+
648+void TopLevelWindowModel::onModificationsEnded()
649+{
650+ if (m_focusedWindowChanged) {
651+ setFocusedWindow(m_newlyFocusedWindow);
652+ }
653+ // reset
654+ m_focusedWindowChanged = false;
655+ m_newlyFocusedWindow = nullptr;
656+}
657+
658+void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
659+{
660+ DEBUG_MSG << "(" << forbiddenId << ")";
661+
662+ for (int i = 0; i < m_windowModel.count(); ++i) {
663+ Window *window = m_windowModel[i].window;
664+ if (window->id() != forbiddenId) {
665+ window->activate();
666+ }
667+ }
668+}
plugins/WindowManager/TopLevelWindowModel.hnew file+260
@@ -0,0 +1,260 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
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
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef TOPLEVELWINDOWMODEL_H
18+#define TOPLEVELWINDOWMODEL_H
19+
20+#include <QAbstractListModel>
21+#include <QLoggingCategory>
22+
23+Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL)
24+
25+class Window;
26+
27+namespace unity {
28+ namespace shell {
29+ namespace application {
30+ class ApplicationInfoInterface;
31+ class ApplicationManagerInterface;
32+ class MirSurfaceInterface;
33+ class SurfaceManagerInterface;
34+ }
35+ }
36+}
37+
38+/**
39+ * @brief A model of top-level surfaces
40+ *
41+ * It's an abstraction of top-level application windows.
42+ *
43+ * When an entry first appears, it normaly doesn't have a surface yet, meaning that the application is
44+ * still starting up. A shell should then display a splash screen or saved screenshot of the application
45+ * until its surface comes up.
46+ *
47+ * As applications can have multiple surfaces and you can also have entries without surfaces at all,
48+ * the only way to unambiguously refer to an entry in this model is through its id.
49+ */
50+class TopLevelWindowModel : public QAbstractListModel
51+{
52+ Q_OBJECT
53+
54+ /**
55+ * @brief Number of top-level surfaces in this model
56+ *
57+ * This is the same as rowCount, added in order to keep compatibility with QML ListModels.
58+ */
59+ Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
60+
61+ /**
62+ * @brief The input method surface, if any
63+ *
64+ * The surface of a onscreen keyboard (akak "virtual keyboard") would be kept here and not in the model itself.
65+ */
66+ Q_PROPERTY(unity::shell::application::MirSurfaceInterface* inputMethodSurface READ inputMethodSurface NOTIFY inputMethodSurfaceChanged)
67+
68+ /**
69+ * @brief The currently focused window, if any
70+ */
71+ Q_PROPERTY(Window* focusedWindow READ focusedWindow NOTIFY focusedWindowChanged)
72+
73+ Q_PROPERTY(unity::shell::application::SurfaceManagerInterface* surfaceManager
74+ READ surfaceManager
75+ WRITE setSurfaceManager
76+ NOTIFY surfaceManagerChanged)
77+
78+ Q_PROPERTY(unity::shell::application::ApplicationManagerInterface* applicationManager
79+ READ applicationManager
80+ WRITE setApplicationManager
81+ NOTIFY applicationManagerChanged)
82+
83+ /**
84+ The id to be used on the next entry created
85+ Useful for tests
86+ */
87+ Q_PROPERTY(int nextId READ nextId NOTIFY nextIdChanged)
88+
89+public:
90+ /**
91+ * @brief The Roles supported by the model
92+ *
93+ * WindowRole - A Window.
94+ * ApplicationRole - An ApplicationInfoInterface
95+ */
96+ enum Roles {
97+ WindowRole = Qt::UserRole,
98+ ApplicationRole = Qt::UserRole + 1,
99+ };
100+
101+ TopLevelWindowModel();
102+
103+ // From QAbstractItemModel
104+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
105+ QVariant data(const QModelIndex& index, int role) const override;
106+ QHash<int, QByteArray> roleNames() const override {
107+ QHash<int, QByteArray> roleNames { {WindowRole, "window"},
108+ {ApplicationRole, "application"} };
109+ return roleNames;
110+ }
111+
112+ // Own API
113+
114+ unity::shell::application::MirSurfaceInterface* inputMethodSurface() const;
115+ Window* focusedWindow() const;
116+
117+ unity::shell::application::ApplicationManagerInterface *applicationManager() const { return m_applicationManager; }
118+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface*);
119+
120+ unity::shell::application::SurfaceManagerInterface *surfaceManager() const { return m_surfaceManager; }
121+ void setSurfaceManager(unity::shell::application::SurfaceManagerInterface*);
122+
123+ int nextId() const { return m_nextId; }
124+
125+public:
126+ /**
127+ * @brief Returns the surface at the given index
128+ *
129+ * It will be a nullptr if the application is still starting up and thus hasn't yet created
130+ * and drawn into a surface.
131+ *
132+ * Same as windowAt(index).surface()
133+ */
134+ Q_INVOKABLE unity::shell::application::MirSurfaceInterface *surfaceAt(int index) const;
135+
136+ /**
137+ * @brief Returns the window at the given index
138+ *
139+ * Will always be valid
140+ */
141+ Q_INVOKABLE Window *windowAt(int index) const;
142+
143+ /**
144+ * @brief Returns the application at the given index
145+ */
146+ Q_INVOKABLE unity::shell::application::ApplicationInfoInterface *applicationAt(int index) const;
147+
148+ /**
149+ * @brief Returns the unique id of the element at the given index
150+ */
151+ Q_INVOKABLE int idAt(int index) const;
152+
153+ /**
154+ * @brief Returns the index where the row with the given id is located
155+ *
156+ * Returns -1 if there's no row with the given id.
157+ */
158+ Q_INVOKABLE int indexForId(int id) const;
159+
160+ /**
161+ * @brief Raises the row with the given id to the top of the window stack (index == count-1)
162+ */
163+ Q_INVOKABLE void raiseId(int id);
164+
165+Q_SIGNALS:
166+ void countChanged();
167+ void inputMethodSurfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface);
168+ void focusedWindowChanged(Window *focusedWindow);
169+ void applicationManagerChanged(unity::shell::application::ApplicationManagerInterface*);
170+ void surfaceManagerChanged(unity::shell::application::SurfaceManagerInterface*);
171+
172+ /**
173+ * @brief Emitted when the list changes
174+ *
175+ * Emitted when model gains an element, loses an element or when elements exchange positions.
176+ */
177+ void listChanged();
178+
179+ void nextIdChanged();
180+
181+private Q_SLOTS:
182+ void onSurfaceCreated(unity::shell::application::MirSurfaceInterface *surface);
183+ void onSurfacesRaised(const QVector<unity::shell::application::MirSurfaceInterface*> &surfaces);
184+
185+ void onModificationsStarted();
186+ void onModificationsEnded();
187+
188+private:
189+ void doRaiseId(int id);
190+ int generateId();
191+ int nextFreeId(int candidateId, const int latestId);
192+ int nextId(int id) const;
193+ QString toString();
194+ int indexOf(unity::shell::application::MirSurfaceInterface *surface);
195+
196+ void setInputMethodWindow(Window *window);
197+ void setFocusedWindow(Window *window);
198+ void removeInputMethodWindow();
199+ int findIndexOf(const unity::shell::application::MirSurfaceInterface *surface) const;
200+ void removeAt(int index);
201+
202+ void addApplication(unity::shell::application::ApplicationInfoInterface *application);
203+ void removeApplication(unity::shell::application::ApplicationInfoInterface *application);
204+
205+ void prependPlaceholder(unity::shell::application::ApplicationInfoInterface *application);
206+ void prependSurface(unity::shell::application::MirSurfaceInterface *surface,
207+ unity::shell::application::ApplicationInfoInterface *application);
208+ void prependSurfaceHelper(unity::shell::application::MirSurfaceInterface *surface,
209+ unity::shell::application::ApplicationInfoInterface *application);
210+
211+ void connectWindow(Window *window);
212+ void connectSurface(unity::shell::application::MirSurfaceInterface *surface);
213+
214+ void onSurfaceDied(unity::shell::application::MirSurfaceInterface *surface);
215+ void onSurfaceDestroyed(unity::shell::application::MirSurfaceInterface *surface);
216+
217+ void move(int from, int to);
218+
219+ void activateEmptyWindow(Window *window);
220+
221+ void activateTopMostWindowWithoutId(int forbiddenId);
222+
223+ struct ModelEntry {
224+ ModelEntry() {}
225+ ModelEntry(Window *window,
226+ unity::shell::application::ApplicationInfoInterface *application)
227+ : window(window), application(application) {}
228+ Window *window{nullptr};
229+ unity::shell::application::ApplicationInfoInterface *application{nullptr};
230+ bool removeOnceSurfaceDestroyed{false};
231+ };
232+
233+ QVector<ModelEntry> m_windowModel;
234+ Window* m_inputMethodWindow{nullptr};
235+ Window* m_focusedWindow{nullptr};
236+
237+ int m_nextId{1};
238+ // Just something big enough that we don't risk running out of unused id numbers.
239+ // Not sure if QML int type supports something close to std::numeric_limits<int>::max() and
240+ // there's no reason to try out its limits.
241+ static const int m_maxId{1000000};
242+
243+ unity::shell::application::ApplicationManagerInterface* m_applicationManager{nullptr};
244+ unity::shell::application::SurfaceManagerInterface *m_surfaceManager{nullptr};
245+
246+ enum ModelState {
247+ IdleState,
248+ InsertingState,
249+ RemovingState,
250+ MovingState,
251+ ResettingState
252+ };
253+ ModelState m_modelState{IdleState};
254+
255+ // Valid between modificationsStarted and modificationsEnded
256+ bool m_focusedWindowChanged{false};
257+ Window *m_newlyFocusedWindow{nullptr};
258+};
259+
260+#endif // TOPLEVELWINDOWMODEL_H
plugins/WindowManager/Window.cppnew file+237
@@ -0,0 +1,237 @@
1+/*
2+ * Copyright (C) 2016 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 "Window.h"
18+
19+// unity-api
20+#include <unity/shell/application/MirSurfaceInterface.h>
21+
22+#include <QQmlEngine>
23+#include <QTextStream>
24+
25+namespace unityapi = unity::shell::application;
26+
27+Q_LOGGING_CATEGORY(UNITY_WINDOW, "unity.window", QtWarningMsg)
28+
29+#define DEBUG_MSG qCDebug(UNITY_WINDOW).nospace() << qPrintable(toString()) << "::" << __func__
30+
31+Window::Window(int id, QObject *parent)
32+ : QObject(parent)
33+ , m_id(id)
34+{
35+ DEBUG_MSG << "()";
36+ QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
37+}
38+
39+Window::~Window()
40+{
41+ DEBUG_MSG << "()";
42+}
43+
44+QPoint Window::position() const
45+{
46+ return m_position;
47+}
48+
49+QPoint Window::requestedPosition() const
50+{
51+ return m_requestedPosition;
52+}
53+
54+void Window::setRequestedPosition(const QPoint &value)
55+{
56+ m_positionRequested = true;
57+ if (value != m_requestedPosition) {
58+ m_requestedPosition = value;
59+ Q_EMIT requestedPositionChanged(m_requestedPosition);
60+ if (m_surface) {
61+ m_surface->setRequestedPosition(value);
62+ } else {
63+ // fake-miral: always comply
64+ m_position = m_requestedPosition;
65+ Q_EMIT positionChanged(m_position);
66+ }
67+ }
68+}
69+
70+Mir::State Window::state() const
71+{
72+ return m_state;
73+}
74+
75+bool Window::focused() const
76+{
77+ return m_focused;
78+}
79+
80+bool Window::confinesMousePointer() const
81+{
82+ if (m_surface) {
83+ return m_surface->confinesMousePointer();
84+ } else {
85+ return false;
86+ }
87+}
88+
89+int Window::id() const
90+{
91+ return m_id;
92+}
93+
94+unityapi::MirSurfaceInterface* Window::surface() const
95+{
96+ return m_surface;
97+}
98+
99+void Window::requestState(Mir::State state)
100+{
101+ m_stateRequested = true;
102+ if (m_surface) {
103+ m_surface->requestState(state);
104+ } else if (m_state != state) {
105+ m_state = state;
106+ Q_EMIT stateChanged(m_state);
107+ }
108+}
109+
110+void Window::close()
111+{
112+ if (m_surface) {
113+ m_surface->close();
114+ } else {
115+ Q_EMIT closeRequested();
116+ }
117+}
118+
119+void Window::activate()
120+{
121+ DEBUG_MSG << "()";
122+ if (m_surface) {
123+ m_surface->activate();
124+ } else {
125+ Q_EMIT emptyWindowActivated();
126+ }
127+}
128+
129+void Window::setSurface(unityapi::MirSurfaceInterface *surface)
130+{
131+ DEBUG_MSG << "(" << surface << ")";
132+ if (m_surface) {
133+ disconnect(m_surface, 0, this, 0);
134+ }
135+
136+ m_surface = surface;
137+
138+ if (m_surface) {
139+ connect(surface, &unityapi::MirSurfaceInterface::focusRequested, this, [this]() {
140+ Q_EMIT focusRequested();
141+ });
142+
143+ connect(surface, &unityapi::MirSurfaceInterface::closeRequested, this, &Window::closeRequested);
144+
145+ connect(surface, &unityapi::MirSurfaceInterface::positionChanged, this, [this]() {
146+ updatePosition();
147+ });
148+
149+ connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [this]() {
150+ updateState();
151+ });
152+
153+ connect(surface, &unityapi::MirSurfaceInterface::focusedChanged, this, [this]() {
154+ updateFocused();
155+ });
156+
157+ // bring it up to speed
158+ if (m_positionRequested) {
159+ m_surface->setRequestedPosition(m_requestedPosition);
160+ }
161+ if (m_stateRequested && m_surface->state() == Mir::RestoredState) {
162+ m_surface->requestState(m_state);
163+ }
164+
165+ // and sync with surface
166+ updatePosition();
167+ updateState();
168+ updateFocused();
169+ }
170+
171+ Q_EMIT surfaceChanged(surface);
172+}
173+
174+void Window::updatePosition()
175+{
176+ if (m_surface->position() != m_position) {
177+ m_position = m_surface->position();
178+ Q_EMIT positionChanged(m_position);
179+ }
180+}
181+
182+void Window::updateState()
183+{
184+ if (m_surface->state() != m_state) {
185+ m_state = m_surface->state();
186+ Q_EMIT stateChanged(m_state);
187+ }
188+}
189+
190+void Window::updateFocused()
191+{
192+ if (m_surface->focused() != m_focused) {
193+ m_focused = m_surface->focused();
194+ Q_EMIT focusedChanged(m_focused);
195+ }
196+}
197+
198+void Window::setFocused(bool value)
199+{
200+ if (value != m_focused) {
201+ DEBUG_MSG << "(" << value << ")";
202+ m_focused = value;
203+ Q_EMIT focusedChanged(m_focused);
204+ // when we have a surface we get focus changes from updateFocused() instead
205+ Q_ASSERT(!m_surface);
206+ }
207+}
208+
209+QString Window::toString() const
210+{
211+ QString result;
212+ {
213+ QTextStream stream(&result);
214+ stream << "Window["<<(void*)this<<", id="<<id()<<", ";
215+ if (surface()) {
216+ stream << "MirSurface["<<(void*)surface()<<",\""<<surface()->name()<<"\"]";
217+ } else {
218+ stream << "null";
219+ }
220+ stream << "]";
221+ }
222+ return result;
223+}
224+
225+QDebug operator<<(QDebug dbg, const Window *window)
226+{
227+ QDebugStateSaver saver(dbg);
228+ dbg.nospace();
229+
230+ if (window) {
231+ dbg << qPrintable(window->toString());
232+ } else {
233+ dbg << (void*)(window);
234+ }
235+
236+ return dbg;
237+}
plugins/WindowManager/Window.hnew file+161
@@ -0,0 +1,161 @@
1+/*
2+ * Copyright (C) 2016 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+#ifndef UNITY_WINDOW_H
18+#define UNITY_WINDOW_H
19+
20+#include <QLoggingCategory>
21+#include <QObject>
22+#include <QPoint>
23+
24+// Unity API
25+#include <unity/shell/application/Mir.h>
26+
27+namespace unity {
28+ namespace shell {
29+ namespace application {
30+ class MirSurfaceInterface;
31+ }
32+ }
33+}
34+
35+
36+Q_DECLARE_LOGGING_CATEGORY(UNITY_WINDOW)
37+
38+/**
39+ @brief A slightly higher concept than MirSurface
40+
41+ A Window exists before its MirSurface gets created (for splashscreen purposes)
42+ and might also hang around after the backing surface is gone (In case the application
43+ was killed to free up memory, as it should still remain in the window list since the user
44+ did not explicitly close it).
45+ */
46+class Window : public QObject
47+{
48+ Q_OBJECT
49+
50+ /**
51+ * @brief Position of the current surface buffer, in pixels.
52+ */
53+ Q_PROPERTY(QPoint position READ position NOTIFY positionChanged)
54+
55+ /**
56+ * @brief Requested position of the current surface buffer, in pixels.
57+ */
58+ Q_PROPERTY(QPoint requestedPosition READ requestedPosition WRITE setRequestedPosition NOTIFY requestedPositionChanged)
59+
60+ /**
61+ * @brief State of the surface
62+ */
63+ Q_PROPERTY(Mir::State state READ state NOTIFY stateChanged)
64+
65+ /**
66+ * @brief Whether the surface is focused
67+ *
68+ * It will be true if this surface is MirFocusControllerInterface::focusedSurface
69+ */
70+ Q_PROPERTY(bool focused READ focused NOTIFY focusedChanged)
71+
72+ /**
73+ * @brief Whether the surface wants to confine the mouse pointer within its boundaries
74+ *
75+ * If true, the surface doesn't want the mouse pointer to leave its boundaries while it's focused.
76+ */
77+ Q_PROPERTY(bool confinesMousePointer READ confinesMousePointer NOTIFY confinesMousePointerChanged)
78+
79+ /**
80+ * @brief A unique identifier for this window.
81+ * Useful for telling windows apart in a list model as they get moved around
82+ */
83+ Q_PROPERTY(int id READ id CONSTANT)
84+
85+ /**
86+ * @brief Surface backing up this window
87+ * It might be null if a surface hasn't been created yet (application is starting up) or if
88+ * the corresponding application has been killed (but can still get restarted to continue from
89+ * where it left)
90+ */
91+ Q_PROPERTY(unity::shell::application::MirSurfaceInterface* surface READ surface NOTIFY surfaceChanged)
92+
93+public:
94+ Window(int id, QObject *parent = nullptr);
95+ virtual ~Window();
96+ QPoint position() const;
97+ QPoint requestedPosition() const;
98+ void setRequestedPosition(const QPoint &);
99+ Mir::State state() const;
100+ bool focused() const;
101+ bool confinesMousePointer() const;
102+ int id() const;
103+ unity::shell::application::MirSurfaceInterface* surface() const;
104+
105+ void setSurface(unity::shell::application::MirSurfaceInterface *surface);
106+ void setFocused(bool value);
107+
108+ QString toString() const;
109+
110+public Q_SLOTS:
111+ /**
112+ * @brief Requests a change to the specified state
113+ */
114+ void requestState(Mir::State state);
115+
116+ /**
117+ * @brief Sends a close request
118+ *
119+ */
120+ void close();
121+
122+ /**
123+ * @brief Focuses and raises the window
124+ */
125+ void activate();
126+
127+Q_SIGNALS:
128+ void closeRequested();
129+ void emptyWindowActivated();
130+
131+ void positionChanged(QPoint position);
132+ void requestedPositionChanged(QPoint position);
133+ void stateChanged(Mir::State value);
134+ void focusedChanged(bool value);
135+ void confinesMousePointerChanged(bool value);
136+ void surfaceChanged(unity::shell::application::MirSurfaceInterface *surface);
137+
138+ /**
139+ * @brief Emitted when focus for this window is requested by an external party
140+ */
141+ void focusRequested();
142+
143+private:
144+ void updatePosition();
145+ void updateState();
146+ void updateFocused();
147+
148+ QPoint m_position;
149+ QPoint m_requestedPosition;
150+ bool m_positionRequested{false};
151+ bool m_focused{false};
152+ int m_id;
153+ Mir::State m_state{Mir::RestoredState};
154+ bool m_stateRequested{false};
155+ unity::shell::application::MirSurfaceInterface *m_surface{nullptr};
156+};
157+
158+QDebug operator<<(QDebug dbg, const Window *window);
159+
160+Q_DECLARE_METATYPE(Window*)
161+#endif // UNITY_WINDOW_H
plugins/WindowManager/WindowManagerPlugin.cpp+5−2
@@ -16,13 +16,16 @@
1616
1717#include "WindowManagerPlugin.h"
1818
19−#include "TopLevelSurfaceList.h"
19+#include "TopLevelWindowModel.h"
20+#include "Window.h"
2021
2122#include <QtQml>
2223
2324void WindowManagerPlugin::registerTypes(const char *uri)
2425{
25− qmlRegisterType<TopLevelSurfaceList>(uri, 0, 1, "TopLevelSurfaceList");
26+ qmlRegisterType<TopLevelWindowModel>(uri, 1, 0, "TopLevelWindowModel");
27+
28+ qRegisterMetaType<Window*>("Window*");
2629
2730 qRegisterMetaType<QAbstractListModel*>("QAbstractListModel*");
2831}
po/aa.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-02-12 10:18+0000\n"
1212"Last-Translator: Charif AYFARAH <ayfarah@ymail.com>\n"
1313"Language-Team: Afar <aa@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:55+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/am.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-08-13 14:45+0000\n"
1212"Last-Translator: samson <Unknown>\n"
1313"Language-Team: Amharic <am@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ar.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-29 09:41+0000\n"
1212"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
1313"Language-Team: Arabic <ar@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
1818"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/ast.po+13−13
@@ -7,16 +7,16 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
11−"PO-Revision-Date: 2016-09-28 13:40+0000\n"
12−"Last-Translator: Xuacu Saturio <xuacusk8@gmail.com>\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
11+"PO-Revision-Date: 2016-12-16 02:13+0000\n"
12+"Last-Translator: enolp <enolp@softastur.org>\n"
1313"Language-Team: Asturian <ast@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
@@ -183,7 +183,7 @@ msgstr ""
183183
184184#: qml/Components/KeyboardShortcutsOverlay.qml:182
185185msgid "Opens the Application Drawer."
186−msgstr ""
186+msgstr "Abre'l caxón d'aplicaciones."
187187
188188#: qml/Components/KeyboardShortcutsOverlay.qml:194
189189msgid "Scopes"
@@ -331,19 +331,19 @@ msgstr ""
331331
332332#: qml/Components/VirtualTouchPad.qml:327
333333msgid "Tap left button to click."
334−msgstr ""
334+msgstr "Calca nel botón esquierdu pa facer clic."
335335
336336#: qml/Components/VirtualTouchPad.qml:339
337337msgid "Tap right button to right click."
338−msgstr ""
338+msgstr "Calca nel botón drechu pa facer clic."
339339
340340#: qml/Components/VirtualTouchPad.qml:351
341341msgid "Swipe with two fingers to scroll."
342−msgstr ""
342+msgstr "Esliza con dos deos pa desplazate."
343343
344344#: qml/Components/VirtualTouchPad.qml:396
345345msgid "Find more settings in the system settings."
346−msgstr ""
346+msgstr "Alcuentra más axustes nos Axustes del Sistema."
347347
348348#: qml/Dash/DashPageHeader.qml:361
349349msgctxt "Button: Open the Ubuntu Store"
@@ -496,16 +496,16 @@ msgstr "Esbillar entornu d'escritoriu"
496496
497497#: qml/Launcher/Drawer.qml:80
498498msgid "Search…"
499−msgstr ""
499+msgstr "Guetar..."
500500
501501#: qml/Launcher/Drawer.qml:101
502502msgctxt "Apps sorted alphabetically"
503503msgid "A-Z"
504−msgstr ""
504+msgstr "A-Z"
505505
506506#: qml/Launcher/MoreAppsHeader.qml:32
507507msgid "More apps in the store"
508−msgstr ""
508+msgstr "Más aplicaciones na tienda"
509509
510510#: qml/Notifications/NotificationMenuItemFactory.qml:124
511511msgid "Show password"
po/az.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-09-03 16:39+0000\n"
1212"Last-Translator: Nicat Məmmədov <n1c4t97@gmail.com>\n"
1313"Language-Team: Azerbaijani <az@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/be.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-12-13 13:12+0000\n"
1212"Last-Translator: Mikhail_SaTuRn <mikhail_saturn@tut.by>\n"
1313"Language-Team: Belarusian <be@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/bg.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-04-12 14:43+0000\n"
1212"Last-Translator: George Karavasilev <kokoto-java@ubuntu.com>\n"
1313"Language-Team: Bulgarian <bg@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/bn.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-11-27 19:38+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Bengali <bn@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/br.po+8−6
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-08-29 11:37+0000\n"
1212"Last-Translator: Fohanno Thierry <thierry.fohanno@ofis-bzh.org>\n"
1313"Language-Team: Breton <br@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
@@ -267,7 +267,7 @@ msgstr "Erl + F4"
267267
268268#: qml/Components/KeyboardShortcutsOverlay.qml:336
269269msgid "Closes the current window."
270−msgstr ""
270+msgstr "Serriñ ar prenestr a vremañ"
271271
272272#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:252
273273msgid "Return to Call"
@@ -496,7 +496,7 @@ msgstr ""
496496
497497#: qml/Greeter/SessionsList.qml:122
498498msgid "Select desktop environment"
499−msgstr ""
499+msgstr "Diuzañ endro ar burev"
500500
501501#: qml/Launcher/Drawer.qml:80
502502msgid "Search…"
@@ -768,6 +768,8 @@ msgid ""
768768"There is a system update available and ready to install. Afterwards, the "
769769"device will automatically restart."
770770msgstr ""
771+"Un hizivadenn eus ar reizhiad zo da gaout. Prest eo da vezañ staliet. Goude-"
772+"se e adloc'ho ho penveg e-unan."
771773
772774#: qml/Wizard/Pages/77-system-update.qml:76
773775msgctxt "string identifying name of the update"
@@ -806,7 +808,7 @@ msgstr "Penndivizoù implij"
806808
807809#: qml/Wizard/Pages/here-terms.qml:69
808810msgid "Your device uses positioning technologies provided by HERE."
809−msgstr ""
811+msgstr "Ho penveg a implij teknologiezhoù lec'hiañ pourchaset gant HERE."
810812
811813#: qml/Wizard/Pages/here-terms.qml:81
812814msgid ""
po/bs.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-12-01 20:06+0000\n"
1212"Last-Translator: Kenan Gutić <Unknown>\n"
1313"Language-Team: Bosnian <bs@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/ca.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-11-11 06:30+0000\n"
1212"Last-Translator: Walter Garcia-Fontes <walter.garcia@upf.edu>\n"
1313"Language-Team: Catalan <ca@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ca@valencia.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-02-22 15:22+0000\n"
1212"Last-Translator: David Planella <david.planella@gmail.com>\n"
1313"Language-Team: Catalan <ca@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ce.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-02-15 04:01+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Chechen <ce@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ckb.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-01-21 11:05+0000\n"
1212"Last-Translator: Aras <aras.noori@gmail.com>\n"
1313"Language-Team: Kurdish (Sorani) <ckb@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/co.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-05-24 10:40+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Corsican <co@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/cs.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-07 10:44+0000\n"
1212"Last-Translator: Vojtěch Daněk <vdanek@outlook.com>\n"
1313"Language-Team: Czech <cs@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020"Language: cs\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
po/cy.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-31 13:21+0000\n"
1212"Last-Translator: Owen Llywelyn <owen.llywelyn@gmail.com>\n"
1313"Language-Team: Welsh <cy@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==2 ? 1 : (n != 8 && n != 11) ? "
1818"2 : 3;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/da.po+3−3
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
11−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
11+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1212"PO-Revision-Date: 2015-07-06 12:05+0000\n"
1313"Last-Translator: Folmer Fredslund <Unknown>\n"
1414"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/de.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-09-29 12:06+0000\n"
1212"Last-Translator: Matthias Seidel <matthias.seidel@hamburg.de>\n"
1313"Language-Team: German <de@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/el.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-04-25 16:24+0000\n"
1212"Last-Translator: Aggelos Arnaoutis <angelosarn@hotmail.com>\n"
1313"Language-Team: Greek <el@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/en_AU.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-03-27 10:09+0000\n"
1212"Last-Translator: Jared Norris <jarednorris@ubuntu.com>\n"
1313"Language-Team: English (Australia) <en_AU@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/en_GB.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-07-05 00:37+0000\n"
1212"Last-Translator: Andi Chandler <Unknown>\n"
1313"Language-Team: English (United Kingdom) <en_GB@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/eo.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-06-06 23:37+0000\n"
1212"Last-Translator: Eliovir <Unknown>\n"
1313"Language-Team: Esperanto <eo@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/es.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-03-25 12:02+0000\n"
1212"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
1313"Language-Team: Spanish <es@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/et.ponew file+932
@@ -0,0 +1,932 @@
1+# Estonian translation for unity8
2+# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
11+"PO-Revision-Date: 2016-12-28 06:29+0000\n"
12+"Last-Translator: Karlis <Unknown>\n"
13+"Language-Team: Estonian <et@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2016-12-29 04:56+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:123
22+msgid "Password: "
23+msgstr "Parool: "
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:50
26+#: plugins/Unity/Launcher/launcheritem.cpp:122
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:55
31+msgid "Quit"
32+msgstr "Välju"
33+
34+#: plugins/Unity/Launcher/launcheritem.cpp:122
35+msgid "Unpin shortcut"
36+msgstr ""
37+
38+#: qml/Components/Dialogs.qml:149
39+msgctxt "Title: Lock/Log out dialog"
40+msgid "Log out"
41+msgstr "Logi välja"
42+
43+#: qml/Components/Dialogs.qml:150
44+msgid "Are you sure you want to log out?"
45+msgstr ""
46+
47+#: qml/Components/Dialogs.qml:152
48+msgctxt "Button: Lock the system"
49+msgid "Lock"
50+msgstr ""
51+
52+#: qml/Components/Dialogs.qml:159
53+msgctxt "Button: Log out from the system"
54+msgid "Log Out"
55+msgstr "Logi välja"
56+
57+#: qml/Components/Dialogs.qml:166 qml/Components/Dialogs.qml:222
58+#: qml/Dash/DashPageHeader.qml:324 qml/Greeter/NarrowView.qml:232
59+#: qml/Wizard/Pages/passcode-confirm.qml:32
60+#: qml/Wizard/Pages/passcode-set.qml:32
61+msgid "Cancel"
62+msgstr "Sulge"
63+
64+#: qml/Components/Dialogs.qml:178
65+msgctxt "Title: Reboot dialog"
66+msgid "Reboot"
67+msgstr "Taaskäivita"
68+
69+#: qml/Components/Dialogs.qml:179
70+msgid "Are you sure you want to reboot?"
71+msgstr ""
72+
73+#: qml/Components/Dialogs.qml:181
74+msgid "No"
75+msgstr "Ei"
76+
77+#: qml/Components/Dialogs.qml:187
78+msgid "Yes"
79+msgstr "Jah"
80+
81+#: qml/Components/Dialogs.qml:202
82+msgctxt "Title: Power off/Restart dialog"
83+msgid "Power"
84+msgstr ""
85+
86+#: qml/Components/Dialogs.qml:203
87+msgid ""
88+"Are you sure you would like\n"
89+"to power off?"
90+msgstr ""
91+
92+#: qml/Components/Dialogs.qml:205
93+msgctxt "Button: Power off the system"
94+msgid "Power off"
95+msgstr "Lülita välja"
96+
97+#: qml/Components/Dialogs.qml:214
98+msgctxt "Button: Restart the system"
99+msgid "Restart"
100+msgstr "Taaskäivita"
101+
102+#: qml/Components/KeyboardShortcutsOverlay.qml:41
103+msgid "Keyboard Shortcuts"
104+msgstr ""
105+
106+#: qml/Components/KeyboardShortcutsOverlay.qml:55
107+msgid "Unity 8"
108+msgstr ""
109+
110+#: qml/Components/KeyboardShortcutsOverlay.qml:62
111+msgid "PrtScr"
112+msgstr ""
113+
114+#: qml/Components/KeyboardShortcutsOverlay.qml:67
115+msgid "Takes a screenshot."
116+msgstr ""
117+
118+#: qml/Components/KeyboardShortcutsOverlay.qml:75
119+msgid "Alt + PrtScr"
120+msgstr ""
121+
122+#: qml/Components/KeyboardShortcutsOverlay.qml:80
123+msgid "Takes a screenshot of a window."
124+msgstr ""
125+
126+#: qml/Components/KeyboardShortcutsOverlay.qml:88
127+msgid "Super + Space"
128+msgstr ""
129+
130+#: qml/Components/KeyboardShortcutsOverlay.qml:93
131+msgid "Switches to next keyboard layout."
132+msgstr ""
133+
134+#: qml/Components/KeyboardShortcutsOverlay.qml:101
135+msgid "Super + Shift + Space"
136+msgstr ""
137+
138+#: qml/Components/KeyboardShortcutsOverlay.qml:106
139+msgid "Switches to previous keyboard layout."
140+msgstr ""
141+
142+#: qml/Components/KeyboardShortcutsOverlay.qml:118
143+msgid "Launcher"
144+msgstr ""
145+
146+#: qml/Components/KeyboardShortcutsOverlay.qml:125
147+msgid "Super (Hold)"
148+msgstr ""
149+
150+#: qml/Components/KeyboardShortcutsOverlay.qml:130
151+msgid "Opens the launcher, displays shortcuts."
152+msgstr ""
153+
154+#: qml/Components/KeyboardShortcutsOverlay.qml:138
155+msgid "Alt + F1"
156+msgstr ""
157+
158+#: qml/Components/KeyboardShortcutsOverlay.qml:143
159+msgid "Opens launcher keyboard navigation mode."
160+msgstr ""
161+
162+#: qml/Components/KeyboardShortcutsOverlay.qml:151
163+msgid "Super + Tab"
164+msgstr ""
165+
166+#: qml/Components/KeyboardShortcutsOverlay.qml:156
167+msgid "Switches applications via the launcher."
168+msgstr ""
169+
170+#: qml/Components/KeyboardShortcutsOverlay.qml:164
171+msgid "Super + 0 to 9"
172+msgstr ""
173+
174+#: qml/Components/KeyboardShortcutsOverlay.qml:169
175+msgid "Same as clicking on a launcher icon."
176+msgstr ""
177+
178+#: qml/Components/KeyboardShortcutsOverlay.qml:177
179+msgid "Super + A"
180+msgstr ""
181+
182+#: qml/Components/KeyboardShortcutsOverlay.qml:182
183+msgid "Opens the Application Drawer."
184+msgstr ""
185+
186+#: qml/Components/KeyboardShortcutsOverlay.qml:194
187+msgid "Scopes"
188+msgstr ""
189+
190+#: qml/Components/KeyboardShortcutsOverlay.qml:201
191+msgid "Super (Tap)"
192+msgstr ""
193+
194+#: qml/Components/KeyboardShortcutsOverlay.qml:206
195+msgid "Opens the Scopes home."
196+msgstr ""
197+
198+#: qml/Components/KeyboardShortcutsOverlay.qml:222
199+msgid "Switching"
200+msgstr ""
201+
202+#: qml/Components/KeyboardShortcutsOverlay.qml:229
203+msgid "Alt + Tab"
204+msgstr ""
205+
206+#: qml/Components/KeyboardShortcutsOverlay.qml:234
207+msgid "Switches between applications."
208+msgstr ""
209+
210+#: qml/Components/KeyboardShortcutsOverlay.qml:242
211+msgid "Super + W"
212+msgstr ""
213+
214+#: qml/Components/KeyboardShortcutsOverlay.qml:247
215+msgid "Opens the desktop spread."
216+msgstr ""
217+
218+#: qml/Components/KeyboardShortcutsOverlay.qml:255
219+msgid "Cursor Left or Right"
220+msgstr ""
221+
222+#: qml/Components/KeyboardShortcutsOverlay.qml:260
223+msgid "Moves the focus."
224+msgstr ""
225+
226+#: qml/Components/KeyboardShortcutsOverlay.qml:272
227+msgid "Windows"
228+msgstr ""
229+
230+#: qml/Components/KeyboardShortcutsOverlay.qml:279
231+msgid "Ctrl + Super + D"
232+msgstr ""
233+
234+#: qml/Components/KeyboardShortcutsOverlay.qml:284
235+msgid "Minimizes all windows."
236+msgstr ""
237+
238+#: qml/Components/KeyboardShortcutsOverlay.qml:292
239+msgid "Ctrl + Super + Up"
240+msgstr ""
241+
242+#: qml/Components/KeyboardShortcutsOverlay.qml:297
243+msgid "Maximizes the current window."
244+msgstr ""
245+
246+#: qml/Components/KeyboardShortcutsOverlay.qml:305
247+msgid "Ctrl + Super + Down"
248+msgstr ""
249+
250+#: qml/Components/KeyboardShortcutsOverlay.qml:310
251+msgid "Minimizes or restores the current window."
252+msgstr ""
253+
254+#: qml/Components/KeyboardShortcutsOverlay.qml:318
255+msgid "Ctrl + Super + Left or Right"
256+msgstr ""
257+
258+#: qml/Components/KeyboardShortcutsOverlay.qml:323
259+msgid "Semi-maximizes the current window."
260+msgstr ""
261+
262+#: qml/Components/KeyboardShortcutsOverlay.qml:331
263+msgid "Alt + F4"
264+msgstr ""
265+
266+#: qml/Components/KeyboardShortcutsOverlay.qml:336
267+msgid "Closes the current window."
268+msgstr ""
269+
270+#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:252
271+msgid "Return to Call"
272+msgstr ""
273+
274+#: qml/Components/Lockscreen.qml:212
275+msgid "Emergency Call"
276+msgstr ""
277+
278+#: qml/Components/Lockscreen.qml:244
279+msgid "OK"
280+msgstr ""
281+
282+#: qml/Components/MediaServices/VideoPlayerControls.qml:44
283+#, qt-format
284+msgid "%1:%2:%3"
285+msgstr ""
286+
287+#: qml/Components/MediaServices/VideoPlayerControls.qml:49
288+#, qt-format
289+msgid "%1:%2"
290+msgstr ""
291+
292+#: qml/Components/ModeSwitchWarningDialog.qml:32
293+msgid "Apps may have unsaved data:"
294+msgstr ""
295+
296+#: qml/Components/ModeSwitchWarningDialog.qml:57
297+msgctxt ""
298+"Re-dock means connect the device again to an external screen/mouse/keyboard"
299+msgid "Re-dock, save your work and close these apps to continue."
300+msgstr ""
301+
302+#: qml/Components/ModeSwitchWarningDialog.qml:63
303+msgid "Or force close now (unsaved data will be lost)."
304+msgstr ""
305+
306+#: qml/Components/ModeSwitchWarningDialog.qml:75
307+msgid "OK, I will reconnect"
308+msgstr ""
309+
310+#: qml/Components/ModeSwitchWarningDialog.qml:76
311+msgid "Reconnect now!"
312+msgstr ""
313+
314+#: qml/Components/ModeSwitchWarningDialog.qml:88
315+msgid "Close all"
316+msgstr ""
317+
318+#: qml/Components/SharingPicker.qml:54
319+msgid "Preview Share Item"
320+msgstr ""
321+
322+#: qml/Components/VirtualTouchPad.qml:317
323+msgid ""
324+"Your device is now connected to an external display. Use this screen as a "
325+"touch pad to interact with the pointer."
326+msgstr ""
327+
328+#: qml/Components/VirtualTouchPad.qml:327
329+msgid "Tap left button to click."
330+msgstr ""
331+
332+#: qml/Components/VirtualTouchPad.qml:339
333+msgid "Tap right button to right click."
334+msgstr ""
335+
336+#: qml/Components/VirtualTouchPad.qml:351
337+msgid "Swipe with two fingers to scroll."
338+msgstr ""
339+
340+#: qml/Components/VirtualTouchPad.qml:396
341+msgid "Find more settings in the system settings."
342+msgstr ""
343+
344+#: qml/Dash/DashPageHeader.qml:361
345+msgctxt "Button: Open the Ubuntu Store"
346+msgid "Store"
347+msgstr ""
348+
349+#: qml/Dash/DashPageHeader.qml:368
350+msgctxt "Button: Start a search in the current dash scope"
351+msgid "Search"
352+msgstr ""
353+
354+#: qml/Dash/DashPageHeader.qml:378
355+msgctxt "Button: Show the current dash scope settings"
356+msgid "Settings"
357+msgstr ""
358+
359+#: qml/Dash/DashPageHeader.qml:385
360+msgid "Remove from Favorites"
361+msgstr ""
362+
363+#: qml/Dash/DashPageHeader.qml:385
364+msgid "Add to Favorites"
365+msgstr ""
366+
367+#: qml/Dash/FiltersPopover.qml:61
368+msgid "Refine your results"
369+msgstr ""
370+
371+#: qml/Dash/FiltersPopover.qml:70
372+msgid "Reset"
373+msgstr ""
374+
375+#: qml/Dash/GenericScopeView.qml:547 qml/Dash/GenericScopeView.qml:718
376+msgid "Show less"
377+msgstr ""
378+
379+#: qml/Dash/GenericScopeView.qml:547
380+msgid "Show all"
381+msgstr ""
382+
383+#: qml/Dash/GenericScopeView.qml:578
384+msgctxt "Label: Hint for dash search line edit"
385+msgid "Search"
386+msgstr ""
387+
388+#: qml/Dash/PageHeaderExtraPanel.qml:67
389+msgid "Recent Searches"
390+msgstr ""
391+
392+#: qml/Dash/PageHeaderExtraPanel.qml:78
393+msgid "Clear All"
394+msgstr ""
395+
396+#: qml/Dash/Previews/PreviewActionCombo.qml:36
397+msgid "More..."
398+msgstr ""
399+
400+#: qml/Dash/Previews/PreviewActionCombo.qml:36
401+msgid "Less..."
402+msgstr ""
403+
404+#: qml/Dash/Previews/PreviewCommentInput.qml:83
405+#: qml/Dash/Previews/PreviewRatingInput.qml:202
406+msgid "Send"
407+msgstr ""
408+
409+#: qml/Dash/Previews/PreviewRatingInput.qml:109
410+msgid "Rate this"
411+msgstr ""
412+
413+#: qml/Dash/Previews/PreviewRatingInput.qml:184
414+msgid "Add a review"
415+msgstr ""
416+
417+#: qml/Dash/PullToRefreshScopeStyle.qml:56
418+msgid "Pull to refresh…"
419+msgstr ""
420+
421+#: qml/Dash/PullToRefreshScopeStyle.qml:61
422+msgid "Release to refresh…"
423+msgstr ""
424+
425+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
426+msgid "Enable location data"
427+msgstr ""
428+
429+#: qml/Dash/ScopesList.qml:57
430+msgid "Manage"
431+msgstr ""
432+
433+#: qml/Dash/ScopesList.qml:104
434+msgid "Home"
435+msgstr ""
436+
437+#: qml/Dash/ScopesList.qml:105
438+msgid "Also installed"
439+msgstr ""
440+
441+#: qml/Greeter/CoverPage.qml:127
442+msgid "Unlock"
443+msgstr ""
444+
445+#: qml/Greeter/DelayedLockscreen.qml:43
446+msgid "Device Locked"
447+msgstr ""
448+
449+#: qml/Greeter/DelayedLockscreen.qml:58
450+msgid "You have been locked out due to too many failed passphrase attempts."
451+msgstr ""
452+
453+#: qml/Greeter/DelayedLockscreen.qml:59
454+msgid "You have been locked out due to too many failed passcode attempts."
455+msgstr ""
456+
457+#: qml/Greeter/DelayedLockscreen.qml:68
458+#, qt-format
459+msgid "Please wait %1 minute and then try again…"
460+msgid_plural "Please wait %1 minutes and then try again…"
461+msgstr[0] ""
462+msgstr[1] ""
463+
464+#: qml/Greeter/Greeter.qml:592
465+msgid "Try again"
466+msgstr ""
467+
468+#: qml/Greeter/LoginList.qml:70
469+msgid "Passphrase"
470+msgstr ""
471+
472+#: qml/Greeter/LoginList.qml:71
473+msgid "Passcode"
474+msgstr ""
475+
476+#: qml/Greeter/LoginList.qml:98
477+msgid "Retry"
478+msgstr ""
479+
480+#: qml/Greeter/LoginList.qml:99
481+msgid "Log In"
482+msgstr ""
483+
484+#: qml/Greeter/NarrowView.qml:252
485+msgid "Emergency"
486+msgstr ""
487+
488+#: qml/Greeter/SessionsList.qml:122
489+msgid "Select desktop environment"
490+msgstr ""
491+
492+#: qml/Launcher/Drawer.qml:80
493+msgid "Search…"
494+msgstr ""
495+
496+#: qml/Launcher/Drawer.qml:101
497+msgctxt "Apps sorted alphabetically"
498+msgid "A-Z"
499+msgstr ""
500+
501+#: qml/Launcher/MoreAppsHeader.qml:32
502+msgid "More apps in the store"
503+msgstr ""
504+
505+#: qml/Notifications/NotificationMenuItemFactory.qml:124
506+msgid "Show password"
507+msgstr ""
508+
509+#: qml/Panel/ActiveCallHint.qml:79
510+msgid "Tap to return to call..."
511+msgstr ""
512+
513+#: qml/Panel/ActiveCallHint.qml:92
514+msgid "Conference"
515+msgstr ""
516+
517+#: qml/Panel/Indicators/MenuItemFactory.qml:868
518+msgid "Nothing is playing"
519+msgstr ""
520+
521+#: qml/Panel/Indicators/MenuItemFactory.qml:996
522+#, qt-format
523+msgid "%1 hour"
524+msgid_plural "%1 hours"
525+msgstr[0] ""
526+msgstr[1] ""
527+
528+#: qml/Panel/Indicators/MenuItemFactory.qml:1000
529+#, qt-format
530+msgid "%1 minute"
531+msgid_plural "%1 minutes"
532+msgstr[0] ""
533+msgstr[1] ""
534+
535+#: qml/Panel/Indicators/MenuItemFactory.qml:1005
536+#, qt-format
537+msgid "%1 second"
538+msgid_plural "%1 seconds"
539+msgstr[0] ""
540+msgstr[1] ""
541+
542+#: qml/Panel/Indicators/MenuItemFactory.qml:1008
543+msgid "0 seconds"
544+msgstr ""
545+
546+#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
547+#: qml/Panel/Indicators/MenuItemFactory.qml:1010
548+#, qt-format
549+msgid "%1 remaining"
550+msgstr ""
551+
552+#: qml/Panel/Indicators/MenuItemFactory.qml:1016
553+msgid "In queue…"
554+msgstr ""
555+
556+#: qml/Panel/Indicators/MenuItemFactory.qml:1020
557+msgid "Downloading"
558+msgstr ""
559+
560+#: qml/Panel/Indicators/MenuItemFactory.qml:1022
561+msgid "Paused, tap to resume"
562+msgstr ""
563+
564+#: qml/Panel/Indicators/MenuItemFactory.qml:1024
565+msgid "Canceled"
566+msgstr ""
567+
568+#: qml/Panel/Indicators/MenuItemFactory.qml:1026
569+msgid "Finished"
570+msgstr ""
571+
572+#: qml/Panel/Indicators/MenuItemFactory.qml:1028
573+msgid "Failed, tap to retry"
574+msgstr ""
575+
576+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:151
577+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:210
578+msgctxt "Button: Send a reply message"
579+msgid "Send"
580+msgstr ""
581+
582+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:152
583+msgctxt "Label: Hint in message indicator line edit"
584+msgid "Reply"
585+msgstr ""
586+
587+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:209
588+msgctxt "Button: Call back on phone"
589+msgid "Call back"
590+msgstr ""
591+
592+#: qml/Stage/SideStage.qml:76
593+msgid "Drag using 3 fingers any application from one window to the other"
594+msgstr ""
595+
596+#: qml/Tutorial/TutorialLeftLong.qml:49
597+msgid "Long swipe from the left edge to open the Today scope"
598+msgstr ""
599+
600+#: qml/Tutorial/TutorialLeft.qml:47
601+msgid "Short swipe from the left edge to open the launcher"
602+msgstr ""
603+
604+#: qml/Tutorial/TutorialRight.qml:55
605+msgid "Push your mouse against the right edge to view your open apps"
606+msgstr ""
607+
608+#: qml/Tutorial/TutorialRight.qml:56
609+msgid "Swipe from the right edge to view your open apps"
610+msgstr ""
611+
612+#: qml/Tutorial/TutorialTop.qml:53
613+msgid "Swipe from the top edge to access notifications and quick settings"
614+msgstr ""
615+
616+#: qml/Wizard/Page.qml:54
617+msgctxt "Button: Go back one page in the Wizard"
618+msgid "Back"
619+msgstr ""
620+
621+#: qml/Wizard/Pages/10-welcome.qml:28
622+msgid "Language"
623+msgstr ""
624+
625+#: qml/Wizard/Pages/10-welcome.qml:173 qml/Wizard/Pages/20-keyboard.qml:152
626+#: qml/Wizard/Pages/30-wifi.qml:207 qml/Wizard/Pages/40-location.qml:271
627+#: qml/Wizard/Pages/50-timezone.qml:271 qml/Wizard/Pages/60-account.qml:65
628+#: qml/Wizard/Pages/70-passwd-type.qml:145
629+#: qml/Wizard/Pages/75-report-check.qml:84
630+#: qml/Wizard/Pages/passcode-desktop.qml:142
631+#: qml/Wizard/Pages/password-set.qml:142
632+msgid "Next"
633+msgstr ""
634+
635+#: qml/Wizard/Pages/20-keyboard.qml:31
636+msgid "Select Keyboard"
637+msgstr ""
638+
639+#: qml/Wizard/Pages/20-keyboard.qml:71
640+msgid "Keyboard language"
641+msgstr ""
642+
643+#: qml/Wizard/Pages/20-keyboard.qml:93
644+msgid "Keyboard layout"
645+msgstr ""
646+
647+#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:207
648+#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/77-system-update.qml:152
649+#: qml/Wizard/Pages/sim.qml:101
650+msgid "Skip"
651+msgstr ""
652+
653+#: qml/Wizard/Pages/30-wifi.qml:31
654+msgid "Connect to Wi‑Fi"
655+msgstr ""
656+
657+#: qml/Wizard/Pages/30-wifi.qml:132
658+msgid "Connected"
659+msgstr ""
660+
661+#: qml/Wizard/Pages/30-wifi.qml:165
662+msgid "Available Wi-Fi networks"
663+msgstr ""
664+
665+#: qml/Wizard/Pages/30-wifi.qml:166
666+msgid "No available Wi-Fi networks"
667+msgstr ""
668+
669+#: qml/Wizard/Pages/40-location.qml:27
670+msgid "Location Services"
671+msgstr ""
672+
673+#: qml/Wizard/Pages/40-location.qml:83
674+msgid ""
675+"Use GPS, Wi-Fi hotspots and mobile network anonymously to detect location "
676+"(recommended)"
677+msgstr ""
678+
679+#: qml/Wizard/Pages/40-location.qml:105
680+#, qt-format
681+msgid "By selecting this option you agree to the Nokia HERE %1."
682+msgstr ""
683+
684+#: qml/Wizard/Pages/40-location.qml:106
685+msgctxt "part of: Nokia HERE terms and conditions"
686+msgid "terms and conditions"
687+msgstr ""
688+
689+#: qml/Wizard/Pages/40-location.qml:160
690+msgid "GPS only"
691+msgstr ""
692+
693+#: qml/Wizard/Pages/40-location.qml:214
694+msgid "Don't use my location"
695+msgstr ""
696+
697+#: qml/Wizard/Pages/40-location.qml:260
698+msgid "You can change it later in System Settings."
699+msgstr ""
700+
701+#: qml/Wizard/Pages/50-timezone.qml:30
702+msgid "Time Zone"
703+msgstr ""
704+
705+#: qml/Wizard/Pages/50-timezone.qml:182
706+msgid "Enter your city"
707+msgstr ""
708+
709+#: qml/Wizard/Pages/60-account.qml:24
710+msgid "Personalize Your Device"
711+msgstr ""
712+
713+#: qml/Wizard/Pages/60-account.qml:48
714+msgid "Preferred Name"
715+msgstr ""
716+
717+#: qml/Wizard/Pages/70-passwd-type.qml:40
718+msgid "Lock Screen"
719+msgstr ""
720+
721+#: qml/Wizard/Pages/70-passwd-type.qml:102
722+msgctxt "Label: Type of security method"
723+msgid "Create new password"
724+msgstr ""
725+
726+#: qml/Wizard/Pages/70-passwd-type.qml:104
727+msgctxt "Label: Type of security method"
728+msgid "Create passcode (numbers only)"
729+msgstr ""
730+
731+#: qml/Wizard/Pages/70-passwd-type.qml:106
732+msgctxt "Label: Type of security method"
733+msgid "No lock code"
734+msgstr ""
735+
736+#: qml/Wizard/Pages/75-report-check.qml:26 qml/Wizard/Pages/here-terms.qml:108
737+msgid "Privacy Policy"
738+msgstr ""
739+
740+#: qml/Wizard/Pages/75-report-check.qml:26
741+msgid "Help Us Improve"
742+msgstr ""
743+
744+#: qml/Wizard/Pages/75-report-check.qml:59
745+msgid "Improve system performance by sending us crashes and error reports."
746+msgstr ""
747+
748+#: qml/Wizard/Pages/75-report-check.qml:60
749+msgid "Privacy policy"
750+msgstr ""
751+
752+#: qml/Wizard/Pages/77-system-update.qml:28
753+msgid "Update Device"
754+msgstr ""
755+
756+#: qml/Wizard/Pages/77-system-update.qml:55
757+msgid ""
758+"There is a system update available and ready to install. Afterwards, the "
759+"device will automatically restart."
760+msgstr ""
761+
762+#: qml/Wizard/Pages/77-system-update.qml:76
763+msgctxt "string identifying name of the update"
764+msgid "Ubuntu system"
765+msgstr ""
766+
767+#: qml/Wizard/Pages/77-system-update.qml:83
768+#, qt-format
769+msgctxt "version of the system update"
770+msgid "Version %1"
771+msgstr ""
772+
773+#: qml/Wizard/Pages/77-system-update.qml:102
774+msgid "This could take a few minutes..."
775+msgstr ""
776+
777+#: qml/Wizard/Pages/77-system-update.qml:116
778+msgid "Install and restart now"
779+msgstr ""
780+
781+#: qml/Wizard/Pages/80-finished.qml:89
782+msgid "Welcome to Ubuntu"
783+msgstr ""
784+
785+#: qml/Wizard/Pages/80-finished.qml:104
786+msgid "You are ready to use your device now"
787+msgstr ""
788+
789+#: qml/Wizard/Pages/80-finished.qml:124
790+msgid "Get Started"
791+msgstr ""
792+
793+#: qml/Wizard/Pages/here-terms.qml:25
794+msgid "Terms & Conditions"
795+msgstr ""
796+
797+#: qml/Wizard/Pages/here-terms.qml:69
798+msgid "Your device uses positioning technologies provided by HERE."
799+msgstr ""
800+
801+#: qml/Wizard/Pages/here-terms.qml:81
802+msgid ""
803+"To provide you with positioning services and to improve their quality, HERE "
804+"collects information about nearby cell towers and Wi-Fi hotspots around your "
805+"current location whenever your position is being found."
806+msgstr ""
807+
808+#: qml/Wizard/Pages/here-terms.qml:93
809+msgid ""
810+"The information collected is used to analyze the service and to improve the "
811+"use of service, but not to identify you personally."
812+msgstr ""
813+
814+#: qml/Wizard/Pages/here-terms.qml:106
815+#, qt-format
816+msgid "By continuing, you agree to the HERE platform %1 and %2."
817+msgstr ""
818+
819+#: qml/Wizard/Pages/here-terms.qml:107
820+msgid "Service Terms"
821+msgstr ""
822+
823+#: qml/Wizard/Pages/passcode-confirm.qml:43
824+#: qml/Wizard/Pages/passcode-desktop.qml:97
825+msgid "Confirm passcode"
826+msgstr ""
827+
828+#: qml/Wizard/Pages/passcode-confirm.qml:45
829+msgid "Incorrect passcode."
830+msgstr ""
831+
832+#: qml/Wizard/Pages/passcode-confirm.qml:45
833+msgctxt "Enter the passcode again"
834+msgid "Please re-enter."
835+msgstr ""
836+
837+#: qml/Wizard/Pages/passcode-desktop.qml:31
838+msgid "Lock Screen Passcode"
839+msgstr ""
840+
841+#: qml/Wizard/Pages/passcode-desktop.qml:61
842+msgid "Enter 4 numbers to setup your passcode"
843+msgstr ""
844+
845+#: qml/Wizard/Pages/passcode-desktop.qml:77
846+#: qml/Wizard/Pages/passcode-set.qml:54
847+msgid "Choose passcode"
848+msgstr ""
849+
850+#: qml/Wizard/Pages/passcode-desktop.qml:127
851+msgid "Passcode too short"
852+msgstr ""
853+
854+#: qml/Wizard/Pages/passcode-desktop.qml:129
855+msgid "Passcodes match"
856+msgstr ""
857+
858+#: qml/Wizard/Pages/passcode-desktop.qml:131
859+msgid "Passcodes do not match"
860+msgstr ""
861+
862+#: qml/Wizard/Pages/passcode-set.qml:62
863+msgid "Passcode must be 4 characters long"
864+msgstr ""
865+
866+#: qml/Wizard/Pages/password-set.qml:31
867+msgid "Lock Screen Password"
868+msgstr ""
869+
870+#: qml/Wizard/Pages/password-set.qml:62
871+msgid "Enter at least 8 characters"
872+msgstr ""
873+
874+#: qml/Wizard/Pages/password-set.qml:74
875+msgid "Choose password"
876+msgstr ""
877+
878+#: qml/Wizard/Pages/password-set.qml:104
879+msgid "Confirm password"
880+msgstr ""
881+
882+#: qml/Wizard/Pages/sim.qml:27
883+msgid "No SIM card installed"
884+msgstr ""
885+
886+#: qml/Wizard/Pages/sim.qml:54
887+msgid "SIM card added"
888+msgstr ""
889+
890+#: qml/Wizard/Pages/sim.qml:55
891+msgid "You must restart the device to access the mobile network."
892+msgstr ""
893+
894+#: qml/Wizard/Pages/sim.qml:59
895+msgid "Restart"
896+msgstr ""
897+
898+#: qml/Wizard/Pages/sim.qml:78
899+msgid "You won’t be able to make calls or use text messaging without a SIM."
900+msgstr ""
901+
902+#: qml/Wizard/Pages/sim.qml:90
903+msgid "To proceed with no SIM tap Skip."
904+msgstr ""
905+
906+#: qml/Wizard/PasswordMeter.qml:87
907+msgid "Password too short"
908+msgstr ""
909+
910+#: qml/Wizard/PasswordMeter.qml:89
911+msgid "Passwords match"
912+msgstr ""
913+
914+#: qml/Wizard/PasswordMeter.qml:91
915+msgid "Passwords do not match"
916+msgstr ""
917+
918+#: qml/Wizard/PasswordMeter.qml:95
919+msgid "Strong password"
920+msgstr ""
921+
922+#: qml/Wizard/PasswordMeter.qml:97
923+msgid "Fair password"
924+msgstr ""
925+
926+#: qml/Wizard/PasswordMeter.qml:99
927+msgid "Weak password"
928+msgstr ""
929+
930+#: qml/Wizard/PasswordMeter.qml:101
931+msgid "Very weak password"
932+msgstr ""
po/eu.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-13 23:29+0000\n"
1212"Last-Translator: Ibai Oihanguren Sala <Unknown>\n"
1313"Language-Team: Basque <eu@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/fa.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-10-18 07:03+0000\n"
1212"Last-Translator: Danial Behzadi <dani.behzi@gmail.com>\n"
1313"Language-Team: Persian <fa@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/fi.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-04 18:32+0000\n"
1212"Last-Translator: Jiri Grönroos <Unknown>\n"
1313"Language-Team: Finnish <fi@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/fo.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-01-14 20:08+0000\n"
1212"Last-Translator: Jógvan Olsen <jo@jebster.net>\n"
1313"Language-Team: Faroese <fo@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/fr.po+14−14
@@ -7,16 +7,16 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
11−"PO-Revision-Date: 2016-10-02 08:06+0000\n"
12−"Last-Translator: Olivier Febwin <febwin@free.fr>\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
11+"PO-Revision-Date: 2016-12-17 14:32+0000\n"
12+"Last-Translator: Anne017 <anneonyme017@openmailbox.org>\n"
1313"Language-Team: French <fr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
@@ -179,11 +179,11 @@ msgstr "Même effet qu'un clic sur une icône du lanceur."
179179
180180#: qml/Components/KeyboardShortcutsOverlay.qml:177
181181msgid "Super + A"
182−msgstr ""
182+msgstr "Super + A"
183183
184184#: qml/Components/KeyboardShortcutsOverlay.qml:182
185185msgid "Opens the Application Drawer."
186−msgstr ""
186+msgstr "Ouvre le tiroir des applications."
187187
188188#: qml/Components/KeyboardShortcutsOverlay.qml:194
189189msgid "Scopes"
@@ -335,19 +335,19 @@ msgstr ""
335335
336336#: qml/Components/VirtualTouchPad.qml:327
337337msgid "Tap left button to click."
338−msgstr ""
338+msgstr "Appuyez sur le bouton gauche pour cliquer."
339339
340340#: qml/Components/VirtualTouchPad.qml:339
341341msgid "Tap right button to right click."
342−msgstr ""
342+msgstr "Appuyez sur le bouton droit pour effectuer un clic droit."
343343
344344#: qml/Components/VirtualTouchPad.qml:351
345345msgid "Swipe with two fingers to scroll."
346−msgstr ""
346+msgstr "Faites glisser deux de vos doigts pour faire défiler."
347347
348348#: qml/Components/VirtualTouchPad.qml:396
349349msgid "Find more settings in the system settings."
350−msgstr ""
350+msgstr "Les réglages avancés sont accessibles via les paramètres système."
351351
352352#: qml/Dash/DashPageHeader.qml:361
353353msgctxt "Button: Open the Ubuntu Store"
@@ -503,16 +503,16 @@ msgstr "Sélectionner l'environnement de bureau"
503503
504504#: qml/Launcher/Drawer.qml:80
505505msgid "Search…"
506−msgstr ""
506+msgstr "Rechercher..."
507507
508508#: qml/Launcher/Drawer.qml:101
509509msgctxt "Apps sorted alphabetically"
510510msgid "A-Z"
511−msgstr ""
511+msgstr "A-Z"
512512
513513#: qml/Launcher/MoreAppsHeader.qml:32
514514msgid "More apps in the store"
515−msgstr ""
515+msgstr "Plus d'applications dans la logithèque"
516516
517517#: qml/Notifications/NotificationMenuItemFactory.qml:124
518518msgid "Show password"
po/ga.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-01-25 11:33+0000\n"
1212"Last-Translator: Joshua Kiley <Unknown>\n"
1313"Language-Team: Irish <ga@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n==2 ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/gd.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-04-08 18:34+0000\n"
1212"Last-Translator: Akerbeltz <fios@akerbeltz.org>\n"
1313"Language-Team: Fòram na Gàidhlig\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
1818"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121"Language: gd\n"
2222
2323#: plugins/LightDM/Greeter.cpp:123
po/gl.po+13−13
@@ -7,16 +7,16 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
11−"PO-Revision-Date: 2016-01-15 14:56+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
11+"PO-Revision-Date: 2016-12-16 00:21+0000\n"
1212"Last-Translator: Marcos Lans <Unknown>\n"
1313"Language-Team: Galician <gl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
@@ -177,11 +177,11 @@ msgstr "Igual que premer nunha icona do Iniciador."
177177
178178#: qml/Components/KeyboardShortcutsOverlay.qml:177
179179msgid "Super + A"
180−msgstr ""
180+msgstr "Super + A"
181181
182182#: qml/Components/KeyboardShortcutsOverlay.qml:182
183183msgid "Opens the Application Drawer."
184−msgstr ""
184+msgstr "Abre o caixón dos aplicativos."
185185
186186#: qml/Components/KeyboardShortcutsOverlay.qml:194
187187msgid "Scopes"
@@ -331,19 +331,19 @@ msgstr ""
331331
332332#: qml/Components/VirtualTouchPad.qml:327
333333msgid "Tap left button to click."
334−msgstr ""
334+msgstr "Toque no botón esquerdo para facer clic."
335335
336336#: qml/Components/VirtualTouchPad.qml:339
337337msgid "Tap right button to right click."
338−msgstr ""
338+msgstr "Toque no botón dereito para clic dereito."
339339
340340#: qml/Components/VirtualTouchPad.qml:351
341341msgid "Swipe with two fingers to scroll."
342−msgstr ""
342+msgstr "Esvare dous dedos para desprazar."
343343
344344#: qml/Components/VirtualTouchPad.qml:396
345345msgid "Find more settings in the system settings."
346−msgstr ""
346+msgstr "Atope máis axustes nos axustes do sistema."
347347
348348#: qml/Dash/DashPageHeader.qml:361
349349msgctxt "Button: Open the Ubuntu Store"
@@ -495,16 +495,16 @@ msgstr "Seleccione o ambiente de escritorio"
495495
496496#: qml/Launcher/Drawer.qml:80
497497msgid "Search…"
498−msgstr ""
498+msgstr "Buscar..."
499499
500500#: qml/Launcher/Drawer.qml:101
501501msgctxt "Apps sorted alphabetically"
502502msgid "A-Z"
503−msgstr ""
503+msgstr "A-Z"
504504
505505#: qml/Launcher/MoreAppsHeader.qml:32
506506msgid "More apps in the store"
507−msgstr ""
507+msgstr "Máis aplicativos na tenda"
508508
509509#: qml/Notifications/NotificationMenuItemFactory.qml:124
510510msgid "Show password"
po/gv.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-04-02 14:24+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Manx <gv@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : (n == 2 ? 1 : 2);\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/he.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-12 08:47+0000\n"
1212"Last-Translator: Yaron <sh.yaron@gmail.com>\n"
1313"Language-Team: Hebrew <he@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/hi.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-01-15 01:49+0000\n"
1212"Last-Translator: Siddhanathan Shanmugam <Unknown>\n"
1313"Language-Team: Hindi <hi@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/hr.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-05-04 17:52+0000\n"
1212"Last-Translator: gogo <trebelnik2@gmail.com>\n"
1313"Language-Team: Croatian <hr@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/hu.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-08-17 10:53+0000\n"
1212"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/hy.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-10-28 06:34+0000\n"
1212"Last-Translator: Vahan Harutyunyan <Unknown>\n"
1313"Language-Team: Armenian <hy@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ia.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-08-25 19:31+0000\n"
1212"Last-Translator: karm <melo@carmu.com>\n"
1313"Language-Team: Interlingua <ia@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/id.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2013-06-26 22:35+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Indonesian <id@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/is.po+3−3
@@ -9,7 +9,7 @@ msgid ""
99msgstr ""
1010"Project-Id-Version: po_unity8-is\n"
1111"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
12−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
12+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1313"PO-Revision-Date: 2014-11-13 09:32+0000\n"
1414"Last-Translator: Sveinn í Felli <sv1@fellsnet.is>\n"
1515"Language-Team: Icelandic <translation-team-is@lists.sourceforge.net>\n"
@@ -17,8 +17,8 @@ msgstr ""
1717"Content-Type: text/plain; charset=UTF-8\n"
1818"Content-Transfer-Encoding: 8bit\n"
1919"Plural-Forms: nplurals=2; plural=n != 1;\n"
20−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
21−"X-Generator: Launchpad (build 18293)\n"
20+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
21+"X-Generator: Launchpad (build 18298)\n"
2222"Language: is\n"
2323
2424#: plugins/LightDM/Greeter.cpp:123
po/it.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-09-11 10:40+0000\n"
1212"Last-Translator: Claudio Arseni <claudio.arseni@gmail.com>\n"
1313"Language-Team: Italian <it@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ja.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-10-05 10:05+0000\n"
1212"Last-Translator: Kentaro Kazuhama <Unknown>\n"
1313"Language-Team: Japanese <ja@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/km.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-08-20 02:48+0000\n"
1212"Last-Translator: Sophea Sok <sksophea@gmail.com>\n"
1313"Language-Team: Khmer <km@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/kn.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2013-06-27 05:43+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Kannada <kn@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ko.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-08-19 13:02+0000\n"
1212"Last-Translator: MinSik CHO <Unknown>\n"
1313"Language-Team: Korean <ko@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ku.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-09-10 13:14+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Kurdish <ku@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ky.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2013-06-27 06:17+0000\n"
1212"Last-Translator: Michał Sawicz <michal.sawicz@canonical.com>\n"
1313"Language-Team: Kirghiz <ky@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/lo.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2013-10-02 10:00+0000\n"
1212"Last-Translator: Anousak <anousak@gmail.com>\n"
1313"Language-Team: Lao <lo@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/lt.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-05-19 17:15+0000\n"
1212"Last-Translator: Mantas Kriaučiūnas <mantas@akl.lt>\n"
1313"Language-Team: Lithuanian <lt@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"(n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/lv.po+3−3
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity\n"
1010"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
11−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
11+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1212"PO-Revision-Date: 2015-11-22 19:13+0000\n"
1313"Last-Translator: Klavs Anson <anson.klavs@gmail.com>\n"
1414"Language-Team: Latvian <lata-l10n@googlegroups.com>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121"Language: lv\n"
2222
2323#: plugins/LightDM/Greeter.cpp:123
po/ml.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2013-11-23 11:44+0000\n"
1212"Last-Translator: ST Alfas <Unknown>\n"
1313"Language-Team: Malayalam <ml@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/mr.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-01-14 19:54+0000\n"
1212"Last-Translator: Rohan Deshmukh <Unknown>\n"
1313"Language-Team: Marathi <mr@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ms.po+76−72
@@ -7,16 +7,16 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
11−"PO-Revision-Date: 2016-05-24 01:36+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
11+"PO-Revision-Date: 2016-12-20 14:02+0000\n"
1212"Last-Translator: abuyop <Unknown>\n"
1313"Language-Team: Malay <ms@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
@@ -101,171 +101,171 @@ msgstr "Mula Semula"
101101
102102#: qml/Components/KeyboardShortcutsOverlay.qml:41
103103msgid "Keyboard Shortcuts"
104−msgstr ""
104+msgstr "Pintasan Papan Kekunci"
105105
106106#: qml/Components/KeyboardShortcutsOverlay.qml:55
107107msgid "Unity 8"
108−msgstr ""
108+msgstr "Unity 8"
109109
110110#: qml/Components/KeyboardShortcutsOverlay.qml:62
111111msgid "PrtScr"
112−msgstr ""
112+msgstr "PrtScr"
113113
114114#: qml/Components/KeyboardShortcutsOverlay.qml:67
115115msgid "Takes a screenshot."
116−msgstr ""
116+msgstr "Tangkap cekupan skrin."
117117
118118#: qml/Components/KeyboardShortcutsOverlay.qml:75
119119msgid "Alt + PrtScr"
120−msgstr ""
120+msgstr "Alt + PrtScr"
121121
122122#: qml/Components/KeyboardShortcutsOverlay.qml:80
123123msgid "Takes a screenshot of a window."
124−msgstr ""
124+msgstr "Ambil cekupan skrin bagi tetingkap."
125125
126126#: qml/Components/KeyboardShortcutsOverlay.qml:88
127127msgid "Super + Space"
128−msgstr ""
128+msgstr "Super + Space"
129129
130130#: qml/Components/KeyboardShortcutsOverlay.qml:93
131131msgid "Switches to next keyboard layout."
132−msgstr ""
132+msgstr "Tukar ke bentangan papan kekunci berikutnya."
133133
134134#: qml/Components/KeyboardShortcutsOverlay.qml:101
135135msgid "Super + Shift + Space"
136−msgstr ""
136+msgstr "Super + Shift + Space"
137137
138138#: qml/Components/KeyboardShortcutsOverlay.qml:106
139139msgid "Switches to previous keyboard layout."
140−msgstr ""
140+msgstr "Tukar ke bentangan papan kekunci terdahulu."
141141
142142#: qml/Components/KeyboardShortcutsOverlay.qml:118
143143msgid "Launcher"
144−msgstr ""
144+msgstr "Pelancar"
145145
146146#: qml/Components/KeyboardShortcutsOverlay.qml:125
147147msgid "Super (Hold)"
148−msgstr ""
148+msgstr "Super (Tahan)"
149149
150150#: qml/Components/KeyboardShortcutsOverlay.qml:130
151151msgid "Opens the launcher, displays shortcuts."
152−msgstr ""
152+msgstr "Buka pelancar, papar pintasan."
153153
154154#: qml/Components/KeyboardShortcutsOverlay.qml:138
155155msgid "Alt + F1"
156−msgstr ""
156+msgstr "Alt + F1"
157157
158158#: qml/Components/KeyboardShortcutsOverlay.qml:143
159159msgid "Opens launcher keyboard navigation mode."
160−msgstr ""
160+msgstr "Buka mod navigasi pelancar papan kekunci."
161161
162162#: qml/Components/KeyboardShortcutsOverlay.qml:151
163163msgid "Super + Tab"
164−msgstr ""
164+msgstr "Super + Tab"
165165
166166#: qml/Components/KeyboardShortcutsOverlay.qml:156
167167msgid "Switches applications via the launcher."
168−msgstr ""
168+msgstr "Tukar aplikasi melalui pelancar."
169169
170170#: qml/Components/KeyboardShortcutsOverlay.qml:164
171171msgid "Super + 0 to 9"
172−msgstr ""
172+msgstr "Super + 0 hingga 9"
173173
174174#: qml/Components/KeyboardShortcutsOverlay.qml:169
175175msgid "Same as clicking on a launcher icon."
176−msgstr ""
176+msgstr "Sama seperti mengklik pada ikon pelancar."
177177
178178#: qml/Components/KeyboardShortcutsOverlay.qml:177
179179msgid "Super + A"
180−msgstr ""
180+msgstr "Super + A"
181181
182182#: qml/Components/KeyboardShortcutsOverlay.qml:182
183183msgid "Opens the Application Drawer."
184−msgstr ""
184+msgstr "Buka Bekas Aplikasi."
185185
186186#: qml/Components/KeyboardShortcutsOverlay.qml:194
187187msgid "Scopes"
188−msgstr ""
188+msgstr "Skop"
189189
190190#: qml/Components/KeyboardShortcutsOverlay.qml:201
191191msgid "Super (Tap)"
192−msgstr ""
192+msgstr "Super (Ketik)"
193193
194194#: qml/Components/KeyboardShortcutsOverlay.qml:206
195195msgid "Opens the Scopes home."
196−msgstr ""
196+msgstr "Buka rumah Skop."
197197
198198#: qml/Components/KeyboardShortcutsOverlay.qml:222
199199msgid "Switching"
200−msgstr ""
200+msgstr "Pertukaran"
201201
202202#: qml/Components/KeyboardShortcutsOverlay.qml:229
203203msgid "Alt + Tab"
204−msgstr ""
204+msgstr "Alt + Tab"
205205
206206#: qml/Components/KeyboardShortcutsOverlay.qml:234
207207msgid "Switches between applications."
208−msgstr ""
208+msgstr "Tukar diantara aplikasi."
209209
210210#: qml/Components/KeyboardShortcutsOverlay.qml:242
211211msgid "Super + W"
212−msgstr ""
212+msgstr "Super + W"
213213
214214#: qml/Components/KeyboardShortcutsOverlay.qml:247
215215msgid "Opens the desktop spread."
216−msgstr ""
216+msgstr "Buka hamparan desktop."
217217
218218#: qml/Components/KeyboardShortcutsOverlay.qml:255
219219msgid "Cursor Left or Right"
220−msgstr ""
220+msgstr "Kursor Kiri atau Kanan"
221221
222222#: qml/Components/KeyboardShortcutsOverlay.qml:260
223223msgid "Moves the focus."
224−msgstr ""
224+msgstr "Alihkan fokus."
225225
226226#: qml/Components/KeyboardShortcutsOverlay.qml:272
227227msgid "Windows"
228−msgstr ""
228+msgstr "Tetingkap"
229229
230230#: qml/Components/KeyboardShortcutsOverlay.qml:279
231231msgid "Ctrl + Super + D"
232−msgstr ""
232+msgstr "Ctrl + Super + D"
233233
234234#: qml/Components/KeyboardShortcutsOverlay.qml:284
235235msgid "Minimizes all windows."
236−msgstr ""
236+msgstr "Minimumkan semua tetingkap."
237237
238238#: qml/Components/KeyboardShortcutsOverlay.qml:292
239239msgid "Ctrl + Super + Up"
240−msgstr ""
240+msgstr "Ctrl + Super + Atas"
241241
242242#: qml/Components/KeyboardShortcutsOverlay.qml:297
243243msgid "Maximizes the current window."
244−msgstr ""
244+msgstr "Maksimumkan tetingkap semasa."
245245
246246#: qml/Components/KeyboardShortcutsOverlay.qml:305
247247msgid "Ctrl + Super + Down"
248−msgstr ""
248+msgstr "Ctrl + Super + Bawah"
249249
250250#: qml/Components/KeyboardShortcutsOverlay.qml:310
251251msgid "Minimizes or restores the current window."
252−msgstr ""
252+msgstr "Minimumkan atau pulihkan tetingkap semasa."
253253
254254#: qml/Components/KeyboardShortcutsOverlay.qml:318
255255msgid "Ctrl + Super + Left or Right"
256−msgstr ""
256+msgstr "Ctrl + Super + Kiri atau Kanan"
257257
258258#: qml/Components/KeyboardShortcutsOverlay.qml:323
259259msgid "Semi-maximizes the current window."
260−msgstr ""
260+msgstr "Separa-maksimum tetingkap semasa."
261261
262262#: qml/Components/KeyboardShortcutsOverlay.qml:331
263263msgid "Alt + F4"
264−msgstr ""
264+msgstr "Alt + F4"
265265
266266#: qml/Components/KeyboardShortcutsOverlay.qml:336
267267msgid "Closes the current window."
268−msgstr ""
268+msgstr "Tutup tetingkap semasa."
269269
270270#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:252
271271msgid "Return to Call"
@@ -282,12 +282,12 @@ msgstr "OK"
282282#: qml/Components/MediaServices/VideoPlayerControls.qml:44
283283#, qt-format
284284msgid "%1:%2:%3"
285−msgstr ""
285+msgstr "%1:%2:%3"
286286
287287#: qml/Components/MediaServices/VideoPlayerControls.qml:49
288288#, qt-format
289289msgid "%1:%2"
290−msgstr ""
290+msgstr "%1:%2"
291291
292292#: qml/Components/ModeSwitchWarningDialog.qml:32
293293msgid "Apps may have unsaved data:"
@@ -329,19 +329,19 @@ msgstr ""
329329
330330#: qml/Components/VirtualTouchPad.qml:327
331331msgid "Tap left button to click."
332−msgstr ""
332+msgstr "Ketik butang kiri untuk mengklik."
333333
334334#: qml/Components/VirtualTouchPad.qml:339
335335msgid "Tap right button to right click."
336−msgstr ""
336+msgstr "Ketik butang kanan untuk mengklik kanan."
337337
338338#: qml/Components/VirtualTouchPad.qml:351
339339msgid "Swipe with two fingers to scroll."
340−msgstr ""
340+msgstr "Leret dengan dua jari untuk menatal."
341341
342342#: qml/Components/VirtualTouchPad.qml:396
343343msgid "Find more settings in the system settings."
344−msgstr ""
344+msgstr "Cari lagi tetapan dalam tetapan sistem."
345345
346346#: qml/Dash/DashPageHeader.qml:361
347347msgctxt "Button: Open the Ubuntu Store"
@@ -467,15 +467,15 @@ msgstr[1] "Sila tunggu %1 minit kemudian cuba lagi..."
467467
468468#: qml/Greeter/Greeter.qml:592
469469msgid "Try again"
470−msgstr ""
470+msgstr "Cuba lagi"
471471
472472#: qml/Greeter/LoginList.qml:70
473473msgid "Passphrase"
474−msgstr ""
474+msgstr "Frasa laluan"
475475
476476#: qml/Greeter/LoginList.qml:71
477477msgid "Passcode"
478−msgstr ""
478+msgstr "Kod laluan"
479479
480480#: qml/Greeter/LoginList.qml:98
481481msgid "Retry"
@@ -483,28 +483,28 @@ msgstr "Cuba Lagi"
483483
484484#: qml/Greeter/LoginList.qml:99
485485msgid "Log In"
486−msgstr ""
486+msgstr "Daftar Masuk"
487487
488488#: qml/Greeter/NarrowView.qml:252
489489msgid "Emergency"
490−msgstr ""
490+msgstr "Kecemasan"
491491
492492#: qml/Greeter/SessionsList.qml:122
493493msgid "Select desktop environment"
494−msgstr ""
494+msgstr "Pilih persekitaran desktop"
495495
496496#: qml/Launcher/Drawer.qml:80
497497msgid "Search…"
498−msgstr ""
498+msgstr "Gelintar..."
499499
500500#: qml/Launcher/Drawer.qml:101
501501msgctxt "Apps sorted alphabetically"
502502msgid "A-Z"
503−msgstr ""
503+msgstr "A-Z"
504504
505505#: qml/Launcher/MoreAppsHeader.qml:32
506506msgid "More apps in the store"
507−msgstr ""
507+msgstr "Lagi apl di dalam kedai"
508508
509509#: qml/Notifications/NotificationMenuItemFactory.qml:124
510510msgid "Show password"
@@ -605,7 +605,7 @@ msgstr "Leret panjang dari pinggir kiri untuk membuka skop Hari Ini"
605605
606606#: qml/Tutorial/TutorialLeft.qml:47
607607msgid "Short swipe from the left edge to open the launcher"
608−msgstr ""
608+msgstr "Leret pendek dari pinggir kiri untuk membuka pelancar"
609609
610610#: qml/Tutorial/TutorialRight.qml:55
611611msgid "Push your mouse against the right edge to view your open apps"
@@ -615,11 +615,13 @@ msgstr ""
615615
616616#: qml/Tutorial/TutorialRight.qml:56
617617msgid "Swipe from the right edge to view your open apps"
618−msgstr ""
618+msgstr "Leret dari pinggir kanan untuk melihat apl yang dibuka"
619619
620620#: qml/Tutorial/TutorialTop.qml:53
621621msgid "Swipe from the top edge to access notifications and quick settings"
622622msgstr ""
623+"Leret dari pinggir atas untuk mencapai tetapan pemberitahuan dan tetapan "
624+"pantas"
623625
624626#: qml/Wizard/Page.qml:54
625627msgctxt "Button: Go back one page in the Wizard"
@@ -642,15 +644,15 @@ msgstr "Berikutnya"
642644
643645#: qml/Wizard/Pages/20-keyboard.qml:31
644646msgid "Select Keyboard"
645−msgstr ""
647+msgstr "Pilih Papan Kekuci"
646648
647649#: qml/Wizard/Pages/20-keyboard.qml:71
648650msgid "Keyboard language"
649−msgstr ""
651+msgstr "Bahasa papan kekunci"
650652
651653#: qml/Wizard/Pages/20-keyboard.qml:93
652654msgid "Keyboard layout"
653−msgstr ""
655+msgstr "Bentangan papan kekunci"
654656
655657#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:207
656658#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/77-system-update.qml:152
@@ -762,32 +764,34 @@ msgstr "Dasar kerahsiaan"
762764
763765#: qml/Wizard/Pages/77-system-update.qml:28
764766msgid "Update Device"
765−msgstr ""
767+msgstr "Kemaskini Peranti"
766768
767769#: qml/Wizard/Pages/77-system-update.qml:55
768770msgid ""
769771"There is a system update available and ready to install. Afterwards, the "
770772"device will automatically restart."
771773msgstr ""
774+"Terdapat kemaskini sistem tersedia dan sedia dipasangkan. Selepas itu, "
775+"peranti akan dimulakan semula secara automatik."
772776
773777#: qml/Wizard/Pages/77-system-update.qml:76
774778msgctxt "string identifying name of the update"
775779msgid "Ubuntu system"
776−msgstr ""
780+msgstr "Sistem Ubuntu"
777781
778782#: qml/Wizard/Pages/77-system-update.qml:83
779783#, qt-format
780784msgctxt "version of the system update"
781785msgid "Version %1"
782−msgstr ""
786+msgstr "Versi %1"
783787
784788#: qml/Wizard/Pages/77-system-update.qml:102
785789msgid "This could take a few minutes..."
786−msgstr ""
790+msgstr "Ia mengambil masa beberapa minit..."
787791
788792#: qml/Wizard/Pages/77-system-update.qml:116
789793msgid "Install and restart now"
790−msgstr ""
794+msgstr "Pasang dan mula semula sekarang"
791795
792796#: qml/Wizard/Pages/80-finished.qml:89
793797msgid "Welcome to Ubuntu"
@@ -926,7 +930,7 @@ msgstr "Untuk teruskan tanpa SIM ketik Langkau."
926930
927931#: qml/Wizard/PasswordMeter.qml:87
928932msgid "Password too short"
929−msgstr ""
933+msgstr "Kata laluan terlalu pendek"
930934
931935#: qml/Wizard/PasswordMeter.qml:89
932936msgid "Passwords match"
po/my.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2013-12-12 11:12+0000\n"
1212"Last-Translator: Pyae Sone <Unknown>\n"
1313"Language-Team: Burmese <my@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/nb.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-10-30 17:41+0000\n"
1212"Last-Translator: Åka Sikrom <Unknown>\n"
1313"Language-Team: Norwegian Bokmal <nb@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ne.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-12-06 15:11+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Nepali <ne@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/nl.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-11-01 08:29+0000\n"
1212"Last-Translator: Hannie Dumoleyn <Unknown>\n"
1313"Language-Team: Dutch <nl@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/nn.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-05-12 11:32+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Norwegian Nynorsk <nn@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/oc.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-10-27 13:43+0000\n"
1212"Last-Translator: Cédric VALMARY (Tot en òc) <cvalmary@yahoo.fr>\n"
1313"Language-Team: Occitan (post 1500) <oc@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/or.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-03-21 05:28+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Oriya <or@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n!=1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/pa.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-04-24 04:29+0000\n"
1212"Last-Translator: Gursharnjit_Singh <ubuntuser13@gmail.com>\n"
1313"Language-Team: Punjabi <pa@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/pl.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-11-01 15:06+0000\n"
1212"Last-Translator: Krzysztof Tataradziński <Unknown>\n"
1313"Language-Team: Polish <pl@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
1818"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/pt.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-06-20 11:37+0000\n"
1212"Last-Translator: Ivo Xavier <ivoxavier.8@gmail.com>\n"
1313"Language-Team: Portuguese <pt@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/pt_BR.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-08-05 04:40+0000\n"
1212"Last-Translator: Julio Alexander Sieg <julio.sieg@gmail.com>\n"
1313"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ro.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-06 17:47+0000\n"
1212"Last-Translator: Meriuță Cornel <meriutacornel@neacornel.eu>\n"
1313"Language-Team: Romanian <ro@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n == 1 ? 0: (((n % 100 > 19) || ((n % 100 "
1818"== 0) && (n != 0))) ? 2: 1));\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/ru.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-06 05:33+0000\n"
1212"Last-Translator: ☠Jay ZDLin☠ <Unknown>\n"
1313"Language-Team: Russian <ru@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/shn.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-11-10 12:09+0000\n"
1212"Last-Translator: Rockworld <sumoisrock@gmail.com>\n"
1313"Language-Team: Shan <shn@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=(n == 1) ? 0 : 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/si.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2013-06-26 22:36+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Sinhalese <si@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/sk.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-09-28 04:43+0000\n"
1212"Last-Translator: P_E_T_O <Unknown>\n"
1313"Language-Team: Slovak <sk@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020"Language: sk\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
po/sl.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-01-31 18:34+0000\n"
1212"Last-Translator: Bernard Banko <Unknown>\n"
1313"Language-Team: Slovenian <sl@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
1818"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/sq.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-04-21 06:03+0000\n"
1212"Last-Translator: Indrit Bashkimi <indrit.bashkimi@gmail.com>\n"
1313"Language-Team: sq\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:56+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:58+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020"Language: sq\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
po/sr.po+3−3
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity\n"
1010"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
11−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
11+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1212"PO-Revision-Date: 2016-08-22 20:43+0000\n"
1313"Last-Translator: Bojan Bogdanović <Unknown>\n"
1414"Language-Team: српски <xfce4@xfce4.org>\n"
@@ -17,8 +17,8 @@ msgstr ""
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1919"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
20−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
21−"X-Generator: Launchpad (build 18293)\n"
20+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
21+"X-Generator: Launchpad (build 18298)\n"
2222"Language: sr\n"
2323
2424#: plugins/LightDM/Greeter.cpp:123
po/sv.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-06-19 19:31+0000\n"
1212"Last-Translator: Josef Andersson <Unknown>\n"
1313"Language-Team: Swedish\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020"Language: sv\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
po/sw.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-17 20:02+0000\n"
1212"Last-Translator: Valentine <mwangi_valentino@yahoo.com>\n"
1313"Language-Team: Swahili <sw@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 04:59+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ta.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2013-10-24 11:36+0000\n"
1212"Last-Translator: Arun Kumar - அருண் குமார் <thangam.arunx@gmail.com>\n"
1313"Language-Team: Ubuntu Tamil Team/Free Tamil Computing/Thamizha\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020"Language: ta\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
po/te.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-07-19 10:04+0000\n"
1212"Last-Translator: Praveen Illa <mail2ipn@gmail.com>\n"
1313"Language-Team: Telugu <te@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/th.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-10-02 04:55+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Thai <th@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/tr.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-03-22 15:56+0000\n"
1212"Last-Translator: Arda Ünlü <aarda.uunlu@gmail.com>\n"
1313"Language-Team: Turkish <tr@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/ug.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-07-03 13:56+0000\n"
1212"Last-Translator: Eltikin <eltikinuyghur@hotmail.com>\n"
1313"Language-Team: Uyghur Computer Science Association <UKIJ@yahoogroups.com>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/uk.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-04-01 17:32+0000\n"
1212"Last-Translator: Yuri Chornoivan <yurchor@gmail.com>\n"
1313"Language-Team: Ukrainian <uk@li.org>\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
20−"X-Generator: Launchpad (build 18293)\n"
19+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
20+"X-Generator: Launchpad (build 18298)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
2323msgid "Password: "
po/unity8.pot+1−1
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
11+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
po/ur.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-05-05 03:12+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Urdu <ur@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/uz.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-07-04 16:03+0000\n"
1212"Last-Translator: Learner <muzaffar.habibullayev@yandex.ru>\n"
1313"Language-Team: Uzbek <uz@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/vi.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-01-15 01:19+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Vietnamese <vi@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/wo.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2015-10-29 11:16+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Wolof <wo@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/xh.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2013-09-28 13:20+0000\n"
1212"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313"Language-Team: Xhosa <xh@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/zh_CN.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-04-13 06:27+0000\n"
1212"Last-Translator: YunQiang Su <wzssyqa@gmail.com>\n"
1313"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/zh_HK.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2014-08-10 11:45+0000\n"
1212"Last-Translator: Daniel Cheung <daniel.2345@hotmail.com>\n"
1313"Language-Team: Chinese (Hong Kong) <zh_HK@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:123
2222msgid "Password: "
po/zh_TW.po+3−3
@@ -7,7 +7,7 @@ msgid ""
77msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10−"POT-Creation-Date: 2016-12-07 13:49+0000\n"
10+"POT-Creation-Date: 2016-12-15 16:17+0000\n"
1111"PO-Revision-Date: 2016-02-24 04:40+0000\n"
1212"Last-Translator: Po-Hsu Lin <po-hsu.lin@canonical.com>\n"
1313"Language-Team: Chinese (Traditional) <zh_TW@li.org>\n"
@@ -15,8 +15,8 @@ msgstr ""
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2016-12-15 04:57+0000\n"
19−"X-Generator: Launchpad (build 18293)\n"
18+"X-Launchpad-Export-Date: 2016-12-23 05:00+0000\n"
19+"X-Generator: Launchpad (build 18298)\n"
2020"Language: zh_TW\n"
2121
2222#: plugins/LightDM/Greeter.cpp:123
qml/Components/InputMethod.qml+5−4
@@ -23,6 +23,8 @@ Item {
2323
2424 readonly property rect visibleRect: surfaceItem.surface && visible ? surfaceItem.surface.inputBounds : Qt.rect(0, 0, 0, 0)
2525
26+ property var surface
27+
2628 MirSurfaceItem {
2729 id: surfaceItem
2830 anchors.fill: parent
@@ -31,7 +33,7 @@ Item {
3133
3234 surfaceWidth: root.enabled ? width : -1
3335 surfaceHeight: root.enabled ? height : -1
34− surface: SurfaceManager.inputMethodSurface
36+ surface: root.surface
3537
3638 onLiveChanged: {
3739 if (surface !== null && !live) {
@@ -50,7 +52,6 @@ Item {
5052 }
5153
5254 visible: surfaceItem.surface &&
53− surfaceItem.surfaceState != Mir.HiddenState &&
54− surfaceItem.surfaceState != Mir.MinimizedState &&
55− root.enabled
55+ surfaceItem.surface.visible &&
56+ root.enabled
5657}
qml/Components/KeymapSwitcher.qml+4−1
@@ -23,6 +23,9 @@ import Unity.Application 0.1
2323QtObject {
2424 id: root
2525
26+ // to be set from outside
27+ property var focusedSurface: null
28+
2629 property GlobalShortcut shortcutNext: GlobalShortcut {
2730 shortcut: Qt.MetaModifier|Qt.Key_Space
2831 onTriggered: root.nextKeymap()
@@ -60,7 +63,7 @@ QtObject {
6063 }
6164
6265 property Binding surfaceKeymapBinding: Binding {
63− target: MirFocusController.focusedSurface
66+ target: root.focusedSurface
6467 property: "keymap"
6568 value: root.currentKeymap
6669 }
qml/Shell.qml+14−6
@@ -43,7 +43,7 @@ import Unity.Session 0.1
4343import Unity.DashCommunicator 0.1
4444import Unity.Indicators 0.1 as Indicators
4545import Cursor 1.1
46−import WindowManager 0.1
46+import WindowManager 1.0
4747
4848
4949StyledItem {
@@ -264,10 +264,15 @@ StyledItem {
264264 width: parent.width
265265 height: parent.height
266266
267− TopLevelSurfaceList {
267+ SurfaceManager {
268+ id: surfaceMan
269+ objectName: "surfaceManager"
270+ }
271+ TopLevelWindowModel {
268272 id: topLevelSurfaceList
269273 objectName: "topLevelSurfaceList"
270− applicationsModel: ApplicationManager
274+ applicationManager: ApplicationManager // it's a singleton
275+ surfaceManager: surfaceMan
271276 }
272277
273278 Stage {
@@ -314,6 +319,7 @@ StyledItem {
314319 InputMethod {
315320 id: inputMethod
316321 objectName: "inputMethod"
322+ surface: topLevelSurfaceList.inputMethodSurface
317323 anchors {
318324 fill: parent
319325 topMargin: panel.panelHeight
@@ -467,8 +473,8 @@ StyledItem {
467473 greeterShown: greeter.shown
468474 }
469475
470− readonly property bool focusedSurfaceIsFullscreen: MirFocusController.focusedSurface
471− ? MirFocusController.focusedSurface.state === Mir.FullscreenState
476+ readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
477+ ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
472478 : false
473479 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
474480 || greeter.hasLockedApp
@@ -762,7 +768,9 @@ StyledItem {
762768 }
763769
764770 // non-visual objects
765− KeymapSwitcher {}
771+ KeymapSwitcher {
772+ focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
773+ }
766774 BrightnessControl {}
767775
768776 Rectangle {
qml/Stage/FakeMaximizeDelegate.qml+7−7
@@ -98,19 +98,19 @@ Rectangle {
9898 function commit() {
9999 if (progress > hintThreshold && edge != -1) {
100100 if (edge == Item.Top) {
101− target.maximize();
101+ target.requestMaximize();
102102 } else if (edge == Item.Left) {
103− target.maximizeLeft();
103+ target.requestMaximizeLeft();
104104 } else if (edge == Item.Right) {
105− target.maximizeRight();
105+ target.requestMaximizeRight();
106106 } else if (edge == Item.TopLeft) {
107− target.maximizeTopLeft();
107+ target.requestMaximizeTopLeft();
108108 } else if (edge == Item.TopRight) {
109− target.maximizeTopRight();
109+ target.requestMaximizeTopRight();
110110 } else if (edge == Item.BottomLeft) {
111− target.maximizeBottomLeft();
111+ target.requestMaximizeBottomLeft();
112112 } else if (edge == Item.BottomRight) {
113− target.maximizeBottomRight();
113+ target.requestMaximizeBottomRight();
114114 }
115115 } else {
116116 stop();
qml/Stage/MoveHandler.qml+1−1
@@ -114,7 +114,7 @@ QtObject {
114114 // restore from maximized when dragging away from edges/corners; guard against inadvertent changes when going into maximized state
115115 if (target.anyMaximized && !target.windowedTransitionRunning) {
116116 priv.progress = 0;
117− target.restore(false, WindowStateStorage.WindowStateNormal);
117+ target.requestRestore();
118118 }
119119
120120 var pos = mapToItem(target.parent, mouse.x, mouse.y);
qml/Stage/Stage.qml+127−145
@@ -117,8 +117,7 @@ FocusScope {
117117 }
118118 }
119119
120− property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.surface &&
121− priv.focusedAppDelegate.surface.confinesMousePointer ?
120+ property Item itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.window.confinesMousePointer ?
122121 priv.focusedAppDelegate.clientAreaItem : null;
123122
124123 signal itemSnapshotRequested(Item item)
@@ -184,29 +183,34 @@ FocusScope {
184183 GlobalShortcut {
185184 id: maximizeWindowShortcut
186185 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
187− onTriggered: priv.focusedAppDelegate.maximize()
186+ onTriggered: priv.focusedAppDelegate.requestMaximize()
188187 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximized
189188 }
190189
191190 GlobalShortcut {
192191 id: maximizeWindowLeftShortcut
193192 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
194− onTriggered: priv.focusedAppDelegate.maximizeLeft()
193+ onTriggered: priv.focusedAppDelegate.requestMaximizeLeft()
195194 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
196195 }
197196
198197 GlobalShortcut {
199198 id: maximizeWindowRightShortcut
200199 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
201− onTriggered: priv.focusedAppDelegate.maximizeRight()
200+ onTriggered: priv.focusedAppDelegate.requestMaximizeRight()
202201 active: root.state == "windowed" && priv.focusedAppDelegate && priv.focusedAppDelegate.canBeMaximizedLeftRight
203202 }
204203
205204 GlobalShortcut {
206205 id: minimizeRestoreShortcut
207206 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
208− onTriggered: priv.focusedAppDelegate.anyMaximized
209− ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
207+ onTriggered: {
208+ if (priv.focusedAppDelegate.anyMaximized) {
209+ priv.focusedAppDelegate.requestRestore();
210+ } else {
211+ priv.focusedAppDelegate.requestMinimize();
212+ }
213+ }
210214 active: root.state == "windowed" && priv.focusedAppDelegate
211215 }
212216
@@ -242,20 +246,10 @@ FocusScope {
242246 }
243247
244248 function minimizeAllWindows() {
245− for (var i = 0; i < appRepeater.count; i++) {
246− var appDelegate = appRepeater.itemAt(i);
247− if (appDelegate && !appDelegate.minimized) {
248− appDelegate.minimize();
249− }
250− }
251− }
252−
253− function focusNext() {
254− for (var i = 0; i < appRepeater.count; i++) {
249+ for (var i = appRepeater.count - 1; i >= 0; i--) {
255250 var appDelegate = appRepeater.itemAt(i);
256251 if (appDelegate && !appDelegate.minimized) {
257− appDelegate.focus = true;
258− return;
252+ appDelegate.requestMinimize();
259253 }
260254 }
261255 }
@@ -356,8 +350,8 @@ FocusScope {
356350 Connections {
357351 target: PanelState
358352 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
359− onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
360− onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
353+ onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } }
354+ onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } }
361355 }
362356
363357 Binding {
@@ -432,13 +426,6 @@ FocusScope {
432426 }
433427 }
434428
435− Binding {
436− target: MirFocusController
437− property: "focusedSurface"
438− value: priv.focusedAppDelegate ? priv.focusedAppDelegate.focusedSurface : null
439− when: !appRepeater.startingUp && root.parent
440− }
441−
442429 states: [
443430 State {
444431 name: "spread"; when: priv.goneToSpread
@@ -631,7 +618,7 @@ FocusScope {
631618
632619 onShownChanged: {
633620 if (!shown && priv.mainStageDelegate && !root.spreadShown) {
634− priv.mainStageDelegate.claimFocus();
621+ priv.mainStageDelegate.activate();
635622 }
636623 }
637624
@@ -664,14 +651,23 @@ FocusScope {
664651 }
665652 }
666653
667− TopLevelSurfaceRepeater {
654+ Repeater {
668655 id: appRepeater
669656 model: topLevelSurfaceList
670657 objectName: "appRepeater"
671658
659+ function indexOf(delegateItem) {
660+ for (var i = 0; i < count; i++) {
661+ if (itemAt(i) === delegateItem) {
662+ return i;
663+ }
664+ }
665+ return -1;
666+ }
667+
672668 delegate: FocusScope {
673669 id: appDelegate
674− objectName: "appDelegate_" + model.id
670+ objectName: "appDelegate_" + model.window.id
675671 property int itemIndex: index // We need this from outside the repeater
676672 // z might be overriden in some cases by effects, but we need z ordering
677673 // to calculate occlusion detection
@@ -683,11 +679,11 @@ FocusScope {
683679 }
684680 z: normalZ
685681
686− // Normally we want x/y where we request it to be. Width/height of our delegate will
682+ // Normally we want x/y where the surface thinks it is. Width/height of our delegate will
687683 // match what the actual surface size is.
688684 // Don't write to those, they will be set by states
689− x: requestedX
690− y: requestedY
685+ x: model.window.position.x - clientAreaItem.x
686+ y: model.window.position.y - clientAreaItem.y
691687 width: decoratedWindow.implicitWidth
692688 height: decoratedWindow.implicitHeight
693689
@@ -697,6 +693,12 @@ FocusScope {
697693 property real requestedY: windowedY
698694 property real requestedWidth: windowedWidth
699695 property real requestedHeight: windowedHeight
696+ Binding {
697+ target: model.window; property: "requestedPosition"
698+ // miral doesn't know about our window decorations. So we have to deduct them
699+ value: Qt.point(appDelegate.requestedX + appDelegate.clientAreaItem.x,
700+ appDelegate.requestedY + appDelegate.clientAreaItem.y)
701+ }
700702
701703 // In those are for windowed mode. Those values basically store the window's properties
702704 // when having a floating window. If you want to move/resize a window in normal mode, this is what you want to write to.
@@ -788,7 +790,7 @@ FocusScope {
788790 maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
789791
790792 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
791− readonly property bool fullscreen: surface ? surface.state === Mir.FullscreenState : application.fullscreen
793+ readonly property bool fullscreen: window.state === Mir.FullscreenState
792794
793795 readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
794796 readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
@@ -814,7 +816,9 @@ FocusScope {
814816 priv.updateMainAndSideStageIndexes()
815817 }
816818
817− readonly property var surface: model.surface
819+ readonly property var surface: model.window.surface
820+ readonly property var window: model.window
821+
818822 readonly property alias resizeArea: resizeArea
819823 readonly property alias focusedSurface: decoratedWindow.focusedSurface
820824 readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
@@ -823,6 +827,25 @@ FocusScope {
823827 readonly property bool isDash: appId == "unity8-dash"
824828 readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
825829
830+ function activate() {
831+ if (model.window.focused) {
832+ updateQmlFocusFromMirSurfaceFocus();
833+ } else {
834+ model.window.activate();
835+ }
836+ }
837+ function requestMaximize() { model.window.requestState(Mir.MaximizedState); }
838+ function requestMaximizeVertically() { model.window.requestState(Mir.VertMaximizedState); }
839+ function requestMaximizeHorizontally() { model.window.requestState(Mir.HorizMaximizedState); }
840+ function requestMaximizeLeft() { model.window.requestState(Mir.MaximizedLeftState); }
841+ function requestMaximizeRight() { model.window.requestState(Mir.MaximizedRightState); }
842+ function requestMaximizeTopLeft() { model.window.requestState(Mir.MaximizedTopLeftState); }
843+ function requestMaximizeTopRight() { model.window.requestState(Mir.MaximizedTopRightState); }
844+ function requestMaximizeBottomLeft() { model.window.requestState(Mir.MaximizedBottomLeftState); }
845+ function requestMaximizeBottomRight() { model.window.requestState(Mir.MaximizedBottomRightState); }
846+ function requestMinimize() { model.window.requestState(Mir.MinimizedState); }
847+ function requestRestore() { model.window.requestState(Mir.RestoredState); }
848+
826849 function claimFocus() {
827850 if (root.state == "spread") {
828851 spreadItem.highlightedIndex = index
@@ -834,43 +857,16 @@ FocusScope {
834857 }
835858 priv.updateMainAndSideStageIndexes();
836859 }
837−
838860 if (root.mode == "windowed") {
839861 appDelegate.restore(true /* animated */, appDelegate.windowState);
840− } else {
841− appDelegate.focus = true;
842− }
843− }
844− Connections {
845− target: model.surface
846− onFocusRequested: {
847− claimFocus();
848− }
849− }
850− Connections {
851− target: model.application
852− onFocusRequested: {
853− if (!model.surface) {
854− // when an app has no surfaces, we assume there's only one entry representing it:
855− // this delegate.
856− claimFocus();
857− } else {
858− // if the application has surfaces, focus request should be at surface-level.
859− }
860862 }
863+ appDelegate.focus = true;
861864 }
862865
863− onFocusChanged: {
864− if (appRepeater.startingUp)
865− return;
866−
867− if (focus) {
868− topLevelSurfaceList.raiseId(model.id);
866+ function updateQmlFocusFromMirSurfaceFocus() {
867+ if (model.window.focused) {
868+ claimFocus();
869869 priv.focusedAppDelegate = appDelegate;
870− } else if (!focus && priv.focusedAppDelegate === appDelegate && root.state != "spread") {
871− priv.focusedAppDelegate = null;
872− // FIXME: No idea why the Binding{} doens't update when focusedAppDelegate turns null
873− MirFocusController.focusedSurface = null;
874870 }
875871 }
876872
@@ -883,6 +879,41 @@ FocusScope {
883879 minimumY: PanelState.panelHeight
884880 }
885881
882+ Connections {
883+ target: model.window
884+ onFocusedChanged: {
885+ updateQmlFocusFromMirSurfaceFocus();
886+ }
887+ onFocusRequested: {
888+ appDelegate.activate();
889+ }
890+ onStateChanged: {
891+ if (model.window.state === Mir.MinimizedState) {
892+ appDelegate.minimize();
893+ } else if (model.window.state === Mir.MaximizedState) {
894+ appDelegate.maximize();
895+ } else if (model.window.state === Mir.VertMaximizedState) {
896+ appDelegate.maximizeVertically();
897+ } else if (model.window.state === Mir.HorizMaximizedState) {
898+ appDelegate.maximizeHorizontally();
899+ } else if (model.window.state === Mir.MaximizedLeftState) {
900+ appDelegate.maximizeLeft();
901+ } else if (model.window.state === Mir.MaximizedRightState) {
902+ appDelegate.maximizeRight();
903+ } else if (model.window.state === Mir.MaximizedTopLeftState) {
904+ appDelegate.maximizeTopLeft();
905+ } else if (model.window.state === Mir.MaximizedTopRightState) {
906+ appDelegate.maximizeTopRight();
907+ } else if (model.window.state === Mir.MaximizedBottomLeftState) {
908+ appDelegate.maximizeBottomLeft();
909+ } else if (model.window.state === Mir.MaximizedBottomRightState) {
910+ appDelegate.maximizeBottomRight();
911+ } else if (model.window.state === Mir.RestoredState) {
912+ appDelegate.restore();
913+ }
914+ }
915+ }
916+
886917 Component.onCompleted: {
887918 if (application && application.rotatesWindowContents) {
888919 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
@@ -895,17 +926,9 @@ FocusScope {
895926 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
896927 // Now load any saved state. This needs to happen *after* the cascading!
897928 windowStateSaver.load();
929+ model.window.requestState(WindowStateStorage.toMirState(windowState));
898930
899− // NB: We're differentiating if this delegate was created in response to a new entry in the model
900− // or if the Repeater is just populating itself with delegates to match the model it received.
901− if (!appRepeater.startingUp) {
902− // a top level window is always the focused one when it first appears, unfocusing
903− // any preexisting one
904− if (root.state == "spread") {
905− spreadItem.highlightedIndex = index;
906− }
907− claimFocus();
908− }
931+ updateQmlFocusFromMirSurfaceFocus();
909932
910933 refreshStage();
911934 _constructing = false;
@@ -921,17 +944,6 @@ FocusScope {
921944 if (visuallyMaximized) {
922945 priv.updateForegroundMaximizedApp();
923946 }
924−
925− if (focus) {
926− // focus some other window
927− for (var i = 0; i < appRepeater.count; i++) {
928− var appDelegate = appRepeater.itemAt(i);
929− if (appDelegate && !appDelegate.minimized && i != index) {
930− appDelegate.focus = true;
931− return;
932− }
933− }
934− }
935947 }
936948
937949 onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
@@ -952,7 +964,7 @@ FocusScope {
952964 || focusAnimation.running || rightEdgeFocusAnimation.running || hidingAnimation.running
953965
954966 function close() {
955− model.surface.close();
967+ model.window.close();
956968 }
957969
958970 function maximize(animated) {
@@ -995,15 +1007,10 @@ FocusScope {
9951007 animationsEnabled = (animated === undefined) || animated;
9961008 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
9971009 }
998− function restoreFromMaximized(animated) {
999− animationsEnabled = (animated === undefined) || animated;
1000− windowState = WindowStateStorage.WindowStateRestored;
1001− }
10021010 function restore(animated,state) {
10031011 animationsEnabled = (animated === undefined) || animated;
10041012 windowState = state || WindowStateStorage.WindowStateRestored;
10051013 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
1006− focus = true;
10071014 }
10081015
10091016 function playFocusAnimation() {
@@ -1017,7 +1024,7 @@ FocusScope {
10171024 rightEdgeFocusAnimation.start()
10181025 }
10191026 } else if (state == "windowedRightEdge" || state == "windowed") {
1020− claimFocus();
1027+ activate();
10211028 } else {
10221029 focusAnimation.start()
10231030 }
@@ -1055,10 +1062,10 @@ FocusScope {
10551062 to: 1
10561063 duration: UbuntuAnimation.SnapDuration
10571064 onStarted: {
1058− topLevelSurfaceList.raiseId(model.id);
1065+ topLevelSurfaceList.raiseId(model.window.id);
10591066 }
10601067 onStopped: {
1061− appDelegate.claimFocus();
1068+ appDelegate.activate();
10621069 }
10631070 }
10641071 ParallelAnimation {
@@ -1068,7 +1075,7 @@ FocusScope {
10681075 UbuntuNumberAnimation { target: decoratedWindow; properties: "angle"; to: 0; duration: priv.animationDuration }
10691076 UbuntuNumberAnimation { target: decoratedWindow; properties: "itemScale"; to: 1; duration: priv.animationDuration }
10701077 onStopped: {
1071− appDelegate.focus = true
1078+ appDelegate.activate();
10721079 }
10731080 }
10741081 ParallelAnimation {
@@ -1318,15 +1325,9 @@ FocusScope {
13181325 windowedY: restoredY;
13191326 }
13201327 },
1321− State {
1322− name: "semiMaximized"
1323− PropertyChanges { target: touchControls; enabled: true }
1324− PropertyChanges { target: resizeArea; enabled: true }
1325− PropertyChanges { target: decoratedWindow; shadowOpacity: .3 }
1326− },
13271328 State {
13281329 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1329− extend: "semiMaximized"
1330+ extend: "normal"
13301331 PropertyChanges {
13311332 target: appDelegate
13321333 windowedX: root.leftMargin
@@ -1345,7 +1346,7 @@ FocusScope {
13451346 },
13461347 State {
13471348 name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
1348− extend: "semiMaximized"
1349+ extend: "normal"
13491350 PropertyChanges {
13501351 target: appDelegate
13511352 windowedX: root.leftMargin
@@ -1364,7 +1365,7 @@ FocusScope {
13641365 },
13651366 State {
13661367 name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
1367− extend: "semiMaximized"
1368+ extend: "normal"
13681369 PropertyChanges {
13691370 target: appDelegate
13701371 windowedX: root.leftMargin
@@ -1383,15 +1384,15 @@ FocusScope {
13831384 },
13841385 State {
13851386 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
1386− extend: "semiMaximized"
1387− PropertyChanges { target: appDelegate; requestedX: root.leftMargin; requestedY: windowedY;
1388− requestedWidth: appContainer.width - root.leftMargin; requestedHeight: appDelegate.windowedHeight }
1387+ extend: "normal"
1388+ PropertyChanges { target: appDelegate; windowedX: root.leftMargin; windowedY: windowedY;
1389+ windowedWidth: appContainer.width - root.leftMargin; windowedHeight: windowedHeight }
13891390 },
13901391 State {
13911392 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
1392− extend: "semiMaximized"
1393− PropertyChanges { target: appDelegate; requestedX: windowedX; requestedY: PanelState.panelHeight;
1394− requestedWidth: appDelegate.windowedWidth; requestedHeight: appContainer.height - PanelState.panelHeight }
1393+ extend: "normal"
1394+ PropertyChanges { target: appDelegate; windowedX: windowedX; windowedY: PanelState.panelHeight;
1395+ windowedWidth: windowedWidth; windowedHeight: appContainer.height - PanelState.panelHeight }
13951396 },
13961397 State {
13971398 name: "minimized"; when: appDelegate.minimized
@@ -1417,14 +1418,6 @@ FocusScope {
14171418 to: "staged,stagedWithSideStage"
14181419 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,requestedWidth,requestedHeight"; duration: priv.animationDuration}
14191420 },
1420− Transition {
1421− from: "maximized,maximizedHorizontally,maximizedVertically,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedBottomLeft,maximizedTopRight,maximizedBottomRight,minimized";
1422− to: "normal,restored"
1423− enabled: appDelegate.animationsEnabled
1424− PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1425− UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,windowedX,windowedY,requestedWidth,requestedHeight,windowedWidth,windowedHeight,scale";
1426− duration: priv.animationDuration }
1427− },
14281421 Transition {
14291422 to: "minimized"
14301423 enabled: appDelegate.animationsEnabled
@@ -1432,14 +1425,6 @@ FocusScope {
14321425 SequentialAnimation {
14331426 UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
14341427 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1435− ScriptAction {
1436− script: {
1437− if (appDelegate.minimized) {
1438− appDelegate.focus = false;
1439− priv.focusNext();
1440− }
1441− }
1442− }
14431428 }
14441429 },
14451430 Transition {
@@ -1492,8 +1477,8 @@ FocusScope {
14921477 },
14931478 Transition {
14941479 id: windowedTransition
1495− from: "normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTop,maximizedBottom,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1496− to: "normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTop,maximizedBottom,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
1480+ from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen,minimized"
1481+ to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
14971482 enabled: appDelegate.animationsEnabled
14981483 SequentialAnimation {
14991484 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
@@ -1529,7 +1514,7 @@ FocusScope {
15291514 visible: enabled
15301515
15311516 onPressed: {
1532− appDelegate.focus = true;
1517+ appDelegate.activate();
15331518 }
15341519 }
15351520
@@ -1539,7 +1524,7 @@ FocusScope {
15391524 anchors.left: appDelegate.left
15401525 anchors.top: appDelegate.top
15411526 application: model.application
1542− surface: model.surface
1527+ surface: model.window.surface
15431528 active: appDelegate.focus
15441529 focus: true
15451530 interactive: root.interactive
@@ -1564,21 +1549,21 @@ FocusScope {
15641549 onCloseClicked: { appDelegate.close(); }
15651550 onMaximizeClicked: {
15661551 if (appDelegate.canBeMaximized) {
1567− appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
1552+ appDelegate.anyMaximized ? appDelegate.requestRestore() : appDelegate.requestMaximize();
15681553 }
15691554 }
15701555 onMaximizeHorizontallyClicked: {
15711556 if (appDelegate.canBeMaximizedHorizontally) {
1572− appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
1557+ appDelegate.maximizedHorizontally ? appDelegate.requestRestore() : appDelegate.requestMaximizeHorizontally()
15731558 }
15741559 }
15751560 onMaximizeVerticallyClicked: {
15761561 if (appDelegate.canBeMaximizedVertically) {
1577− appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
1562+ appDelegate.maximizedVertically ? appDelegate.requestRestore() : appDelegate.requestMaximizeVertically()
15781563 }
15791564 }
1580− onMinimizeClicked: appDelegate.minimize()
1581− onDecorationPressed: { appDelegate.focus = true; }
1565+ onMinimizeClicked: { appDelegate.requestMinimize(); }
1566+ onDecorationPressed: { appDelegate.activate(); }
15821567 onDecorationReleased: fakeRectangle.commit();
15831568
15841569 property real angle: 0
@@ -1625,12 +1610,12 @@ FocusScope {
16251610 WindowedFullscreenPolicy {
16261611 id: windowedFullscreenPolicy
16271612 active: root.mode == "windowed"
1628− surface: model.surface
1613+ surface: model.window.surface
16291614 }
16301615 StagedFullscreenPolicy {
16311616 id: stagedFullscreenPolicy
16321617 active: root.mode == "staged" || root.mode == "stagedWithSideStage"
1633− surface: model.surface
1618+ surface: model.window.surface
16341619 }
16351620
16361621 SpreadDelegateInputArea {
@@ -1643,16 +1628,13 @@ FocusScope {
16431628 onClicked: {
16441629 spreadItem.highlightedIndex = index;
16451630 if (distance == 0) {
1631+ model.window.activate();
16461632 priv.goneToSpread = false;
16471633 }
16481634 }
16491635 onClose: {
16501636 priv.closingIndex = index
1651− if (model.surface) { // could be stopped by OOM
1652− model.surface.close()
1653− } else if (model.application) {
1654− root.applicationManager.stopApplication(model.application.appId);
1655− }
1637+ model.window.close();
16561638 }
16571639 }
16581640
qml/Stage/StagedFullscreenPolicy.qml+4−4
@@ -32,7 +32,7 @@ QtObject {
3232 onSurfaceChanged: {
3333 if (!active || !surface) return;
3434 if (surface.shellChrome === Mir.LowChrome) {
35− surface.state = Mir.FullscreenState;
35+ surface.requestState(Mir.FullscreenState);
3636 }
3737 }
3838
@@ -41,15 +41,15 @@ QtObject {
4141 onShellChromeChanged: {
4242 if (!active || !surface) return;
4343 if (surface.shellChrome === Mir.LowChrome) {
44− surface.state = Mir.FullscreenState;
44+ surface.requestState(Mir.FullscreenState);
4545 } else {
46− surface.state = Mir.RestoredState;
46+ surface.requestState(Mir.RestoredState);
4747 }
4848 }
4949 onStateChanged: {
5050 if (!active) return;
5151 if (surface.state === Mir.RestoredState && surface.shellChrome === Mir.LowChrome) {
52− surface.state = Mir.FullscreenState;
52+ surface.requestState(Mir.FullscreenState);
5353 }
5454 }
5555 }
qml/Stage/TopLevelSurfaceRepeater.qmldeleted−67
@@ -1,67 +0,0 @@
1−/*
2− * Copyright (C) 2016 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−
19−Repeater {
20− id: root
21− // FIXME: This is a hack around us not knowing whether the Repeater has finished creating its
22− // delegates on start up.
23− // This is a problem when the stage gets a TopLevelSurfaceList already populated with several
24− // rows.
25− property bool startingUp: true
26− onStartingUpChanged: {
27− if (!startingUp) {
28− // the top-most surface must be the focused one.
29− var topmostDelegate = itemAt(0);
30− if (topmostDelegate.focus) {
31− // Delegate got focused while we were still starting up. Not good.
32− // Force signal handler to run again
33− topmostDelegate.onFocusChanged(true);
34− } else {
35− topmostDelegate.focus = true;
36− }
37− }
38− }
39−
40− onItemAdded: {
41− if (startingUp) {
42− checkIfStillStartingUp();
43− }
44− }
45−
46− function checkIfStillStartingUp() {
47− var i = 0;
48− var missingDelegate = false;
49− for (i = 0; i < model.count && !missingDelegate; ++i) {
50− if (!itemAt(i)) {
51− missingDelegate = true;
52− }
53− }
54− if (!missingDelegate) {
55− startingUp = false;
56− }
57− }
58−
59− function indexOf(delegateItem) {
60− for (var i = 0; i < count; i++) {
61− if (itemAt(i) === delegateItem) {
62− return i;
63− }
64− }
65− return -1;
66− }
67−}
qml/Stage/WindowControlsOverlay.qml+1−1
@@ -57,7 +57,7 @@ Item {
5757 touchPoints.length <= maximumTouchPoints
5858 onRecognizedPressChanged: {
5959 if (recognizedPress) {
60− target.focus = true;
60+ target.activate();
6161 overlayTimer.start();
6262 }
6363 }
qml/Stage/WindowedFullscreenPolicy.qml+1−1
@@ -33,7 +33,7 @@ QtObject {
3333 _firstTimeSurface = false;
3434
3535 if (surface.state === Mir.FullscreenState && surface.shellChrome === Mir.LowChrome) {
36− surface.state = Mir.RestoredState;
36+ surface.requestState(Mir.RestoredState);
3737 }
3838 }
3939}
tests/mocks/CMakeLists.txt+1
@@ -35,6 +35,7 @@ add_subdirectory(GSettings.1.0)
3535add_subdirectory(indicator-service)
3636add_subdirectory(liblightdm)
3737add_subdirectory(libusermetrics)
38+add_subdirectory(LightDMController)
3839add_subdirectory(Lights)
3940add_subdirectory(MeeGo)
4041add_subdirectory(Powerd)
tests/mocks/LightDMController/CMakeLists.txtnew file+15
@@ -0,0 +1,15 @@
1+add_library(LightDMController MODULE
2+ plugin.cpp
3+ )
4+
5+target_include_directories(LightDMController PUBLIC
6+ ${CMAKE_SOURCE_DIR}/tests/mocks/liblightdm/
7+ )
8+
9+target_link_libraries(LightDMController
10+ MockLightDM
11+ )
12+
13+qt5_use_modules(LightDMController Qml)
14+
15+add_unity8_mock(LightDMController 0.1 LightDMController TARGETS LightDMController)
tests/mocks/LightDMController/plugin.cppnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2016 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 "plugin.h"
18+
19+#include "MockController.h"
20+
21+#include <QtQml/qqml.h>
22+
23+static QObject *mock_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
24+{
25+ Q_UNUSED(engine)
26+ Q_UNUSED(scriptEngine)
27+ return QLightDM::MockController::instance();
28+}
29+
30+void LightDMControllerPlugin::registerTypes(const char *uri)
31+{
32+ Q_ASSERT(uri == QLatin1String("LightDMController"));
33+ qmlRegisterSingletonType<QLightDM::MockController>(uri, 0, 1, "LightDMController", mock_provider);
34+}
tests/mocks/LightDMController/plugin.hnew file+29
@@ -0,0 +1,29 @@
1+/*
2+ * Copyright (C) 2016 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+#pragma once
18+
19+#include <QQmlEngine>
20+#include <QQmlExtensionPlugin>
21+
22+class LightDMControllerPlugin : public QQmlExtensionPlugin
23+{
24+ Q_OBJECT
25+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
26+
27+public:
28+ void registerTypes(const char *uri) override;
29+};
tests/mocks/LightDMController/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module LightDMController
2+plugin LightDMController
tests/mocks/Unity/Application/ApplicationInfo.cpp+48−24
@@ -75,6 +75,33 @@ ApplicationInfo::~ApplicationInfo()
7575{
7676}
7777
78+void ApplicationInfo::createPromptSurface()
79+{
80+ if (state() == ApplicationInfo::Stopped) { return; }
81+
82+ auto surfaceManager = SurfaceManager::instance();
83+ if (!surfaceManager) {
84+ WARNING_MSG("No SurfaceManager");
85+ return;
86+ }
87+
88+ QStringList screenshotIds = {"gallery", "map", "facebook", "camera", "browser", "music", "twitter"};
89+ int i = rand() % screenshotIds.count();
90+
91+ QUrl screenshotUrl = QString("qrc:///Unity/Application/screenshots/%1@12.png")
92+ .arg(screenshotIds[i]);
93+
94+ auto surface = surfaceManager->createSurface(QString("prompt foo"),
95+ Mir::NormalType,
96+ Mir::RestoredState,
97+ screenshotUrl);
98+ surfaceManager->notifySurfaceCreated(surface);
99+
100+ m_promptSurfaceList->addSurface(surface);
101+
102+ surfaceManager->activate(surface);
103+}
104+
78105void ApplicationInfo::createSurface()
79106{
80107 if (state() == ApplicationInfo::Stopped) { return; }
@@ -90,14 +117,16 @@ void ApplicationInfo::createSurface()
90117 return;
91118 }
92119
120+ bool wasFocused = focused();
121+
93122 auto surface = surfaceManager->createSurface(surfaceName,
94123 Mir::NormalType,
95− fullscreen() ? Mir::FullscreenState : Mir::MaximizedState,
124+ fullscreen() ? Mir::FullscreenState : Mir::RestoredState,
96125 m_screenshotFileName);
97126
98127 surface->setShellChrome(m_shellChrome);
99128
100− m_surfaceList->appendSurface(surface);
129+ m_surfaceList->addSurface(surface);
101130
102131 ++m_liveSurfaceCount;
103132 connect(surface, &MirSurface::liveChanged, this, [this, surface](){
@@ -125,8 +154,19 @@ void ApplicationInfo::createSurface()
125154 setState(Running);
126155 }
127156 });
157+ connect(surface, &MirSurfaceInterface::focusedChanged, this, [&](bool /*value*/) {
158+ #if APPLICATION_DEBUG
159+ qDebug().nospace() << "Application[" << appId() << "].focusedChanged(" << focused() << ")";
160+ #endif
161+ Q_EMIT focusedChanged(focused());
162+ });
163+
128164 connect(surface, &MirSurface::focusRequested, this, &ApplicationInfo::focusRequested);
129165
166+ if (wasFocused != focused()) {
167+ Q_EMIT focusedChanged(focused());
168+ }
169+
130170 if (m_state == Starting) {
131171 if (m_requestedState == RequestedRunning) {
132172 setState(Running);
@@ -134,6 +174,9 @@ void ApplicationInfo::createSurface()
134174 setState(Suspended);
135175 }
136176 }
177+
178+ surfaceManager->notifySurfaceCreated(surface);
179+ surfaceManager->activate(surface);
137180}
138181
139182void ApplicationInfo::setIconId(const QString &iconId)
@@ -218,7 +261,7 @@ void ApplicationInfo::setFullscreen(bool value)
218261{
219262 m_fullscreen = value;
220263 if (m_surfaceList->rowCount() > 0) {
221− m_surfaceList->get(0)->setState(Mir::FullscreenState);
264+ m_surfaceList->get(0)->requestState(Mir::FullscreenState);
222265 }
223266}
224267
@@ -349,26 +392,6 @@ bool ApplicationInfo::focused() const
349392 return someSurfaceHasFocus;
350393}
351394
352−void ApplicationInfo::setFocused(bool value)
353−{
354− if (focused() == value) {
355− return;
356− }
357−
358− if (value) {
359− if (m_surfaceList->count() > 0) {
360− m_surfaceList->get(0)->requestFocus();
361− }
362− } else {
363− for (int i = 0; i < m_surfaceList->count(); ++i) {
364− MirSurface *surface = static_cast<MirSurface*>(m_surfaceList->get(i));
365− if (surface->focused()) {
366− surface->setFocused(false);
367− }
368− }
369− }
370−}
371−
372395void ApplicationInfo::onSurfaceCountChanged()
373396{
374397 if (m_surfaceList->count() == 0 && m_state == Running) {
@@ -381,6 +404,7 @@ void ApplicationInfo::requestFocus()
381404 if (m_surfaceList->count() == 0) {
382405 Q_EMIT focusRequested();
383406 } else {
384− m_surfaceList->get(0)->requestFocus();
407+ auto surface = static_cast<MirSurface*>(m_surfaceList->get(0));
408+ surface->requestFocus();
385409 }
386410}
tests/mocks/Unity/Application/ApplicationInfo.h+2−2
@@ -51,6 +51,8 @@ public:
5151 ApplicationInfo(const QString &appId, QObject *parent = nullptr);
5252 ~ApplicationInfo();
5353
54+ Q_INVOKABLE void createPromptSurface();
55+
5456 RequestedState requestedState() const override;
5557 void setRequestedState(RequestedState) override;
5658
@@ -108,8 +110,6 @@ public:
108110 MirSurfaceListInterface* promptSurfaceList() const override { return m_promptSurfaceList; }
109111 int surfaceCount() const override { return m_surfaceList->count(); }
110112
111− void setFocused(bool value);
112−
113113 //////
114114 // internal mock stuff
115115 void close();
tests/mocks/Unity/Application/ApplicationManager.cpp+88−59
@@ -34,29 +34,23 @@
3434
3535#if APPLICATIONMANAGER_DEBUG
3636#define DEBUG_MSG(params) qDebug().nospace() << "ApplicationManager::" << __func__ << " " << params
37+#define XDEBUG_MSG(params) qDebug().nospace() << "ApplicationManager::" << params
3738#else
3839#define DEBUG_MSG(params) ((void)0)
40+#define XDEBUG_MSG(params) ((void)0)
3941#endif
4042
4143namespace unityapi = unity::shell::application;
4244
45+
4346ApplicationManager::ApplicationManager(QObject *parent)
4447 : ApplicationManagerInterface(parent)
4548{
4649 DEBUG_MSG("");
47− buildListOfAvailableApplications();
4850
49− // polling to find out when the toplevel window has been created as there's
50− // no signal telling us that
51− connect(&m_windowCreatedTimer, &QTimer::timeout,
52− this, &ApplicationManager::onWindowCreatedTimerTimeout);
53− m_windowCreatedTimer.setSingleShot(false);
54− m_windowCreatedTimer.start(200);
55−
56− Q_ASSERT(MirFocusController::instance());
57− connect(MirFocusController::instance(), &MirFocusController::focusedSurfaceChanged,
58− this, &ApplicationManager::updateFocusedApplication, Qt::QueuedConnection);
51+ ApplicationManagerNotifier::instance()->setApplicationManager(this);
5952
53+ buildListOfAvailableApplications();
6054
6155 // Emit signal to notify Upstart that Mir is ready to receive client connections
6256 // see http://upstart.ubuntu.com/cookbook/#expect-stop
@@ -69,19 +63,7 @@ ApplicationManager::ApplicationManager(QObject *parent)
6963
7064ApplicationManager::~ApplicationManager()
7165{
72−}
73−
74−void ApplicationManager::onWindowCreatedTimerTimeout()
75−{
76− if (QGuiApplication::topLevelWindows().count() > 0) {
77− m_windowCreatedTimer.stop();
78− onWindowCreated();
79− }
80−}
81−
82−void ApplicationManager::onWindowCreated()
83−{
84− startApplication("unity8-dash");
66+ ApplicationManagerNotifier::instance()->setApplicationManager(nullptr);
8567}
8668
8769int ApplicationManager::rowCount(const QModelIndex& parent) const {
@@ -132,6 +114,17 @@ ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const
132114 return nullptr;
133115}
134116
117+unityapi::ApplicationInfoInterface *ApplicationManager::findApplicationWithSurface(unityapi::MirSurfaceInterface* surface) const
118+{
119+ for (ApplicationInfo *app : m_runningApplications) {
120+ auto surfaceList = static_cast<MirSurfaceListModel*>(app->surfaceList());
121+ if (surfaceList->contains(static_cast<MirSurface*>(surface))) {
122+ return app;
123+ }
124+ }
125+ return nullptr;
126+}
127+
135128QModelIndex ApplicationManager::findIndex(ApplicationInfo* application)
136129{
137130 for (int i = 0; i < m_runningApplications.size(); ++i) {
@@ -158,6 +151,11 @@ bool ApplicationManager::add(ApplicationInfo *application) {
158151 QModelIndex appIndex = findIndex(application);
159152 if (!appIndex.isValid()) return;
160153 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << ApplicationManager::RoleFocused);
154+ XDEBUG_MSG("focusedApplicationId = " << focusedApplicationId());
155+ Q_EMIT focusedApplicationIdChanged();
156+ if (application->focused()) {
157+ raiseApp(application->appId());
158+ }
161159 });
162160 connect(application, &ApplicationInfo::stateChanged, this, [application, this]() {
163161 QModelIndex appIndex = findIndex(application);
@@ -181,15 +179,40 @@ bool ApplicationManager::add(ApplicationInfo *application) {
181179
182180void ApplicationManager::remove(ApplicationInfo *application) {
183181 int i = m_runningApplications.indexOf(application);
182+ application->disconnect(this);
184183 if (i != -1) {
185184 DEBUG_MSG(application->appId());
185+ Q_ASSERT(!m_modelBusy);
186+ m_modelBusy = true;
186187 beginRemoveRows(QModelIndex(), i, i);
187188 m_runningApplications.removeAt(i);
188189 endRemoveRows();
190+ m_modelBusy = false;
189191 Q_EMIT countChanged();
190192 if (isEmpty()) Q_EMIT emptyChanged(isEmpty());
193+ DEBUG_MSG(application->appId() << " after: " << qPrintable(toString()));
194+ }
195+}
196+
197+void ApplicationManager::raiseApp(const QString &appId)
198+{
199+
200+ int index = -1;
201+ for (int i = 0; i < m_runningApplications.count() && index == -1; ++i) {
202+ if (m_runningApplications[i]->appId() == appId) {
203+ index = i;
204+ }
205+ }
206+
207+ if (index >= 0) {
208+ if (m_modelBusy) {
209+ DEBUG_MSG(appId << " - model busy. Try again later.");
210+ QMetaObject::invokeMethod(this, "raiseApp", Qt::QueuedConnection, Q_ARG(QString, appId));
211+ } else {
212+ DEBUG_MSG(appId);
213+ move(index, 0);
214+ }
191215 }
192− application->disconnect(this);
193216}
194217
195218void ApplicationManager::move(int from, int to) {
@@ -197,12 +220,15 @@ void ApplicationManager::move(int from, int to) {
197220
198221 if (from >= 0 && from < m_runningApplications.size() && to >= 0 && to < m_runningApplications.size()) {
199222 QModelIndex parent;
223+ Q_ASSERT(!m_modelBusy);
224+ m_modelBusy = true;
200225 /* When moving an item down, the destination index needs to be incremented
201226 * by one, as explained in the documentation:
202227 * http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
203228 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
204229 m_runningApplications.move(from, to);
205230 endMoveRows();
231+ m_modelBusy = false;
206232 }
207233}
208234
@@ -224,8 +250,6 @@ ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
224250 if (!application)
225251 return nullptr;
226252
227− Q_EMIT application->focusRequested(); // we assume that an application that's starting up wants focus
228−
229253 return application;
230254}
231255
@@ -473,47 +497,52 @@ bool ApplicationManager::isEmpty() const
473497 return m_runningApplications.isEmpty();
474498}
475499
476−void ApplicationManager::updateFocusedApplication()
500+ApplicationInfo *ApplicationManager::findApplication(MirSurface* surface)
477501{
478− ApplicationInfo *focusedApplication = nullptr;
479− ApplicationInfo *previouslyFocusedApplication = nullptr;
480−
481− auto controller = MirFocusController::instance();
482− if (!controller) {
483− return;
502+ for (ApplicationInfo *app : m_runningApplications) {
503+ auto surfaceList = static_cast<MirSurfaceListModel*>(app->surfaceList());
504+ if (surfaceList->contains(surface)) {
505+ return app;
506+ }
484507 }
508+ return nullptr;
509+}
485510
486− MirSurface *surface = static_cast<MirSurface*>(controller->focusedSurface());
487− if (surface) {
488− focusedApplication = findApplication(surface);
489− }
511+QString ApplicationManager::toString()
512+{
513+ QString str;
514+ for (int i = 0; i < m_runningApplications.count(); ++i) {
515+ auto *application = m_runningApplications.at(i);
490516
491− surface = static_cast<MirSurface*>(controller->previouslyFocusedSurface());
492− if (surface) {
493− previouslyFocusedApplication = findApplication(surface);
494− }
517+ QString itemStr = QString("(index=%1,appId=%2)")
518+ .arg(i)
519+ .arg(application->appId());
495520
496− if (focusedApplication != previouslyFocusedApplication) {
497− if (focusedApplication) {
498− DEBUG_MSG("focused " << focusedApplication->appId());
499− Q_EMIT focusedApplication->focusedChanged(true);
500− this->move(this->m_runningApplications.indexOf(focusedApplication), 0);
521+ if (i > 0) {
522+ str.append(",");
501523 }
502− if (previouslyFocusedApplication) {
503− DEBUG_MSG("unfocused " << previouslyFocusedApplication->appId());
504− Q_EMIT previouslyFocusedApplication->focusedChanged(false);
505− }
506− Q_EMIT focusedApplicationIdChanged();
524+ str.append(itemStr);
507525 }
526+ return str;
508527}
509528
510−ApplicationInfo *ApplicationManager::findApplication(MirSurface* surface)
529+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
530+// ApplicationManagerNotifier
531+
532+ApplicationManagerNotifier *ApplicationManagerNotifier::m_instance = nullptr;
533+
534+ApplicationManagerNotifier *ApplicationManagerNotifier::instance()
511535{
512− for (ApplicationInfo *app : m_runningApplications) {
513− auto surfaceList = static_cast<MirSurfaceListModel*>(app->surfaceList());
514− if (surfaceList->contains(surface)) {
515− return app;
516− }
536+ if (!m_instance) {
537+ m_instance = new ApplicationManagerNotifier;
538+ }
539+ return m_instance;
540+}
541+
542+void ApplicationManagerNotifier::setApplicationManager(ApplicationManager *appMan)
543+{
544+ if (appMan != m_applicationManager) {
545+ m_applicationManager = appMan;
546+ Q_EMIT applicationManagerChanged(m_applicationManager);
517547 }
518− return nullptr;
519548}
tests/mocks/Unity/Application/ApplicationManager.h+31−4
@@ -50,8 +50,11 @@ class ApplicationManager : public ApplicationManagerInterface {
5050 // QAbstractItemModel methods.
5151 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
5252 QVariant data(const QModelIndex& index, int role) const override;
53+
54+ // ApplicationManagerInterface methods
5355 Q_INVOKABLE ApplicationInfo *get(int index) const override;
5456 Q_INVOKABLE ApplicationInfo *findApplication(const QString &appId) const override;
57+ unity::shell::application::ApplicationInfoInterface *findApplicationWithSurface(unity::shell::application::MirSurfaceInterface* surface) const override;
5558
5659 Q_INVOKABLE void move(int from, int to);
5760
@@ -76,18 +79,42 @@ class ApplicationManager : public ApplicationManagerInterface {
7679 void availableApplicationsChanged(QStringList list);
7780
7881 private Q_SLOTS:
79− void onWindowCreatedTimerTimeout();
80− void updateFocusedApplication();
82+ void raiseApp(const QString &appId);
8183
8284 private:
8385 bool add(ApplicationInfo *application);
8486 void remove(ApplicationInfo* application);
8587 void buildListOfAvailableApplications();
86− void onWindowCreated();
88+ QString toString();
8789 ApplicationInfo *findApplication(MirSurface* surface);
8890 QList<ApplicationInfo*> m_runningApplications;
8991 QList<ApplicationInfo*> m_availableApplications;
90− QTimer m_windowCreatedTimer;
92+ bool m_modelBusy{false};
93+};
94+
95+/*
96+ Lifecycle of the ApplicationManager instance belongs to the QML plugin.
97+ So this guy here is used to notify other parts of the system when the plugin creates and destroys
98+ the ApplicationManager.
99+
100+ Unlike ApplicationManager, we create ApplicationManagerNotifier whenever we want.
101+ */
102+class ApplicationManagerNotifier : public QObject {
103+ Q_OBJECT
104+public:
105+ static ApplicationManagerNotifier *instance();
106+
107+ ApplicationManager *applicationManager() { return m_applicationManager; }
108+
109+Q_SIGNALS:
110+ void applicationManagerChanged(ApplicationManager *applicationManager);
111+
112+private:
113+ void setApplicationManager(ApplicationManager *);
114+ static ApplicationManagerNotifier *m_instance;
115+ ApplicationManager *m_applicationManager{nullptr};
116+
117+friend class ApplicationManager;
91118};
92119
93120Q_DECLARE_METATYPE(ApplicationManager*)
tests/mocks/Unity/Application/CMakeLists.txt+1−1
@@ -15,7 +15,7 @@ set(FakeUnityApplicationQml_SOURCES
1515 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
1616 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceItemInterface.h
1717 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
18− ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirFocusControllerInterface.h
18+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
1919 resources/surfaces.qrc
2020)
2121
tests/mocks/Unity/Application/MirSurface.cpp+81−94
@@ -19,14 +19,18 @@
1919#include <QDebug>
2020#include <QQmlEngine>
2121
22+// local
23+#include "SurfaceManager.h"
24+
2225#define MIRSURFACE_DEBUG 0
2326
2427#if MIRSURFACE_DEBUG
2528#define DEBUG_MSG(params) qDebug().nospace() << "MirSurface[" << (void*)this << "," << m_name << "]::" << __func__ << " " << params
29+#define XDEBUG_MSG(params) qDebug().nospace() << "MirSurface[" << (void*)this << "," << m_name << "]::" << params
2630
2731const char *stateToStr(Mir::State state)
2832{
29− switch(state) {
33+ switch (state) {
3034 case Mir::UnknownState:
3135 return "unknown";
3236 case Mir::RestoredState:
@@ -36,26 +40,37 @@ const char *stateToStr(Mir::State state)
3640 case Mir::MaximizedState:
3741 return "maximized";
3842 case Mir::VertMaximizedState:
39− return "vert-maximized";
43+ return "vertMaximized";
4044 case Mir::FullscreenState:
4145 return "fullscreen";
4246 case Mir::HorizMaximizedState:
43− return "horiz-maximized";
47+ return "horizMaximized";
48+ case Mir::MaximizedLeftState:
49+ return "maximizedLeft";
50+ case Mir::MaximizedRightState:
51+ return "maximizedRight";
52+ case Mir::MaximizedTopLeftState:
53+ return "maximizedTopLeft";
54+ case Mir::MaximizedTopRightState:
55+ return "maximizedTopRight";
56+ case Mir::MaximizedBottomLeftState:
57+ return "maximizedBottomLeft";
58+ case Mir::MaximizedBottomRightState:
59+ return "maximizedBottomRight";
4460 case Mir::HiddenState:
4561 return "hidden";
4662 default:
4763 return "???";
48− };
64+ }
4965}
5066
5167#else
5268#define DEBUG_MSG(params) ((void)0)
69+#define XDEBUG_MSG(params) ((void)0)
5370#endif
5471
5572using namespace unity::shell::application;
5673
57−MirFocusController *MirFocusController::m_instance = nullptr;
58−
5974MirSurface::MirSurface(const QString& name,
6075 Mir::Type type,
6176 Mir::State state,
@@ -69,14 +84,14 @@ MirSurface::MirSurface(const QString& name,
6984 , m_screenshotUrl(screenshot)
7085 , m_qmlFilePath(qmlFilePath)
7186 , m_live(true)
72− , m_visible(true)
87+ , m_focused(false)
7388 , m_activeFocus(false)
7489 , m_width(-1)
7590 , m_height(-1)
7691 , m_slowToResize(false)
7792 , m_shellChrome(Mir::NormalChrome)
7893{
79− DEBUG_MSG("");
94+ DEBUG_MSG("state=" << stateToStr(state));
8095
8196 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
8297
@@ -95,11 +110,8 @@ MirSurface::~MirSurface()
95110{
96111 DEBUG_MSG("");
97112
98− // controller instance might have been already destroyed by QQmlEngine destructor
99− auto controller = MirFocusController::instance();
100− if (controller && controller->focusedSurface() == this) {
101− controller->clear();
102− }
113+ // Early warning, while MirSurface methods can still be accessed.
114+ Q_EMIT destroyed(this);
103115}
104116
105117QString MirSurface::name() const
@@ -122,14 +134,31 @@ Mir::State MirSurface::state() const
122134 return m_state;
123135}
124136
125−void MirSurface::setState(Mir::State state)
137+void MirSurface::requestState(Mir::State state)
126138{
127− if (state == m_state)
139+ if (state == m_state) {
128140 return;
141+ }
142+ DEBUG_MSG(stateToStr(state));
143+ Q_EMIT stateRequested(state);
144+}
129145
146+void MirSurface::setState(Mir::State state)
147+{
148+ if (state == m_state) {
149+ return;
150+ }
130151 DEBUG_MSG(stateToStr(state));
152+
153+ bool oldVisible = visible();
154+
131155 m_state = state;
132156 Q_EMIT stateChanged(state);
157+
158+ if (visible() != oldVisible) {
159+ XDEBUG_MSG("visibleChanged("<<visible()<<")");
160+ Q_EMIT visibleChanged(visible());
161+ }
133162}
134163
135164bool MirSurface::live() const
@@ -139,7 +168,7 @@ bool MirSurface::live() const
139168
140169bool MirSurface::visible() const
141170{
142− return m_visible;
171+ return m_state != Mir::MinimizedState && m_state != Mir::HiddenState;
143172}
144173
145174void MirSurface::setLive(bool live)
@@ -231,31 +260,30 @@ void MirSurface::unregisterView(qintptr viewId)
231260 if (!m_live && m_views.count() == 0) {
232261 deleteLater();
233262 }
234− updateVisibility();
263+ updateExposure();
235264}
236265
237−void MirSurface::setViewVisibility(qintptr viewId, bool visible)
266+void MirSurface::setViewExposure(qintptr viewId, bool visible)
238267{
239268 if (!m_views.contains(viewId)) return;
240269
241270 m_views[viewId].visible = visible;
242− updateVisibility();
271+ updateExposure();
243272}
244273
245−void MirSurface::updateVisibility()
274+void MirSurface::updateExposure()
246275{
247− bool newVisible = false;
276+ bool newExposure = false;
248277 QHashIterator<qintptr, View> i(m_views);
249278 while (i.hasNext()) {
250279 i.next();
251− newVisible |= i.value().visible;
280+ newExposure |= i.value().visible;
252281 }
253282
254− if (newVisible != visible()) {
255−// qDebug().nospace() << "MirSurface[" << name() << "]::updateVisibility(" << newVisible << ")";
256−
257− m_visible = newVisible;
258− Q_EMIT visibleChanged(m_visible);
283+ if (newExposure != m_exposed) {
284+ m_exposed = newExposure;
285+ DEBUG_MSG(m_exposed);
286+ Q_EMIT exposedChanged(m_exposed);
259287 updateInputBoundsAfterResize();
260288 }
261289}
@@ -273,6 +301,10 @@ void MirSurface::setActiveFocus(bool value)
273301 m_activeFocus = value;
274302
275303 Q_EMIT activeFocusChanged(value);
304+
305+ if (m_activeFocus && !m_focused) {
306+ requestFocus();
307+ }
276308}
277309
278310int MirSurface::width() const
@@ -332,6 +364,7 @@ void MirSurface::doResize(int width, int height)
332364 }
333365
334366 if (changed) {
367+ XDEBUG_MSG("sizeChanged(width="<<width<<", height="<<height<<")");
335368 Q_EMIT sizeChanged(QSize(width, height));
336369 }
337370
@@ -409,11 +442,6 @@ void MirSurface::setHeightIncrement(int value)
409442 }
410443}
411444
412−void MirSurface::raise()
413−{
414− Q_EMIT raiseRequested();
415−}
416−
417445void MirSurface::close()
418446{
419447 DEBUG_MSG("");
@@ -423,6 +451,12 @@ void MirSurface::close()
423451 }
424452}
425453
454+void MirSurface::activate()
455+{
456+ DEBUG_MSG("");
457+ SurfaceManager::instance()->activate(this);
458+}
459+
426460void MirSurface::requestFocus()
427461{
428462 DEBUG_MSG("");
@@ -431,27 +465,18 @@ void MirSurface::requestFocus()
431465
432466void MirSurface::setFocused(bool value)
433467{
434− DEBUG_MSG(value);
435−
436− auto controller = MirFocusController::instance();
437− // controller instance might have been already destroyed by QQmlEngine destructor
438− if (!controller) {
468+ if (m_focused == value)
439469 return;
440− }
441470
442− if (value) {
443− controller->setFocusedSurface(this);
444− } else if (controller->focusedSurface() == this) {
445− controller->setFocusedSurface(nullptr);
446− }
471+ DEBUG_MSG("(" << value << ")");
472+
473+ m_focused = value;
474+ Q_EMIT focusedChanged(value);
447475}
448476
449477bool MirSurface::focused() const
450478{
451− auto controller = MirFocusController::instance();
452−
453− // controller instance might have been already destroyed by QQmlEngine destructor
454− return controller ? controller->focusedSurface() == this : false;
479+ return m_focused;
455480}
456481
457482QRect MirSurface::inputBounds() const
@@ -463,58 +488,20 @@ void MirSurface::setInputBounds(const QRect &boundsRect)
463488{
464489 if (boundsRect != m_inputBounds) {
465490 m_inputBounds = boundsRect;
491+ DEBUG_MSG("(" << m_inputBounds << ")");
466492 Q_EMIT inputBoundsChanged(m_inputBounds);
467493 }
468494}
469−#if MIRSURFACE_DEBUG
470−#undef DEBUG_MSG
471−#define DEBUG_MSG(params) qDebug().nospace() << "MirFocusController::" << __func__ << " " << params
472−#endif
473495
474−void MirFocusController::setFocusedSurface(MirSurfaceInterface *surface)
496+void MirSurface::setRequestedPosition(const QPoint &value)
475497{
476− if (m_focusedSurface == surface) {
477− return;
478− }
479− DEBUG_MSG("MirSurface[" << (void*)surface << "," << (surface?surface->name():"") << "]");
480−
481− m_previouslyFocusedSurface = m_focusedSurface;
482− m_focusedSurface = surface;
483−
484− if (m_previouslyFocusedSurface != m_focusedSurface) {
485− Q_EMIT focusedSurfaceChanged();
486− }
487−
488− if (m_previouslyFocusedSurface) {
489− Q_EMIT m_previouslyFocusedSurface->focusedChanged(false);
490− }
498+ if (value != m_requestedPosition) {
499+ m_requestedPosition = value;
500+ Q_EMIT requestedPositionChanged(value);
491501
492− if (m_focusedSurface) {
493− Q_EMIT m_focusedSurface->focusedChanged(true);
494− m_focusedSurface->raise();
502+ // fake-miral: always comply
503+ m_position = m_requestedPosition;
504+ XDEBUG_MSG("positionChanged("<<m_position<<")");
505+ Q_EMIT positionChanged(m_position);
495506 }
496507}
497−
498−MirFocusController* MirFocusController::instance()
499−{
500− return m_instance;
501−}
502−
503−MirFocusController::MirFocusController()
504−{
505− DEBUG_MSG("");
506− Q_ASSERT(m_instance == nullptr);
507− m_instance = this;
508−}
509−
510−MirFocusController::~MirFocusController()
511−{
512− Q_ASSERT(m_instance == this);
513− m_instance = nullptr;
514−}
515−
516−void MirFocusController::clear()
517−{
518− m_focusedSurface = m_previouslyFocusedSurface = nullptr;
519− Q_EMIT focusedSurfaceChanged();
520−}
tests/mocks/Unity/Application/MirSurface.h+29−27
@@ -23,31 +23,12 @@
2323#include <QHash>
2424
2525// unity-api
26−#include <unity/shell/application/MirFocusControllerInterface.h>
2726#include <unity/shell/application/MirSurfaceInterface.h>
2827
2928#include "MirSurfaceListModel.h"
3029
3130class MirSurface;
3231
33−class MirFocusController : public unity::shell::application::MirFocusControllerInterface
34−{
35− Q_OBJECT
36−public:
37− MirFocusController();
38− virtual ~MirFocusController();
39− static MirFocusController* instance();
40−
41− void setFocusedSurface(unity::shell::application::MirSurfaceInterface *surface) override;
42− unity::shell::application::MirSurfaceInterface* focusedSurface() const override { return m_focusedSurface; }
43− unity::shell::application::MirSurfaceInterface* previouslyFocusedSurface() { return m_previouslyFocusedSurface; }
44− void clear();
45−private:
46− static MirFocusController *m_instance;
47− unity::shell::application::MirSurfaceInterface* m_previouslyFocusedSurface{nullptr};
48− unity::shell::application::MirSurfaceInterface* m_focusedSurface{nullptr};
49−};
50−
5132class MirSurface : public unity::shell::application::MirSurfaceInterface
5233{
5334 Q_OBJECT
@@ -58,6 +39,7 @@ class MirSurface : public unity::shell::application::MirSurfaceInterface
5839 Q_PROPERTY(int height READ height NOTIFY heightChanged)
5940 Q_PROPERTY(bool activeFocus READ activeFocus NOTIFY activeFocusChanged)
6041 Q_PROPERTY(bool slowToResize READ isSlowToResize WRITE setSlowToResize NOTIFY slowToResizeChanged)
42+ Q_PROPERTY(bool exposed READ exposed NOTIFY exposedChanged)
6143
6244public:
6345 MirSurface(const QString& name,
@@ -76,13 +58,14 @@ public:
7658
7759 QString persistentId() const override;
7860
61+ QPoint position() const override { return m_position; }
62+
7963 QSize size() const override { return QSize(width(),height()); }
8064 void resize(int width, int height) override;
8165 void resize(const QSize &size) override { resize(size.width(), size.height()); }
8266
8367
8468 Mir::State state() const override;
85− Q_INVOKABLE void setState(Mir::State) override;
8669
8770 bool live() const override;
8871
@@ -108,15 +91,16 @@ public:
10891
10992 bool confinesMousePointer() const override { return false; }
11093
111− Q_INVOKABLE void requestFocus() override;
94+ QPoint requestedPosition() const override { return m_requestedPosition; }
95+ void setRequestedPosition(const QPoint &) override;
11296
11397 Q_INVOKABLE void close() override;
114−
115− Q_INVOKABLE void raise() override;
98+ Q_INVOKABLE void activate() override;
11699
117100 ////
118101 // API for tests
119102
103+ Q_INVOKABLE void requestFocus();
120104 Q_INVOKABLE void setLive(bool live);
121105 Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome);
122106
@@ -126,6 +110,8 @@ public:
126110 bool isSlowToResize() const;
127111 void setSlowToResize(bool value);
128112
113+ bool exposed() const { return m_exposed; }
114+
129115 Q_INVOKABLE void setMinimumWidth(int);
130116 Q_INVOKABLE void setMaximumWidth(int);
131117 Q_INVOKABLE void setMinimumHeight(int);
@@ -148,24 +134,35 @@ public:
148134
149135 void registerView(qintptr viewId);
150136 void unregisterView(qintptr viewId);
151− void setViewVisibility(qintptr viewId, bool visible);
137+ void setViewExposure(qintptr viewId, bool visible);
152138 int viewCount() const { return m_views.count(); }
153139
154140 void setFocused(bool value);
155141
142+ void setState(Mir::State state);
143+
144+ Mir::State previousState() const { return m_previousState; }
145+ void setPreviousState(Mir::State state) { m_previousState = state; }
146+
147+public Q_SLOTS:
148+ ////
149+ // unity.shell.application.MirSurface
150+ void requestState(Mir::State) override;
151+
156152Q_SIGNALS:
157153 ////
158154 // API for tests
159155 void widthChanged();
160156 void heightChanged();
161157 void slowToResizeChanged();
158+ void exposedChanged(bool exposed);
162159
163160 ////
164161 // internal mock stuff
165162 void screenshotUrlChanged(QUrl);
166163 void activeFocusChanged(bool);
167− void raiseRequested();
168164 void closeRequested();
165+ void stateRequested(Mir::State);
169166
170167protected:
171168 virtual void updateInputBoundsAfterResize();
@@ -175,17 +172,18 @@ private Q_SLOTS:
175172
176173private:
177174 void doResize(int width, int height);
178− void updateVisibility();
175+ void updateExposure();
179176
180177 const QString m_name;
181178 const Mir::Type m_type;
182179 Mir::State m_state;
180+ Mir::State m_previousState{Mir::UnknownState};
183181 Mir::OrientationAngle m_orientationAngle;
184182
185183 QUrl m_screenshotUrl;
186184 QUrl m_qmlFilePath;
187185 bool m_live;
188− bool m_visible;
186+ bool m_focused;
189187 bool m_activeFocus;
190188 int m_width;
191189 int m_height;
@@ -210,10 +208,14 @@ private:
210208 bool visible;
211209 };
212210 QHash<qintptr, View> m_views;
211+ bool m_exposed{false};
213212
214213 QTimer m_zombieTimer;
215214
216215 QRect m_inputBounds;
216+
217+ QPoint m_position;
218+ QPoint m_requestedPosition;
217219};
218220
219221#endif // MOCK_MIR_SURFACE_H
tests/mocks/Unity/Application/MirSurfaceItem.cpp+4−4
@@ -59,7 +59,7 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
5959 Qt::ExtraButton12 | Qt::ExtraButton13);
6060
6161 connect(this, &QQuickItem::activeFocusChanged, this, &MirSurfaceItem::updateMirSurfaceActiveFocus);
62− connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceVisibility);
62+ connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceExposure);
6363
6464 connect(this, &MirSurfaceItem::consumesInputChanged, this, [this]() {
6565 updateMirSurfaceActiveFocus(hasActiveFocus());
@@ -278,7 +278,7 @@ void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
278278 m_qmlSurface->registerView((qintptr)this);
279279
280280 updateSurfaceSize();
281− updateMirSurfaceVisibility();
281+ updateMirSurfaceExposure();
282282
283283 if (m_orientationAngle) {
284284 m_qmlSurface->setOrientationAngle(*m_orientationAngle);
@@ -335,11 +335,11 @@ void MirSurfaceItem::updateMirSurfaceActiveFocus(bool focused)
335335 }
336336}
337337
338−void MirSurfaceItem::updateMirSurfaceVisibility()
338+void MirSurfaceItem::updateMirSurfaceExposure()
339339{
340340 if (!m_qmlSurface) return;
341341
342− m_qmlSurface->setViewVisibility((qintptr)this, isVisible());
342+ m_qmlSurface->setViewExposure((qintptr)this, isVisible());
343343}
344344
345345void MirSurfaceItem::setConsumesInput(bool value)
tests/mocks/Unity/Application/MirSurfaceItem.h+1−2
@@ -50,7 +50,6 @@ public:
5050 Mir::ShellChrome shellChrome() const override;
5151
5252 Mir::State surfaceState() const override;
53− void setSurfaceState(Mir::State) override {}
5453
5554 Mir::OrientationAngle orientationAngle() const override;
5655 void setOrientationAngle(Mir::OrientationAngle angle) override;
@@ -102,7 +101,7 @@ protected:
102101private Q_SLOTS:
103102 void onComponentStatusChanged(QQmlComponent::Status status);
104103 void updateScreenshot(QUrl screenshot);
105− void updateMirSurfaceVisibility();
104+ void updateMirSurfaceExposure();
106105 void updateMirSurfaceActiveFocus(bool focused);
107106
108107private:
tests/mocks/Unity/Application/MirSurfaceListModel.cpp+11−33
@@ -21,8 +21,9 @@
2121
2222#define MIRSURFACELISTMODEL_DEBUG 0
2323
24−#ifdef MIRSURFACELISTMODEL_DEBUG
25−#define DEBUG_MSG(params) qDebug().nospace() << "MirSurfaceListModel::" << __func__ << " " << params
24+#if MIRSURFACELISTMODEL_DEBUG
25+#include <QDebug>
26+#define DEBUG_MSG(params) qDebug().nospace() << "MirSurfaceListModel::" << __func__ << params
2627#else
2728#define DEBUG_MSG(params) ((void)0)
2829#endif
@@ -54,26 +55,16 @@ QVariant MirSurfaceListModel::data(const QModelIndex& index, int role) const
5455
5556void MirSurfaceListModel::raise(MirSurface *surface)
5657{
58+ DEBUG_MSG("(" << surface << ")");
5759 int i = m_surfaceList.indexOf(surface);
5860 if (i != -1) {
5961 moveSurface(i, 0);
6062 }
6163}
6264
63−void MirSurfaceListModel::appendSurface(MirSurface *surface)
64−{
65− beginInsertRows(QModelIndex(), m_surfaceList.size(), m_surfaceList.size());
66− m_surfaceList.append(surface);
67− connectSurface(surface);
68− endInsertRows();
69− Q_EMIT countChanged(m_surfaceList.count());
70− if (m_surfaceList.count() == 1) {
71− Q_EMIT firstChanged();
72− }
73−}
74−
75−void MirSurfaceListModel::prependSurface(MirSurface *surface)
65+void MirSurfaceListModel::addSurface(MirSurface *surface)
7666{
67+ DEBUG_MSG("(" << surface << ")");
7768 beginInsertRows(QModelIndex(), 0, 0);
7869 m_surfaceList.prepend(surface);
7970 connectSurface(surface);
@@ -85,6 +76,11 @@ void MirSurfaceListModel::prependSurface(MirSurface *surface)
8576void MirSurfaceListModel::connectSurface(MirSurface *surface)
8677{
8778 connect(surface, &QObject::destroyed, this, [this, surface](){ this->removeSurface(surface); });
79+ connect(surface, &MirSurfaceInterface::focusedChanged, this, [this, surface](bool surfaceFocused){
80+ if (surfaceFocused) {
81+ raise(surface);
82+ }
83+ });
8884}
8985
9086void MirSurfaceListModel::removeSurface(MirSurface *surface)
@@ -138,21 +134,3 @@ const MirSurfaceInterface *MirSurfaceListModel::get(int index) const
138134 return nullptr;
139135 }
140136}
141−
142−MirSurfaceInterface *MirSurfaceListModel::createSurface()
143−{
144− QStringList screenshotIds = {"gallery", "map", "facebook", "camera", "browser", "music", "twitter"};
145− int i = rand() % screenshotIds.count();
146−
147− QUrl screenshotUrl = QString("qrc:///Unity/Application/screenshots/%1@12.png")
148− .arg(screenshotIds[i]);
149−
150− auto surface = new MirSurface(QString("prompt foo"),
151− Mir::NormalType,
152− Mir::RestoredState,
153− screenshotUrl);
154−
155− prependSurface(surface);
156−
157− return surface;
158−}
tests/mocks/Unity/Application/MirSurfaceListModel.h+2−7
@@ -38,18 +38,13 @@ public:
3838 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
3939 QVariant data(const QModelIndex& index, int role) const override;
4040
41− void appendSurface(MirSurface *surface);
41+ void addSurface(MirSurface *surface);
4242 void removeSurface(MirSurface *surface);
4343
4444 bool contains(MirSurface *surface) const { return m_surfaceList.contains(surface); }
4545
46− ////
47− // API for tests
48−
49− Q_INVOKABLE unity::shell::application::MirSurfaceInterface *createSurface();
50−
5146private:
52− void prependSurface(MirSurface *surface);
47+ void appendSurface(MirSurface *surface);
5348 void raise(MirSurface *surface);
5449 void moveSurface(int from, int to);
5550 void connectSurface(MirSurface *surface);
tests/mocks/Unity/Application/SurfaceManager.cpp+170−24
@@ -21,33 +21,41 @@
2121
2222#include <paths.h>
2323
24−SurfaceManager *SurfaceManager::the_surface_manager = nullptr;
24+#define SURFACEMANAGER_DEBUG 0
25+
26+#if SURFACEMANAGER_DEBUG
27+#define DEBUG_MSG(params) qDebug().nospace() << "SurfaceManager[" << (void*)this << "]::" << __func__ << params
28+#else
29+#define DEBUG_MSG(params) ((void)0)
30+#endif
31+
32+namespace unityapi = unity::shell::application;
33+
34+SurfaceManager *SurfaceManager::m_instance = nullptr;
2535
2636SurfaceManager *SurfaceManager::instance()
2737{
28− return the_surface_manager;
38+ return m_instance;
2939}
3040
31−SurfaceManager::SurfaceManager(QObject *parent) :
32− QObject(parent)
33− , m_virtualKeyboard(nullptr)
41+SurfaceManager::SurfaceManager(QObject *)
3442{
35− Q_ASSERT(the_surface_manager == nullptr);
36− the_surface_manager = this;
43+ DEBUG_MSG("");
3744
38− m_virtualKeyboard = new VirtualKeyboard;
39− connect(m_virtualKeyboard, &QObject::destroyed, this, [this](QObject *obj) {
40− MirSurface* surface = qobject_cast<MirSurface*>(obj);
41− m_virtualKeyboard = nullptr;
42− Q_EMIT inputMethodSurfaceChanged();
43− Q_EMIT surfaceDestroyed(surface);
44− });
45+ Q_ASSERT(m_instance == nullptr);
46+ m_instance = this;
4547}
4648
4749SurfaceManager::~SurfaceManager()
4850{
49− Q_ASSERT(the_surface_manager == this);
50− the_surface_manager = nullptr;
51+ DEBUG_MSG("");
52+
53+ if (m_virtualKeyboard) {
54+ m_virtualKeyboard->setLive(false);
55+ }
56+
57+ Q_ASSERT(m_instance == this);
58+ m_instance = nullptr;
5159}
5260
5361MirSurface *SurfaceManager::createSurface(const QString& name,
@@ -56,10 +64,13 @@ MirSurface *SurfaceManager::createSurface(const QString& name,
5664 const QUrl& screenshot)
5765{
5866 MirSurface* surface = new MirSurface(name, type, state, screenshot);
59− connect(surface, &QObject::destroyed, this, [this](QObject *obj) {
60− MirSurface* surface = qobject_cast<MirSurface*>(obj);
61− Q_EMIT surfaceDestroyed(surface);
62− });
67+ registerSurface(surface);
68+ return surface;
69+}
70+
71+void SurfaceManager::registerSurface(MirSurface *surface)
72+{
73+ m_surfaces.prepend(surface);
6374
6475 surface->setMinimumWidth(m_newSurfaceMinimumWidth);
6576 surface->setMaximumWidth(m_newSurfaceMaximumWidth);
@@ -68,13 +79,18 @@ MirSurface *SurfaceManager::createSurface(const QString& name,
6879 surface->setWidthIncrement(m_newSurfaceWidthIncrement);
6980 surface->setHeightIncrement(m_newSurfaceHeightIncrement);
7081
71− Q_EMIT surfaceCreated(surface);
72− return surface;
82+ connect(surface, &MirSurface::stateRequested, this, [=](Mir::State state) {
83+ this->onStateRequested(surface, state);
84+ });
85+
86+ connect(surface, &QObject::destroyed, this, [=]() {
87+ this->onSurfaceDestroyed(surface);
88+ });
7389}
7490
75−MirSurface *SurfaceManager::inputMethodSurface() const
91+void SurfaceManager::notifySurfaceCreated(unityapi::MirSurfaceInterface *surface)
7692{
77− return m_virtualKeyboard;
93+ Q_EMIT surfaceCreated(surface);
7894}
7995
8096void SurfaceManager::setNewSurfaceMinimumWidth(int value)
@@ -124,3 +140,133 @@ void SurfaceManager::setNewSurfaceHeightIncrement(int value)
124140 Q_EMIT newSurfaceHeightIncrementChanged(m_newSurfaceHeightIncrement);
125141 }
126142}
143+
144+void SurfaceManager::raise(unityapi::MirSurfaceInterface *surface)
145+{
146+ if (m_underModification)
147+ return;
148+
149+ DEBUG_MSG("("<<surface<<") started");
150+ Q_EMIT modificationsStarted();
151+ m_underModification = true;
152+
153+ doRaise(surface);
154+
155+ m_underModification = false;
156+ Q_EMIT modificationsEnded();
157+ DEBUG_MSG("("<<surface<<") ended");
158+}
159+
160+void SurfaceManager::doRaise(unityapi::MirSurfaceInterface *apiSurface)
161+{
162+ auto surface = static_cast<MirSurface*>(apiSurface);
163+ int index = m_surfaces.indexOf(surface);
164+ Q_ASSERT(index != -1);
165+ m_surfaces.move(index, 0);
166+
167+ Q_EMIT surfacesRaised({surface});
168+}
169+
170+void SurfaceManager::activate(unityapi::MirSurfaceInterface *apiSurface)
171+{
172+ auto surface = static_cast<MirSurface*>(apiSurface);
173+
174+ if (surface == m_focusedSurface) {
175+ return;
176+ }
177+
178+ Q_ASSERT(!m_underModification);
179+
180+ DEBUG_MSG("("<<surface<<") started");
181+ Q_EMIT modificationsStarted();
182+ m_underModification = true;
183+ if (m_focusedSurface) {
184+ m_focusedSurface->setFocused(false);
185+ }
186+ if (surface) {
187+ if (surface->state() == Mir::HiddenState || surface->state() == Mir::MinimizedState) {
188+ if (surface->previousState() != Mir::UnknownState) {
189+ surface->setState(surface->previousState());
190+ } else {
191+ surface->setState(Mir::RestoredState);
192+ }
193+ }
194+ surface->setFocused(true);
195+ doRaise(surface);
196+ }
197+ m_focusedSurface = surface;
198+ m_underModification = false;
199+ Q_EMIT modificationsEnded();
200+ DEBUG_MSG("("<<surface<<") ended");
201+}
202+
203+void SurfaceManager::onStateRequested(MirSurface *surface, Mir::State state)
204+{
205+ Q_ASSERT(!m_underModification);
206+
207+ DEBUG_MSG("("<<surface<<","<<state<<") started");
208+ Q_EMIT modificationsStarted();
209+ m_underModification = true;
210+
211+ surface->setPreviousState(surface->state());
212+ surface->setState(state);
213+
214+ if ((state == Mir::MinimizedState || state == Mir::HiddenState) && surface->focused()) {
215+ Q_ASSERT(m_focusedSurface == surface);
216+ surface->setFocused(false);
217+ m_focusedSurface = nullptr;
218+ focusFirstAvailableSurface();
219+ }
220+
221+ m_underModification = false;
222+ Q_EMIT modificationsEnded();
223+ DEBUG_MSG("("<<surface<<","<<state<<") ended");
224+}
225+
226+void SurfaceManager::onSurfaceDestroyed(MirSurface *surface)
227+{
228+ m_surfaces.removeAll(surface);
229+ if (m_focusedSurface == surface) {
230+ m_focusedSurface = nullptr;
231+
232+ Q_EMIT modificationsStarted();
233+ m_underModification = true;
234+
235+ focusFirstAvailableSurface();
236+
237+ m_underModification = false;
238+ Q_EMIT modificationsEnded();
239+ }
240+}
241+
242+void SurfaceManager::focusFirstAvailableSurface()
243+{
244+ MirSurface *chosenSurface = nullptr;
245+ for (int i = 0; i < m_surfaces.count() && !chosenSurface; ++i) {
246+ auto *surface = m_surfaces[i];
247+ if (surface->state() != Mir::HiddenState && surface->state() != Mir::MinimizedState) {
248+ chosenSurface = surface;
249+ }
250+ }
251+
252+ if (!chosenSurface) {
253+ return;
254+ }
255+
256+ if (m_focusedSurface) {
257+ m_focusedSurface->setFocused(false);
258+ }
259+ if (chosenSurface) {
260+ chosenSurface->setFocused(true);
261+ }
262+ m_focusedSurface = chosenSurface;
263+}
264+
265+void SurfaceManager::createInputMethodSurface()
266+{
267+ if (!m_virtualKeyboard) {
268+ m_virtualKeyboard = new VirtualKeyboard;
269+ registerSurface(m_virtualKeyboard);
270+ Q_EMIT surfaceCreated(m_virtualKeyboard);
271+ }
272+}
tests/mocks/Unity/Application/SurfaceManager.h+28−10
@@ -19,15 +19,16 @@
1919
2020#include <QObject>
2121
22+#include <unity/shell/application/SurfaceManagerInterface.h>
23+
2224#include "MirSurface.h"
2325#include "VirtualKeyboard.h"
2426
2527class ApplicationInfo;
2628
27−class SurfaceManager : public QObject
29+class SurfaceManager : public unity::shell::application::SurfaceManagerInterface
2830{
2931 Q_OBJECT
30− Q_PROPERTY(MirSurface* inputMethodSurface READ inputMethodSurface NOTIFY inputMethodSurfaceChanged)
3132 Q_PROPERTY(int newSurfaceMinimumWidth READ newSurfaceMinimumWidth WRITE setNewSurfaceMinimumWidth NOTIFY newSurfaceMinimumWidthChanged)
3233 Q_PROPERTY(int newSurfaceMaximumWidth READ newSurfaceMaximumWidth WRITE setNewSurfaceMaximumWidth NOTIFY newSurfaceMaximumWidthChanged)
3334 Q_PROPERTY(int newSurfaceMinimumHeight READ newSurfaceMinimumHeight WRITE setNewSurfaceMinimumHeight NOTIFY newSurfaceMinimumHeightChanged)
@@ -41,12 +42,17 @@ public:
4142
4243 static SurfaceManager *instance();
4344
45+ // SurfaceManagerInterface
46+ void raise(unity::shell::application::MirSurfaceInterface *surface) override;
47+ void activate(unity::shell::application::MirSurfaceInterface *surface) override;
48+
4449 Q_INVOKABLE MirSurface* createSurface(const QString& name,
4550 Mir::Type type,
4651 Mir::State state,
4752 const QUrl& screenshot);
4853
49− MirSurface* inputMethodSurface() const;
54+
55+ void notifySurfaceCreated(unity::shell::application::MirSurfaceInterface *);
5056
5157 int newSurfaceMinimumWidth() const { return m_newSurfaceMinimumWidth; }
5258 void setNewSurfaceMinimumWidth(int value);
@@ -66,12 +72,10 @@ public:
6672 int newSurfaceHeightIncrement() const { return m_newSurfaceHeightIncrement; }
6773 void setNewSurfaceHeightIncrement(int);
6874
69−Q_SIGNALS:
70− void inputMethodSurfaceChanged();
71− void countChanged();
72− void surfaceCreated(MirSurface *surface);
73− void surfaceDestroyed(MirSurface*surface);
75+public Q_SLOTS:
76+ void createInputMethodSurface();
7477
78+Q_SIGNALS:
7579 void newSurfaceMinimumWidthChanged(int value);
7680 void newSurfaceMaximumWidthChanged(int value);
7781 void newSurfaceMinimumHeightChanged(int value);
@@ -79,9 +83,16 @@ Q_SIGNALS:
7983 void newSurfaceWidthIncrementChanged(int value);
8084 void newSurfaceHeightIncrementChanged(int value);
8185
86+private Q_SLOTS:
87+ void onStateRequested(MirSurface *surface, Mir::State state);
88+ void onSurfaceDestroyed(MirSurface *surface);
89+
8290private:
83− static SurfaceManager *the_surface_manager;
84− VirtualKeyboard *m_virtualKeyboard;
91+ void doRaise(unity::shell::application::MirSurfaceInterface *surface);
92+ void focusFirstAvailableSurface();
93+ void registerSurface(MirSurface *surface);
94+
95+ static SurfaceManager *m_instance;
8596
8697 int m_newSurfaceMinimumWidth{0};
8798 int m_newSurfaceMaximumWidth{0};
@@ -89,6 +100,13 @@ private:
89100 int m_newSurfaceMaximumHeight{0};
90101 int m_newSurfaceWidthIncrement{1};
91102 int m_newSurfaceHeightIncrement{1};
103+
104+ MirSurface *m_focusedSurface{nullptr};
105+ bool m_underModification{false};
106+
107+ QList<MirSurface*> m_surfaces;
108+
109+ VirtualKeyboard *m_virtualKeyboard{nullptr};
92110};
93111
94112#endif // SURFACEMANAGER_H
tests/mocks/Unity/Application/plugin.cpp+3−33
@@ -29,45 +29,15 @@
2929
3030namespace {
3131
32−// Creates the singletons that are called throughout C++ code
33−void createUnityApplicationSharedSingletons()
34−{
35− // they have to be created in a specific order
36− if (!MirFocusController::instance()) {
37− new MirFocusController;
38− }
39− if (!SurfaceManager::instance()) {
40− new SurfaceManager;
41− }
42− if (!MirMock::instance()) {
43− new MirMock;
44− }
45−}
46−
4732QObject* applicationManagerSingleton(QQmlEngine*, QJSEngine*)
4833{
49− createUnityApplicationSharedSingletons();
5034 return new ApplicationManager;
5135}
5236
5337QObject* mirSingleton(QQmlEngine*, QJSEngine*)
5438{
55− createUnityApplicationSharedSingletons();
56− return MirMock::instance();
57−}
58−
59−QObject* surfaceManagerSingleton(QQmlEngine*, QJSEngine*)
60−{
61− createUnityApplicationSharedSingletons();
62− return SurfaceManager::instance();
39+ return new MirMock;
6340}
64−
65−QObject* mirFocusControllerSingleton(QQmlEngine*, QJSEngine*)
66−{
67− createUnityApplicationSharedSingletons();
68− return MirFocusController::instance();
69−}
70−
7141} // anonymous namespace
7242
7343void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
@@ -75,6 +45,7 @@ void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
7545 qRegisterMetaType<ApplicationInfo*>("ApplicationInfo*");
7646 qRegisterMetaType<unity::shell::application::MirSurfaceInterface*>("unity::shell::application::MirSurfaceInterface*");
7747 qRegisterMetaType<unity::shell::application::MirSurfaceListInterface*>("unity::shell::application::MirSurfaceListInterface*");
48+ qRegisterMetaType<unity::shell::application::SurfaceManagerInterface*>("unity::shell::application::SurfaceManagerInterface*");
7849 qRegisterMetaType<Mir::Type>("Mir::Type");
7950 qRegisterMetaType<Mir::State>("Mir::State");
8051
@@ -83,13 +54,12 @@ void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
8354 qmlRegisterUncreatableType<MirSurface>(uri, 0, 1, "MirSurface", "MirSurface can't be instantiated from QML");
8455 qmlRegisterUncreatableType<unity::shell::application::MirSurfaceInterface>(
8556 uri, 0, 1, "MirSurface", "MirSurface can't be instantiated from QML");
86− qmlRegisterSingletonType<MirFocusController>(uri, 0, 1, "MirFocusController", mirFocusControllerSingleton);
8757 qmlRegisterType<MirSurfaceItem>(uri, 0, 1, "MirSurfaceItem");
8858 qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo");
8959
9060 qmlRegisterSingletonType<ApplicationManager>(uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
9161 qmlRegisterSingletonType<MirMock>(uri, 0, 1, "Mir", mirSingleton);
92− qmlRegisterSingletonType<SurfaceManager>(uri, 0, 1, "SurfaceManager", surfaceManagerSingleton);
62+ qmlRegisterType<SurfaceManager>(uri, 0, 1, "SurfaceManager");
9363}
9464
9565void FakeUnityApplicationQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
tests/mocks/Utils/windowstatestorage.cpp+23
@@ -75,3 +75,26 @@ WindowStateStorage::WindowState WindowStateStorage::getState(const QString &wind
7575 if (!m_state.contains(windowId)) return defaultValue;
7676 return m_state.value(windowId);
7777}
78+
79+Mir::State WindowStateStorage::toMirState(WindowState state) const
80+{
81+ // assumes a single state (not an OR of several)
82+ switch (state) {
83+ case WindowStateMaximized: return Mir::MaximizedState;
84+ case WindowStateMinimized: return Mir::MinimizedState;
85+ case WindowStateFullscreen: return Mir::FullscreenState;
86+ case WindowStateMaximizedLeft: return Mir::MaximizedLeftState;
87+ case WindowStateMaximizedRight: return Mir::MaximizedRightState;
88+ case WindowStateMaximizedHorizontally: return Mir::HorizMaximizedState;
89+ case WindowStateMaximizedVertically: return Mir::VertMaximizedState;
90+ case WindowStateMaximizedTopLeft: return Mir::MaximizedTopLeftState;
91+ case WindowStateMaximizedTopRight: return Mir::MaximizedTopRightState;
92+ case WindowStateMaximizedBottomLeft: return Mir::MaximizedBottomLeftState;
93+ case WindowStateMaximizedBottomRight: return Mir::MaximizedBottomRightState;
94+
95+ case WindowStateNormal:
96+ case WindowStateRestored:
97+ default:
98+ return Mir::RestoredState;
99+ }
100+}
tests/mocks/Utils/windowstatestorage.h+5
@@ -18,6 +18,9 @@
1818#include <QVariantMap>
1919#include <QRect>
2020
21+// unity-api
22+#include <unity/shell/application/Mir.h>
23+
2124class WindowStateStorage: public QObject
2225{
2326 Q_OBJECT
@@ -58,6 +61,8 @@ public:
5861 // Only in the mock, to easily restore a fresh state
5962 Q_INVOKABLE void clear();
6063
64+ Q_INVOKABLE Mir::State toMirState(WindowState state) const;
65+
6166Q_SIGNALS:
6267 void geometryChanged(const QVariantMap& geometry);
6368
tests/mocks/liblightdm/MockController.cpp+8−9
@@ -23,8 +23,6 @@ namespace QLightDM
2323
2424MockController::MockController(QObject *parent)
2525 : QObject(parent)
26− , m_selectGuestHint(false)
27− , m_hasGuestAccountHint(false)
2826 , m_fullSessions(
2927 {
3028 {"ubuntu", "Ubuntu"},
@@ -40,12 +38,7 @@ MockController::MockController(QObject *parent)
4038 {"", "Unknown?"}
4139 })
4240{
43− m_userMode = qgetenv("LIBLIGHTDM_MOCK_MODE");
44− if (m_userMode.isEmpty()) {
45− m_userMode = "full";
46− }
47− m_sessionMode = "full";
48− m_numSessions = numFullSessions();
41+ reset();
4942}
5043
5144MockController::~MockController()
@@ -63,11 +56,17 @@ MockController *MockController::instance()
6356
6457void MockController::reset()
6558{
59+ auto userMode = qgetenv("LIBLIGHTDM_MOCK_MODE");
60+ if (userMode.isEmpty()) {
61+ userMode = "full";
62+ }
63+
6664 setSelectUserHint("");
6765 setSelectGuestHint(false);
6866 setHasGuestAccountHint(false);
69− setUserMode("full");
67+ setUserMode(userMode);
7068 setSessionMode("full");
69+ setNumSessions(numFullSessions());
7170}
7271
7372QString MockController::selectUserHint() const
tests/mocks/liblightdm/MockGreeter.cpp−5
@@ -255,9 +255,4 @@ void Greeter::sendAuthenticationComplete()
255255 }
256256}
257257
258−QObject *Greeter::mock()
259−{
260− return MockController::instance();
261−}
262−
263258}
tests/mocks/liblightdm/MockGreeter.h−4
@@ -42,8 +42,6 @@ class Q_DECL_EXPORT Greeter : public QObject
4242
4343 Q_ENUMS(PromptType MessageType)
4444
45− Q_PROPERTY(QObject *mock READ mock CONSTANT) // only in mock
46−
4745public:
4846 enum PromptType {
4947 PromptTypeQuestion,
@@ -75,8 +73,6 @@ public:
7573 QString authenticationUser() const;
7674 QString hostname() const;
7775
78− QObject *mock();
79−
8076public Q_SLOTS:
8177 bool connectSync();
8278 void authenticate(const QString &username=QString());
tests/mocks/liblightdm/MockSessionsModel.cpp+1−5
@@ -43,6 +43,7 @@ SessionsModel::SessionsModel(QObject* parent)
4343 d->roleNames = QAbstractListModel::roleNames();
4444 d->roleNames[KeyRole] = "key";
4545 d->roleNames[TypeRole] = "type";
46+ setRoleNames(d->roleNames);
4647
4748 connect(MockController::instance(), &MockController::sessionModeChanged,
4849 this, &SessionsModel::resetEntries);
@@ -114,9 +115,4 @@ void SessionsModel::resetEntries()
114115 endResetModel();
115116}
116117
117−QObject *SessionsModel::mock()
118−{
119− return MockController::instance();
120−}
121−
122118} // namespace QLightDM
tests/mocks/liblightdm/MockSessionsModel.h−4
@@ -30,8 +30,6 @@ class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
3030 {
3131 Q_OBJECT
3232
33− Q_PROPERTY(QObject *mock READ mock CONSTANT) // only in mock
34−
3533 Q_ENUMS(SessionModelRoles SessionType)
3634
3735 public:
@@ -56,8 +54,6 @@ class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
5654 int rowCount(const QModelIndex& parent) const override;
5755 QVariant data(const QModelIndex& index, int role) const override;
5856
59− QObject *mock();
60−
6157 private Q_SLOTS:
6258 void resetEntries();
6359
tests/mocks/liblightdm/MockUsersModel.cpp−5
@@ -183,9 +183,4 @@ void UsersModel::resetEntries()
183183 endResetModel();
184184}
185185
186−QObject *UsersModel::mock()
187−{
188− return MockController::instance();
189−}
190−
191186}
tests/mocks/liblightdm/MockUsersModel.h−4
@@ -31,8 +31,6 @@ class Q_DECL_EXPORT UsersModel : public QAbstractListModel
3131
3232 Q_ENUMS(UserModelRoles)
3333
34− Q_PROPERTY(QObject *mock READ mock CONSTANT) // only in mock
35−
3634public:
3735 explicit UsersModel(QObject *parent = 0);
3836 virtual ~UsersModel();
@@ -51,8 +49,6 @@ public:
5149 int rowCount(const QModelIndex &parent) const override;
5250 QVariant data(const QModelIndex &index, int role) const override;
5351
54− QObject *mock();
55−
5652private Q_SLOTS:
5753 void resetEntries();
5854
tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt+1
@@ -58,6 +58,7 @@ target_include_directories(GreeterSessionsModelTestExec PUBLIC
5858 ${CMAKE_SOURCE_DIR}/plugins/LightDM
5959 ${CMAKE_SOURCE_DIR}/plugins/Utils
6060 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
61+ ${CMAKE_SOURCE_DIR}/tests/mocks/liblightdm/
6162 )
6263add_unity8_uitest(GreeterSessions GreeterSessionsModelTestExec
6364 DEPENDS MockLightDM
tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp+3−1
@@ -16,6 +16,8 @@
1616
1717#include "SessionsModel.h"
1818
19+#include "MockController.h"
20+
1921#include <QLightDM/SessionsModel>
2022#include <QtCore/QModelIndex>
2123#include <QtTest>
@@ -31,7 +33,7 @@ private Q_SLOTS:
3133 {
3234 model = new SessionsModel();
3335 QVERIFY(model);
34− mock = model->mock();
36+ mock = QLightDM::MockController::instance();
3537 QVERIFY(mock);
3638 }
3739
tests/plugins/Unity/Launcher/launchermodeltest.cpp+6
@@ -33,6 +33,8 @@
3333
3434#include <glib.h>
3535
36+namespace unityapi = unity::shell::application;
37+
3638// This is a mock, specifically to test the LauncherModel
3739class MockApp: public unity::shell::application::ApplicationInfoInterface
3840{
@@ -65,6 +67,7 @@ public:
6567 MirSurfaceListInterface* promptSurfaceList() const override { return nullptr; }
6668 int surfaceCount() const override { return m_surfaceCount; }
6769 void setSurfaceCount(int count) { m_surfaceCount = count; Q_EMIT surfaceCountChanged(count); }
70+ void close() override {}
6871
6972 // Methods used for mocking (not in the interface)
7073 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
@@ -98,6 +101,9 @@ public:
98101 }
99102 return nullptr;
100103 }
104+ unityapi::ApplicationInfoInterface *findApplicationWithSurface(unityapi::MirSurfaceInterface* surface) const override {
105+ return nullptr;
106+ }
101107 unity::shell::application::ApplicationInfoInterface *startApplication(const QString &, const QStringList &) override { return nullptr; }
102108 bool stopApplication(const QString &appId) override {
103109 Q_FOREACH(MockApp* app, m_list) {
tests/qmltests/Dash/tst_DashShell.qml+2−1
@@ -20,6 +20,7 @@ import "../../../qml/Dash"
2020import "../../../qml/"
2121import Ubuntu.Components 1.3
2222import Unity.Test 0.1 as UT
23+import LightDMController 0.1
2324import LightDM.FullLightDM 0.1 as LightDM
2425
2526Item {
@@ -28,7 +29,7 @@ Item {
2829 height: units.gu(80)
2930
3031 Binding {
31− target: LightDM.Users.mock
32+ target: LightDMController
3233 property: "userMode"
3334 value: "single"
3435 }
tests/qmltests/Greeter/tst_Greeter.qml+5−4
@@ -22,6 +22,7 @@ import Ubuntu.Components 1.3
2222import AccountsService 0.1
2323import Biometryd 0.0
2424import GSettings 1.0
25+import LightDMController 0.1
2526import LightDM.FullLightDM 0.1 as LightDM
2627import Unity.Test 0.1 as UT
2728
@@ -143,7 +144,7 @@ Item {
143144
144145 function init() {
145146 greeterSettings.lockedOutTime = 0;
146− LightDM.Greeter.mock.reset();
147+ LightDMController.reset();
147148 greeter.failedLoginsDelayAttempts = 7;
148149 greeter.failedLoginsDelayMinutes = 5;
149150 teaseSpy.clear();
@@ -570,20 +571,20 @@ Item {
570571 }
571572
572573 function test_selectUserHint() {
573− LightDM.Greeter.mock.selectUserHint = "info-prompt";
574+ LightDMController.selectUserHint = "info-prompt";
574575 resetLoader();
575576 var i = verifySelected("info-prompt");
576577 verify(i != 0); // sanity-check that info-prompt isn't default 0 answer
577578 }
578579
579580 function test_selectUserHintUnset() {
580− LightDM.Greeter.mock.selectUserHint = "";
581+ LightDMController.selectUserHint = "";
581582 resetLoader();
582583 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
583584 }
584585
585586 function test_selectUserHintInvalid() {
586− LightDM.Greeter.mock.selectUserHint = "not-a-real-user";
587+ LightDMController.selectUserHint = "not-a-real-user";
587588 resetLoader();
588589 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
589590 }
tests/qmltests/Greeter/tst_NarrowView.qml+2−1
@@ -18,6 +18,7 @@ import QtQuick 2.4
1818import QtTest 1.0
1919import ".."
2020import "../../../qml/Greeter"
21+import LightDMController 0.1
2122import LightDM.FullLightDM 0.1 as LightDM
2223import Ubuntu.Components 1.3
2324import Ubuntu.Telephony 0.1 as Telephony
@@ -31,7 +32,7 @@ Item {
3132 Component.onCompleted: theme.name = "Ubuntu.Components.Themes.SuruDark" // use the same theme as the real shell
3233
3334 Binding {
34− target: LightDM.Users.mock
35+ target: LightDMController
3536 property: "userMode"
3637 value: "single"
3738 }
tests/qmltests/Greeter/tst_WideView.qml+18−20
@@ -18,6 +18,7 @@ import QtQuick 2.4
1818import QtTest 1.0
1919import ".."
2020import "../../../qml/Greeter"
21+import LightDMController 0.1
2122import LightDM.FullLightDM 0.1 as LightDM
2223import Ubuntu.Components 1.3
2324import Unity.Test 0.1 as UT
@@ -265,15 +266,15 @@ StyledItem {
265266 id: multipleSessionsCheckbox
266267 onClicked: {
267268 if (checked) {
268− LightDM.Sessions.mock.sessionMode = "full";
269+ LightDMController.sessionMode = "full";
269270 } else {
270− LightDM.Sessions.mock.sessionMode = "single";
271+ LightDMController.sessionMode = "single";
271272 }
272273 }
273274 Connections {
274− target: LightDM.Sessions.mock
275+ target: LightDMController
275276 onSessionModeChanged: {
276− if (LightDM.Sessions.mock.sessionMode === "full") {
277+ if (LightDMController.sessionMode === "full") {
277278 multipleSessionsCheckbox.checked = true;
278279 } else {
279280 multipleSessionsCheckbox.checked = false;
@@ -291,11 +292,11 @@ StyledItem {
291292
292293 width: units.gu(10)
293294 minimumValue: 0
294− maximumValue: LightDM.Sessions.mock.numAvailableSessions
295− value: LightDM.Sessions.mock.numSessions
296− visible: LightDM.Sessions.mock.sessionMode === "full"
295+ maximumValue: LightDMController.numAvailableSessions
296+ value: LightDMController.numSessions
297+ visible: LightDMController.sessionMode === "full"
297298 Binding {
298− target: LightDM.Sessions.mock
299+ target: LightDMController
299300 property: "numSessions"
300301 value: numSessionsSlider.value
301302 }
@@ -362,7 +363,8 @@ StyledItem {
362363 respondedSpy.clear();
363364 teaseSpy.clear();
364365 emergencySpy.clear();
365− LightDM.Sessions.mock.sessionMode = "full";
366+ LightDMController.sessionMode = "full";
367+ LightDM.Sessions.iconSearchDirectories = [testIconDirectory];
366368 }
367369
368370 function cleanup() {
@@ -420,9 +422,6 @@ StyledItem {
420422 }
421423
422424 function test_sessionIconsAreValid() {
423− var originalDirectories = LightDM.Sessions.iconSearchDirectories
424− LightDM.Sessions.iconSearchDirectories = [testIconDirectory]
425−
426425 selectUser("has-password");
427426
428427 // Test the login list icon is valid
@@ -442,8 +441,7 @@ StyledItem {
442441
443442 function test_choosingNewSessionChangesLoginListIcon() {
444443 // Ensure the default session is selected (Ubuntu)
445− loader.active = false;
446− loader.active = true;
444+ cleanup();
447445
448446 selectUser("has-password");
449447
@@ -457,25 +455,25 @@ StyledItem {
457455 var currentDelegate = findChild(view, delegateName);
458456 var sessionKey = LightDM.Sessions.data(i,LightDM.SessionRoles.KeyRole);
459457 if (sessionKey === "gnome-classic") {
458+ waitForRendering(currentDelegate);
460459 tap(currentDelegate);
461− var sessionChooserButton = findChild(view, "sessionChooserButton");
462460 waitForRendering(sessionChooserButton);
463− var icon = String(sessionChooserButton.icon);
464461 break;
465462 }
466463 }
467464
465+ icon = String(sessionChooserButton.icon);
468466 compare(icon.indexOf("gnome") > -1, true,
469467 "Expected icon to contain gnome but it was " + icon);
470468 }
471469
472470 function test_noSessionsDoesntBreakView() {
473− LightDM.Sessions.mock.sessionMode = "none";
471+ LightDMController.sessionMode = "none";
474472 compare(LightDM.Sessions.count, 0)
475473 }
476474
477475 function test_sessionIconNotShownWithOneSession() {
478− LightDM.Sessions.mock.sessionMode = "single";
476+ LightDMController.sessionMode = "single";
479477 compare(LightDM.Sessions.count, 1);
480478
481479 var sessionChooserButton = findChild(view, "sessionChooserButton");
@@ -483,7 +481,7 @@ StyledItem {
483481 }
484482
485483 function test_sessionIconNotShownWithActiveUser() {
486− LightDM.Sessions.mock.sessionMode = "full";
484+ LightDMController.sessionMode = "full";
487485 compare(LightDM.Sessions.count > 1, true);
488486
489487 selectUser("active");
@@ -493,7 +491,7 @@ StyledItem {
493491 }
494492
495493 function test_sessionIconShownWithMultipleSessions() {
496− LightDM.Sessions.mock.sessionMode = "full";
494+ LightDMController.sessionMode = "full";
497495 compare(LightDM.Sessions.count > 1, true);
498496
499497 selectUser("has-password");
tests/qmltests/Panel/tst_ActiveCallHint.qml+16−2
@@ -41,6 +41,8 @@ Item {
4141 elapsedTimerRunning: true
4242 }
4343
44+ SurfaceManager {}
45+
4446 ActiveCallHint {
4547 id: callHint
4648 anchors {
@@ -95,18 +97,30 @@ Item {
9597
9698 function test_activeHint(data) {
9799 if (data.dialer) {
100+ var dashApp = ApplicationManager.startApplication("unity8-dash");
101+ tryCompare(dashApp.surfaceList, "count", 1);
102+
98103 var application = ApplicationManager.startApplication("dialer-app");
99104 tryCompare(application.surfaceList, "count", 1);
100− MirFocusController.focusedSurface = application.surfaceList.get(0);
101105 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
102106 tryCompare(application, "state", ApplicationInfoInterface.Running);
103107
104− if (!data.focused) { MirFocusController.focusedSurface = null; }
108+ if (!data.focused) {
109+ dashApp.surfaceList.get(0).activate();
110+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
111+ }
105112 }
106113 callManager.foregroundCall = data.call;
107114 callManager.callIndicatorVisible = data.visible;
108115
109116 compare(callHint.active, data.expected, "Call hint should be active when callIndicatorVisible=true");
117+
118+ if (data.dialer) {
119+ // clean up
120+ ApplicationManager.stopApplication("dialer-app");
121+ ApplicationManager.stopApplication("unity8-dash");
122+ tryCompare(ApplicationManager, "count", 0);
123+ }
110124 }
111125
112126 function test_currentCall_data() {
tests/qmltests/Panel/tst_Panel.qml+8−1
@@ -33,6 +33,8 @@ IndicatorTest {
3333
3434 Component.onCompleted: theme.name = "Ubuntu.Components.Themes.SuruDark"
3535
36+ SurfaceManager {}
37+
3638 Binding {
3739 target: mouseEmulation
3840 property: "checked"
@@ -470,7 +472,9 @@ IndicatorTest {
470472
471473 callManager.foregroundCall = phoneCall;
472474
473− MirFocusController.focusedSurface = ApplicationManager.findApplication("unity8-dash").surfaceList.get(0);
475+ var dashApp = ApplicationManager.startApplication("unity8-dash");
476+ tryCompare(dashApp.surfaceList, "count", 1);
477+ dashApp.surfaceList.get(0).activate();
474478 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
475479
476480 mouseClick(panel.indicators,
@@ -479,6 +483,9 @@ IndicatorTest {
479483
480484 compare(panel.indicators.shown, false);
481485 verify(panel.indicators.fullyClosed);
486+
487+ // clean up
488+ ApplicationManager.stopApplication("unity8-dash");
482489 }
483490
484491 function test_openAndClosePanelWithMouseClicks() {
tests/qmltests/Stage/ApplicationCheckBox.qml+1−1
@@ -149,7 +149,7 @@ Column {
149149 width: height
150150 height: promptsLabel.height * 0.7
151151 anchors.verticalCenter: parent.verticalCenter
152− onClicked: d.application.promptSurfaceList.createSurface()
152+ onClicked: d.application.createPromptSurface()
153153 Label { text: "➕"; anchors.centerIn: parent }
154154 }
155155 MouseArea {
tests/qmltests/Stage/tst_ApplicationWindow.qml+10−10
@@ -37,6 +37,8 @@ Rectangle {
3737 }
3838 property QtObject fakeApplication: null
3939
40+ SurfaceManager{}
41+
4042 Loader {
4143 id: applicationWindowLoader
4244 focus: true
@@ -105,17 +107,17 @@ Rectangle {
105107 RowLayout {
106108 property var promptSurfaceList: root.fakeApplication ? root.fakeApplication.promptSurfaceList : null
107109 Button {
108− enabled: parent.promptSurfaceList !== null && parent.promptSurfaceList.count > 0
110+ enabled: root.fakeApplication && root.fakeApplication.promptSurfaceList.count > 0
109111 activeFocusOnPress: false
110112 text: "Remove"
111− onClicked: { parent.promptSurfaceList.get(0).close(); }
113+ onClicked: { root.fakeApplication.promptSurfaceList.get(0).close(); }
112114 }
113115
114116 Button {
115− enabled: parent.promptSurfaceList !== null
117+ enabled: root.fakeApplication
116118 activeFocusOnPress: false
117119 text: "Add Prompt Surface"
118− onClicked: { parent.promptSurfaceList.createSurface(); }
120+ onClicked: { root.fakeApplication.createPromptSurface(); }
119121 }
120122 }
121123
@@ -427,7 +429,7 @@ Rectangle {
427429 var i;
428430 // 3 surfaces should cover all edge cases
429431 for (i = 0; i < 3; i++) {
430− promptSurfaceList.createSurface();
432+ root.fakeApplication.createPromptSurface();
431433 compare(promptSurfaces.count, i+1);
432434 waitUntilSurfaceContainerStopsAnimating(promptSurfaces.itemAt(0));
433435 }
@@ -452,9 +454,7 @@ Rectangle {
452454 }
453455
454456 function test_promptSurfaceAdjustsForParentSize() {
455− var promptSurfaceList = root.fakeApplication.promptSurfaceList;
456−
457− promptSurfaceList.createSurface();
457+ root.fakeApplication.createPromptSurface();
458458
459459 var promptSurfaces = testCase.findChild(applicationWindow, "promptSurfacesRepeater");
460460
@@ -487,10 +487,10 @@ Rectangle {
487487 var promptSurfaceList = root.fakeApplication.promptSurfaceList;
488488 var promptSurfaces = testCase.findChild(applicationWindow, "promptSurfacesRepeater");
489489
490− promptSurfaceList.createSurface();
490+ root.fakeApplication.createPromptSurface();
491491
492492 for (var i = 2; i <= 3; i++) {
493− promptSurfaceList.createSurface();
493+ root.fakeApplication.createPromptSurface();
494494 tryCompare(promptSurfaces, "count", i);
495495 waitUntilSurfaceContainerStopsAnimating(promptSurfaces.itemAt(0));
496496
tests/qmltests/Stage/tst_DesktopStage.qml+53−80
@@ -20,8 +20,8 @@ import Ubuntu.Components 1.3
2020import Ubuntu.Components.ListItems 1.3
2121import Unity.Application 0.1
2222import Unity.Test 0.1
23−import WindowManager 0.1
2423import Utils 0.1
24+import WindowManager 1.0
2525
2626import ".." // For EdgeBarrierControls
2727import "../../../qml/Stage"
@@ -57,9 +57,11 @@ Item {
5757 }
5858 }
5959
60− TopLevelSurfaceList {
60+ SurfaceManager { id: sMgr }
61+ TopLevelWindowModel {
6162 id: topSurfaceList
62− applicationsModel: ApplicationManager
63+ applicationManager: ApplicationManager
64+ surfaceManager: sMgr
6365 }
6466
6567 Loader {
@@ -80,6 +82,7 @@ Item {
8082
8183 Component.onCompleted: {
8284 edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController");
85+ ApplicationManager.startApplication("unity8-dash");
8386 }
8487 Component.onDestruction: {
8588 stageLoader.itemDestroyed = true;
@@ -154,12 +157,13 @@ Item {
154157 target: clickThroughTester
155158 }
156159
157− UnityTestCase {
160+ StageTestCase {
158161 id: testCase
159162 name: "DesktopStage"
160163 when: windowShown
161164
162− property Item stage: stageLoader.status === Loader.Ready ? stageLoader.item : null
165+ stage: stageLoader.status === Loader.Ready ? stageLoader.item : null
166+ topLevelSurfaceList: topSurfaceList
163167
164168 function init() {
165169 // wait until unity8-dash is up and running.
@@ -170,8 +174,9 @@ Item {
170174 tryCompare(dashApp, "state", ApplicationInfoInterface.Running);
171175
172176 tryCompare(topSurfaceList, "count", 1);
173− tryCompareFunction(function(){return topSurfaceList.surfaceAt(0) != null;}, true);
174− compare(MirFocusController.focusedSurface, topSurfaceList.surfaceAt(0));
177+ tryCompareFunction(function(){return topSurfaceList.windowAt(0) != null;}, true);
178+ topSurfaceList.windowAt(0).activate();
179+ tryCompare(topSurfaceList, "focusedWindow", topSurfaceList.windowAt(0));
175180 }
176181
177182 function cleanup() {
@@ -196,44 +201,6 @@ Item {
196201 mouseEaterSpy.clear();
197202 }
198203
199− function waitUntilAppSurfaceShowsUp(surfaceId) {
200− var appDelegate = findChild(stage, "appDelegate_" + surfaceId);
201− verify(appDelegate);
202− var appWindow = findChild(appDelegate, "appWindow");
203− verify(appWindow);
204− var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
205− verify(appWindowStates);
206− tryCompare(appWindowStates, "state", "surface");
207− }
208−
209− /*
210− Returns the appDelegate of the first surface created by the app with the specified appId
211− */
212− function startApplication(appId) {
213− try {
214− var app = ApplicationManager.findApplication(appId);
215− if (app) {
216− for (var i = 0; i < topSurfaceList.count; i++) {
217− if (topSurfaceList.applicationAt(i).appId === appId) {
218− var appRepeater = findChild(stage, "appRepeater");
219− verify(appRepeater);
220− return appRepeater.itemAt(i);
221− }
222− }
223− }
224−
225− var surfaceId = topSurfaceList.nextId;
226− app = ApplicationManager.startApplication(appId);
227− verify(app);
228− waitUntilAppSurfaceShowsUp(surfaceId);
229− compare(app.surfaceList.count, 1);
230−
231− return findChild(stage, "appDelegate_" + surfaceId);
232− } catch(err) {
233− throw new Error("startApplication("+appId+") called from line " + util.callerLine(1) + " failed!");
234− }
235− }
236−
237204 function maximizeAppDelegate(appDelegate) {
238205 var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
239206 if (!maximizeButton) {
@@ -284,13 +251,13 @@ Item {
284251 verify(fromAppWindow);
285252 tap(fromAppWindow);
286253 compare(fromDelegate.surface.activeFocus, true);
287− compare(MirFocusController.focusedSurface, fromDelegate.surface);
254+ compare(topSurfaceList.focusedWindow, fromDelegate.window);
288255
289256 var toAppWindow = findChild(toDelegate, "appWindow");
290257 verify(toAppWindow);
291258 tap(toAppWindow);
292259 compare(toDelegate.surface.activeFocus, true);
293− compare(MirFocusController.focusedSurface, toDelegate.surface);
260+ compare(topSurfaceList.focusedWindow, toDelegate.window);
294261 }
295262
296263 function test_clickingOnWindowChangesFocusedApp_data() {
@@ -309,13 +276,13 @@ Item {
309276 verify(fromAppWindow);
310277 mouseClick(fromAppWindow);
311278 compare(fromDelegate.surface.activeFocus, true);
312− compare(MirFocusController.focusedSurface, fromDelegate.surface);
279+ compare(topSurfaceList.focusedWindow, fromDelegate.window);
313280
314281 var toAppWindow = findChild(toDelegate, "appWindow");
315282 verify(toAppWindow);
316283 mouseClick(toAppWindow);
317284 compare(toDelegate.surface.activeFocus, true);
318− compare(MirFocusController.focusedSurface, toDelegate.surface);
285+ compare(topSurfaceList.focusedWindow, toDelegate.window);
319286 }
320287
321288 function test_tappingOnDecorationFocusesApplication_data() {
@@ -343,6 +310,13 @@ Item {
343310 return findChild(appDelegate, "appWindowDecoration");
344311 }
345312
313+ function maximizeDelegate(appDelegate) {
314+ var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
315+ verify(maximizeButton);
316+ mouseClick(maximizeButton);
317+ tryCompare(appDelegate, "visuallyMaximized", true);
318+ }
319+
346320 function test_tappingOnDecorationFocusesApplication(data) {
347321 var appDelegates = [];
348322 for (var i = 0; i < data.apps.length; i++) {
@@ -398,7 +372,7 @@ Item {
398372 startApplication("camera-app");
399373
400374 tryCompareFunction(function(){ return dialerDelegate.surface !== null; }, true);
401− dialerDelegate.surface.requestFocus();
375+ dialerDelegate.surface.activate();
402376 tryCompare(dialerDelegate, "focus", true);
403377
404378 keyClick(Qt.Key_Up, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Up shortcut to maximize
@@ -411,7 +385,7 @@ Item {
411385 startApplication("camera-app");
412386
413387 tryCompareFunction(function(){ return dialerDelegate.surface !== null; }, true);
414− dialerDelegate.surface.requestFocus();
388+ dialerDelegate.surface.activate();
415389 tryCompare(dialerDelegate, "focus", true);
416390
417391 keyClick(Qt.Key_Left, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Left shortcut to maximizeLeft
@@ -426,7 +400,7 @@ Item {
426400 startApplication("camera-app");
427401
428402 tryCompareFunction(function(){ return dialerDelegate.surface !== null; }, true);
429− dialerDelegate.surface.requestFocus();
403+ dialerDelegate.surface.activate();
430404 tryCompare(dialerDelegate, "focus", true);
431405
432406 keyClick(Qt.Key_Right, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Right shortcut to maximizeRight
@@ -441,7 +415,7 @@ Item {
441415 startApplication("camera-app");
442416
443417 tryCompareFunction(function(){ return dialerDelegate.surface !== null; }, true);
444− dialerDelegate.surface.requestFocus();
418+ dialerDelegate.surface.activate();
445419 tryCompare(dialerDelegate, "focus", true);
446420
447421 keyClick(Qt.Key_Down, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Down shortcut to minimize
@@ -512,11 +486,11 @@ Item {
512486 apps.forEach(startApplication);
513487 verify(topSurfaceList.count == 3);
514488 keyClick(Qt.Key_D, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+D shortcut to minimize all
515− tryCompare(MirFocusController, "focusedSurface", null); // verify no surface is focused
489+ tryCompare(topSurfaceList, "focusedWindow", null); // verify no window is focused
516490
517491 // now try pressing all 4 arrow keys + ctrl + meta
518492 keyClick(Qt.Key_Up | Qt.Key_Down | Qt.Key_Left | Qt.Key_Right, Qt.MetaModifier|Qt.ControlModifier); // smash it!!!
519− tryCompare(MirFocusController, "focusedSurface", null); // verify still no surface is focused
493+ tryCompare(topSurfaceList, "focusedWindow", null); // verify still no window is focused
520494 }
521495
522496 function test_minimizeApplicationHidesSurface() {
@@ -527,9 +501,12 @@ Item {
527501 var decoratedWindow = findDecoratedWindow(dashSurfaceId);
528502 verify(decoratedWindow);
529503
530− tryCompare(dashSurface, "visible", true);
531− decoratedWindow.minimizeClicked();
532− tryCompare(dashSurface, "visible", false);
504+ var minimizeButton = findChild(decoratedWindow, "minimizeWindowButton");
505+ verify(minimizeButton);
506+
507+ tryCompare(dashSurface, "exposed", true);
508+ mouseClick(minimizeButton);
509+ tryCompare(dashSurface, "exposed", false);
533510 }
534511
535512 function test_maximizeApplicationHidesSurfacesBehindIt() {
@@ -538,16 +515,16 @@ Item {
538515 var gmailDelegate = startApplication("gmail-webapp");
539516
540517 // maximize without raising
541− dialerDelegate.maximize();
518+ dialerDelegate.requestMaximize();
542519 tryCompare(dialerDelegate, "visuallyMaximized", true);
543520
544− tryCompare(dashDelegate.surface, "visible", false);
545− compare(gmailDelegate.surface.visible, true);
521+ tryCompare(dashDelegate.surface, "exposed", false);
522+ compare(gmailDelegate.surface.exposed, true);
546523
547524 // restore without raising
548− dialerDelegate.restoreFromMaximized();
549− compare(dashDelegate.surface.visible, true);
550− compare(gmailDelegate.surface.visible, true);
525+ dialerDelegate.requestRestore();
526+ tryCompare(dashDelegate.surface, "exposed", true);
527+ compare(gmailDelegate.surface.exposed, true);
551528 }
552529
553530 function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
@@ -580,26 +557,22 @@ Item {
580557 tryCompare(dialerDelegate, "visuallyMaximized", true);
581558 tryCompare(gmailDelegate, "visuallyMaximized", true);
582559
583− tryCompare(dashApp.surfaceList.get(0), "visible", false);
584− tryCompare(dialerApp.surfaceList.get(0), "visible", false);
585− tryCompare(mapApp.surfaceList.get(0), "visible", false);
560+ tryCompare(dashApp.surfaceList.get(0), "exposed", false);
561+ tryCompare(dialerApp.surfaceList.get(0), "exposed", false);
562+ tryCompare(mapApp.surfaceList.get(0), "exposed", false);
586563
587564 ApplicationManager.stopApplication("gmail-webapp");
588565 wait(2000)
589566
590− tryCompare(mapApp.surfaceList.get(0), "visible", true);
591− tryCompare(dialerApp.surfaceList.get(0), "visible", true);
592− tryCompare(dashApp.surfaceList.get(0), "visible", false); // still occluded by maximised dialer
567+ tryCompare(mapApp.surfaceList.get(0), "exposed", true);
568+ tryCompare(dialerApp.surfaceList.get(0), "exposed", true);
569+ tryCompare(dashApp.surfaceList.get(0), "exposed", false); // still occluded by maximised dialer
593570 }
594571
595572 function test_maximisedAppStaysVisibleWhenAppStarts() {
596573 var dashDelegate = startApplication("unity8-dash");
597574
598− // maximize
599− var dashMaximizeButton = findChild(dashDelegate, "maximizeWindowButton");
600− verify(dashMaximizeButton);
601− mouseClick(dashMaximizeButton);
602− tryCompare(dashDelegate, "visuallyMaximized", true);
575+ maximizeDelegate(dashDelegate);
603576
604577 var dialerDelegate = startApplication("dialer-app");
605578 verify(dialerDelegate);
@@ -621,25 +594,25 @@ Item {
621594 tryCompare(facebookAppDelegate, "visible", true);
622595
623596 // Maximize the topmost and make sure the other two are hidden
624− facebookAppDelegate.maximize();
597+ maximizeDelegate(facebookAppDelegate);
625598 tryCompare(dashAppDelegate, "visible", false);
626599 tryCompare(dialerAppDelegate, "visible", false);
627600 tryCompare(facebookAppDelegate, "visible", true);
628601
629602 // Bring dash to front. make sure dash and the maximized facebook are visible, the restored one behind is hidden
630− dashAppDelegate.focus = true;
603+ dashAppDelegate.activate();
631604 tryCompare(dashAppDelegate, "visible", true);
632605 tryCompare(dialerAppDelegate, "visible", false);
633606 tryCompare(facebookAppDelegate, "visible", true);
634607
635608 // Now focus the dialer app. all 3 should be visible again
636− dialerAppDelegate.focus = true;
609+ dialerAppDelegate.activate();
637610 tryCompare(dashAppDelegate, "visible", true);
638611 tryCompare(dialerAppDelegate, "visible", true);
639612 tryCompare(facebookAppDelegate, "visible", true);
640613
641614 // Maximize the dialer app. The other 2 should hide
642− dialerAppDelegate.maximize();
615+ maximizeDelegate(dialerAppDelegate);
643616 tryCompare(dashAppDelegate, "visible", false);
644617 tryCompare(dialerAppDelegate, "visible", true);
645618 tryCompare(facebookAppDelegate, "visible", false);
@@ -648,7 +621,7 @@ Item {
648621 function test_dropShadow() {
649622 // start an app, maximize it
650623 var facebookAppDelegate = startApplication("facebook-webapp");
651− facebookAppDelegate.maximize();
624+ maximizeDelegate(facebookAppDelegate);
652625
653626 // verify the drop shadow is still not visible
654627 verify(PanelState.dropShadow == false);
tests/qmltests/Stage/tst_PhoneStage.qml+18−12
@@ -22,7 +22,7 @@ import "../../../qml/Components"
2222import "../../../qml/Stage"
2323import Ubuntu.Components 1.3
2424import Unity.Application 0.1
25−import WindowManager 0.1
25+import WindowManager 1.0
2626
2727Item {
2828 id: root
@@ -31,6 +31,7 @@ Item {
3131
3232 property var greeter: { fullyShown: true }
3333
34+ SurfaceManager { id: sMgr }
3435 Stage {
3536 id: stage
3637 anchors { fill: parent; rightMargin: units.gu(30) }
@@ -41,9 +42,13 @@ Item {
4142 orientations: Orientations {}
4243 applicationManager: ApplicationManager
4344 mode: "staged"
44− topLevelSurfaceList: TopLevelSurfaceList {
45+ topLevelSurfaceList: TopLevelWindowModel {
4546 id: topLevelSurfaceList
46− applicationsModel: ApplicationManager
47+ applicationManager: ApplicationManager
48+ surfaceManager: sMgr
49+ }
50+ Component.onCompleted: {
51+ ApplicationManager.startApplication("unity8-dash");
4752 }
4853 }
4954
@@ -86,11 +91,16 @@ Item {
8691
8792 function init() {
8893 // wait until unity8-dash is up and running.
89− // it's started automatically by ApplicationManager mock implementation
94+ ApplicationManager.startApplication("unity8-dash");
9095 tryCompare(ApplicationManager, "count", 1);
9196 var dashApp = ApplicationManager.findApplication("unity8-dash");
9297 verify(dashApp);
9398 tryCompare(dashApp, "state", ApplicationInfoInterface.Running);
99+
100+ // wait for Stage to stabilize back into its initial state
101+ var appRepeater = findChild(stage, "appRepeater");
102+ tryCompare(appRepeater, "count", 1);
103+ tryCompare(appRepeater.itemAt(0), "x", 0);
94104 }
95105
96106 function cleanup() {
@@ -100,10 +110,6 @@ Item {
100110 waitForRendering(stage);
101111
102112 killApps();
103− // wait for Stage to stabilize back into its initial state
104− var appRepeater = findChild(stage, "appRepeater");
105− tryCompare(appRepeater, "count", 1);
106− tryCompare(appRepeater.itemAt(0), "x", 0);
107113
108114 stage.shellOrientationAngle = 0;
109115
@@ -479,7 +485,7 @@ Item {
479485 function test_selectSuspendedAppWithoutSurface() {
480486 compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash");
481487 var dashSurfaceId = topLevelSurfaceList.idAt(0);
482− var dashSurface = topLevelSurfaceList.surfaceAt(0);
488+ var dashWindow = topLevelSurfaceList.windowAt(0);
483489
484490 var webbrowserSurfaceId = topLevelSurfaceList.nextId;
485491 var webbrowserApp = ApplicationManager.startApplication("webbrowser-app");
@@ -487,7 +493,7 @@ Item {
487493
488494 switchToSurface(dashSurfaceId);
489495
490− tryCompare(MirFocusController, "focusedSurface", dashSurface);
496+ tryCompare(topLevelSurfaceList, "focusedWindow", dashWindow);
491497 tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
492498
493499 compare(webbrowserApp.surfaceList.count, 1);
@@ -523,7 +529,7 @@ Item {
523529 {
524530 compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash");
525531 var dashSurfaceId = topLevelSurfaceList.idAt(0);
526− var dashSurface = topLevelSurfaceList.surfaceAt(0);
532+ var dashWindow = topLevelSurfaceList.windowAt(0);
527533
528534 var webbrowserSurfaceId = topLevelSurfaceList.nextId;
529535 var webbrowserApp = ApplicationManager.startApplication("webbrowser-app");
@@ -531,7 +537,7 @@ Item {
531537
532538 switchToSurface(dashSurfaceId);
533539
534− tryCompare(MirFocusController, "focusedSurface", dashSurface);
540+ tryCompare(topLevelSurfaceList, "focusedWindow", dashWindow);
535541 tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
536542
537543 compare(webbrowserApp.surfaceList.count, 1);
tests/qmltests/Stage/tst_SurfaceContainer.qml+4
@@ -41,6 +41,8 @@ Rectangle {
4141 SurfaceContainer {
4242 anchors.fill: parent
4343 focus: true
44+ requestedWidth: width
45+ requestedHeight: height
4446 interactive: interactiveCheckbox.checked
4547 isPromptSurface: promptCheckbox.checked
4648 Component.onDestruction: {
@@ -49,6 +51,8 @@ Rectangle {
4951 }
5052 }
5153
54+ SurfaceManager {}
55+
5256 Loader {
5357 id: surfaceContainerLoader
5458 focus: true
tests/qmltests/Stage/tst_TabletStage.qml+16−10
@@ -21,7 +21,7 @@ import Ubuntu.Components.ListItems 1.3 as ListItem
2121import Unity.Application 0.1
2222import Unity.Test 0.1
2323import Utils 0.1
24−import WindowManager 0.1
24+import WindowManager 1.0
2525
2626import ".."
2727import "../../../qml/Stage"
@@ -35,6 +35,7 @@ Rectangle {
3535
3636 property var greeter: { fullyShown: true }
3737
38+ SurfaceManager { id: sMgr }
3839 Stage {
3940 id: stage
4041 anchors { fill: parent; rightMargin: units.gu(30) }
@@ -50,9 +51,13 @@ Rectangle {
5051 focus: true
5152 mode: "stagedWithSideStage"
5253 applicationManager: ApplicationManager
53− topLevelSurfaceList: TopLevelSurfaceList {
54+ topLevelSurfaceList: TopLevelWindowModel {
5455 id: topLevelSurfaceList
55− applicationsModel: ApplicationManager
56+ applicationManager: ApplicationManager
57+ surfaceManager: sMgr
58+ }
59+ Component.onCompleted: {
60+ ApplicationManager.startApplication("unity8-dash");
5661 }
5762 }
5863
@@ -129,6 +134,7 @@ Rectangle {
129134 function init() {
130135 stageSaver.clear();
131136
137+ ApplicationManager.startApplication("unity8-dash");
132138 tryCompare(topSurfaceList, "count", 1);
133139 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
134140
@@ -143,6 +149,11 @@ Rectangle {
143149 }
144150 }
145151
152+ // wait for Stage to stabilize back into its initial state
153+ var appRepeater = findChild(stage, "appRepeater");
154+ tryCompare(appRepeater, "count", 1);
155+ tryCompare(appRepeater.itemAt(0), "x", 0);
156+
146157 waitUntilAppSurfaceShowsUp(topSurfaceList.idAt(0));
147158 sideStage.hideNow()
148159 tryCompare(sideStage, "x", stage.width)
@@ -156,11 +167,6 @@ Rectangle {
156167
157168 killApps();
158169
159− // wait for Stage to stabilize back into its initial state
160− var appRepeater = findChild(stage, "appRepeater");
161− tryCompare(appRepeater, "count", 1);
162− tryCompare(appRepeater.itemAt(0), "x", 0);
163−
164170 sideStage.hideNow();
165171 tryCompare(sideStage, "x", stage.width)
166172 waitForRendering(stage)
@@ -628,7 +634,7 @@ Rectangle {
628634 function test_selectSuspendedAppWithoutSurface() {
629635 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
630636 var dashSurfaceId = topSurfaceList.idAt(0);
631− var dashSurface = topSurfaceList.surfaceAt(0);
637+ var dashWindow = topSurfaceList.windowAt(0);
632638
633639 var webbrowserSurfaceId = topSurfaceList.nextId;
634640 webbrowserCheckBox.checked = true;
@@ -637,7 +643,7 @@ Rectangle {
637643
638644 switchToSurface(dashSurfaceId);
639645
640− tryCompare(MirFocusController, "focusedSurface", dashSurface);
646+ tryCompare(topLevelSurfaceList, "focusedWindow", dashWindow);
641647 tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
642648
643649 compare(webbrowserApp.surfaceList.count, 1);
tests/qmltests/Tutorial/tst_Tutorial.qml+32−9
@@ -17,6 +17,7 @@
1717import QtQuick 2.4
1818import QtTest 1.0
1919import AccountsService 0.1
20+import LightDMController 0.1
2021import LightDM.FullLightDM 0.1 as LightDM
2122import Ubuntu.Components 1.3
2223import Ubuntu.Components.ListItems 1.3
@@ -85,7 +86,7 @@ Rectangle {
8586
8687 Component.onCompleted: {
8788 // must set the mock mode before loading the Shell
88− LightDM.Users.mock.userMode = "single-pin";
89+ LightDMController.userMode = "single-pin";
8990 shellLoader.active = true;
9091 }
9192
@@ -221,11 +222,11 @@ Rectangle {
221222 CheckBox {
222223 activeFocusOnPress: false
223224 onCheckedChanged: {
224− var surface = SurfaceManager.inputMethodSurface;
225+ var surface = topLevelSurfaceList.inputMethodSurface;
225226 if (checked) {
226− surface.setState(Mir.RestoredState);
227+ surface.requestState(Mir.RestoredState);
227228 } else {
228− surface.setState(Mir.MinimizedState);
229+ surface.requestState(Mir.MinimizedState);
229230 }
230231 }
231232 }
@@ -245,7 +246,7 @@ Rectangle {
245246 }
246247 }
247248
248− UT.UnityTestCase {
249+ UT.StageTestCase {
249250 id: testCase
250251 name: "Tutorial"
251252 when: windowShown
@@ -254,6 +255,14 @@ Rectangle {
254255 property real halfWidth: shell ? shell.width / 2 : 0
255256 property real halfHeight: shell ? shell.height / 2 : 0
256257
258+ onShellChanged: {
259+ if (shell) {
260+ topLevelSurfaceList = testCase.findInvisibleChild(shell, "topLevelSurfaceList");
261+ } else {
262+ topLevelSurfaceList = null;
263+ }
264+ }
265+
257266 function init() {
258267 prepareShell();
259268
@@ -298,13 +307,17 @@ Rectangle {
298307 tryCompare(shell, "waitingOnGreeter", false); // reset by greeter when ready
299308
300309 WindowStateStorage.clear();
301− SurfaceManager.inputMethodSurface.setState(Mir.MinimizedState);
310+ //topLevelSurfaceList.inputMethodSurface.setState(Mir.MinimizedState);
302311 callManager.foregroundCall = null;
303312 AccountsService.demoEdges = false;
304313 AccountsService.demoEdgesCompleted = [];
305314 AccountsService.demoEdges = true;
306315
307316 LightDM.Greeter.hideGreeter();
317+
318+ stage = findChild(shell, "stage"); // from StageTestCase
319+
320+ startApplication("unity8-dash");
308321 }
309322
310323 function loadShell(state) {
@@ -312,6 +325,14 @@ Rectangle {
312325 prepareShell();
313326 }
314327
328+ function ensureInputMethodSurface() {
329+ var surfaceManager = findInvisibleChild(shell, "surfaceManager");
330+ verify(surfaceManager);
331+ surfaceManager.createInputMethodSurface();
332+
333+ tryCompareFunction(function() { return topLevelSurfaceList.inputMethodSurface !== null }, true);
334+ }
335+
315336 function swipeAwayGreeter() {
316337 var coverPage = findChild(shell, "coverPage");
317338 tryCompare(coverPage, "showProgress", 1);
@@ -710,8 +731,9 @@ Rectangle {
710731 var tutorialLeftLoader = findChild(shell, "tutorialLeftLoader");
711732 verify(tutorialLeftLoader.shown);
712733
713− var surface = SurfaceManager.inputMethodSurface;
714− surface.setState(Mir.RestoredState);
734+ ensureInputMethodSurface();
735+ var surface = topLevelSurfaceList.inputMethodSurface;
736+ surface.requestState(Mir.RestoredState);
715737
716738 var inputMethod = findInvisibleChild(shell, "inputMethod");
717739 tryCompare(inputMethod, "visible", true);
@@ -723,7 +745,8 @@ Rectangle {
723745 var tutorial = findChild(shell, "tutorial");
724746 verify(!tutorial.delayed);
725747
726− SurfaceManager.inputMethodSurface.setState(Mir.RestoredState);
748+ ensureInputMethodSurface();
749+ topLevelSurfaceList.inputMethodSurface.requestState(Mir.RestoredState);
727750
728751 tryCompare(tutorial, "delayed", true);
729752 }
tests/qmltests/tst_OrientedShell.qml+7−1
@@ -22,6 +22,7 @@ import Ubuntu.Components 1.3
2222import Ubuntu.Components.ListItems 1.3 as ListItem
2323import Unity.Application 0.1
2424import Unity.Test 0.1
25+import LightDMController 0.1
2526import LightDM.FullLightDM 0.1 as LightDM
2627import Powerd 0.1
2728import Unity.InputInfo 0.1
@@ -39,7 +40,7 @@ Rectangle {
3940 height: units.gu(100)
4041
4142 Binding {
42− target: LightDM.Users.mock
43+ target: LightDMController
4344 property: "userMode"
4445 value: "single"
4546 }
@@ -986,6 +987,11 @@ Rectangle {
986987 function test_launchLandscapeOnlyAppOverPortraitOnlyDashThenSwitchToDash() {
987988 loadShell("mako");
988989
990+ var dashSurfaceId = topLevelSurfaceList.nextId;
991+ var dashApp = ApplicationManager.startApplication("unity8-dash");
992+ verify(dashApp);
993+ waitUntilAppWindowIsFullyLoaded(dashSurfaceId);
994+
989995 // starts as portrait, as unity8-dash is portrait only
990996 tryCompare(shell, "transformRotationAngle", 0);
991997
tests/qmltests/tst_Shell.qml+131−85
@@ -18,6 +18,7 @@ import QtQuick 2.4
1818import QtTest 1.0
1919import AccountsService 0.1
2020import GSettings 1.0
21+import LightDMController 0.1
2122import LightDM.FullLightDM 0.1 as LightDM
2223import SessionBroadcast 0.1
2324import Ubuntu.Components 1.3
@@ -46,11 +47,20 @@ Rectangle {
4647
4748 Component.onCompleted: {
4849 // must set the mock mode before loading the Shell
49− LightDM.Users.mock.userMode = "single";
50+ LightDMController.userMode = "single";
5051 shellLoader.active = true;
5152 }
5253
5354 property var shell: shellLoader.item ? shellLoader.item : null
55+ onShellChanged: {
56+ if (shell) {
57+ topLevelSurfaceList = testCase.findInvisibleChild(shell, "topLevelSurfaceList");
58+ } else {
59+ topLevelSurfaceList = null;
60+ }
61+ }
62+
63+ property var topLevelSurfaceList: null
5464
5565 Item {
5666 id: shellContainer
@@ -119,6 +129,9 @@ Rectangle {
119129 primary: shellLoader.primaryOrientation
120130 }
121131 mode: shellLoader.mode
132+ Component.onCompleted: {
133+ ApplicationManager.startApplication("unity8-dash");
134+ }
122135 Component.onDestruction: {
123136 shellLoader.itemDestroyed = true;
124137 }
@@ -226,8 +239,8 @@ Rectangle {
226239 activeFocusOnPress: false
227240 model: ["single", "single-passphrase", "single-pin", "full"]
228241 onSelectedIndexChanged: {
229− shellLoader.active = false;
230− LightDM.Users.mock.userMode = model[selectedIndex];
242+ testCase.tearDown();
243+ LightDMController.userMode = model[selectedIndex];
231244 shellLoader.active = true;
232245 }
233246 }
@@ -316,21 +329,21 @@ Rectangle {
316329 id: fullscreeAppCheck
317330
318331 onTriggered: {
319− if (!MirFocusController.focusedSurface) return;
320− if (MirFocusController.focusedSurface.state == Mir.FullscreenState) {
321− MirFocusController.focusedSurface.state = Mir.RestoredState;
332+ if (!topLevelSurfaceList.focusedWindow) return;
333+ if (topLevelSurfaceList.focusedWindow.state == Mir.FullscreenState) {
334+ topLevelSurfaceList.focusedWindow.requestState(Mir.RestoredState);
322335 } else {
323− MirFocusController.focusedSurface.state = Mir.FullscreenState;
336+ topLevelSurfaceList.focusedWindow.requestState(Mir.FullscreenState);
324337 }
325338 }
326339
327340 Binding {
328341 target: fullscreeAppCheck
329− when: MirFocusController.focusedSurface
342+ when: topLevelSurfaceList && topLevelSurfaceList.focusedWindow
330343 property: "checked"
331344 value: {
332− if (!MirFocusController.focusedSurface) return false;
333− return MirFocusController.focusedSurface.state === Mir.FullscreenState
345+ if (!topLevelSurfaceList || !topLevelSurfaceList.focusedWindow) return false;
346+ return topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
334347 }
335348 }
336349 }
@@ -344,21 +357,22 @@ Rectangle {
344357 id: chromeAppCheck
345358
346359 onTriggered: {
347− if (!MirFocusController.focusedSurface) return;
348− if (MirFocusController.focusedSurface.shellChrome == Mir.LowChrome) {
349− MirFocusController.focusedSurface.setShellChrome(Mir.NormalChrome);
360+ if (!topLevelSurfaceList.focusedWindow || !topLevelSurfaceList.focusedWindow.surface) return;
361+ var surface = topLevelSurfaceList.focusedWindow.surface;
362+ if (surface.shellChrome == Mir.LowChrome) {
363+ surface.setShellChrome(Mir.NormalChrome);
350364 } else {
351− MirFocusController.focusedSurface.setShellChrome(Mir.LowChrome);
365+ surface.setShellChrome(Mir.LowChrome);
352366 }
353367 }
354368
355369 Binding {
356370 target: chromeAppCheck
357− when: MirFocusController.focusedSurface !== null
371+ when: topLevelSurfaceList && topLevelSurfaceList.focusedWindow !== null && topLevelSurfaceList.focusedWindow.surface !== null
358372 property: "checked"
359373 value: {
360− if (!MirFocusController.focusedSurface) return false;
361− MirFocusController.focusedSurface.shellChrome === Mir.LowChrome
374+ if (!topLevelSurfaceList || !topLevelSurfaceList.focusedWindow || !topLevelSurfaceList.focusedWindow.surface) return false;
375+ topLevelSurfaceList.focusedWindow.surface.shellChrome === Mir.LowChrome
362376 }
363377 }
364378 }
@@ -367,6 +381,57 @@ Rectangle {
367381 }
368382 }
369383
384+ Button {
385+ text: "Toggle input method"
386+ width: parent.width
387+ activeFocusOnPress: false
388+ onClicked: {
389+ testCase.ensureInputMethodSurface();
390+ var inputMethod = root.topLevelSurfaceList.inputMethodSurface;
391+ if (inputMethod.visible) {
392+ inputMethod.requestState(Mir.HiddenState);
393+ } else {
394+ inputMethod.requestState(Mir.RestoredState);
395+ }
396+ }
397+ }
398+
399+ Label {
400+ text: "Shell Mode"
401+ }
402+ ListItem.ItemSelector {
403+ id: shellModeSelector
404+ anchors { left: parent.left; right: parent.right }
405+ activeFocusOnPress: false
406+ model: ["full-greeter", "greeter", "shell"]
407+ property bool guard: false
408+ onSelectedIndexChanged: {
409+ if (guard) return;
410+ guard = true;
411+ testCase.tearDown();
412+ shellLoader.mode = model[selectedIndex];
413+ shellLoader.active = true;
414+ guard = false;
415+ }
416+ Connections {
417+ target: shellLoader
418+ onModeChanged: {
419+ if (shellModeSelector.guard) {
420+ return;
421+ }
422+ shellModeSelector.guard = true;
423+ for (var i = 0; i < 3; i++) {
424+ if (shellModeSelector.model[i] === shellLoader.mode) {
425+ shellModeSelector.selectedIndex = i;
426+ return;
427+ }
428+ }
429+ shellModeSelector.guard = false;
430+ }
431+ }
432+ }
433+
434+
370435 Label {
371436 text: "Fingerprint"
372437 }
@@ -481,13 +546,12 @@ Rectangle {
481546 }
482547 }
483548
484− UnityTestCase {
549+ StageTestCase {
485550 id: testCase
486551 name: "Shell"
487552 when: windowShown
488553
489554 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
490− property QtObject topLevelSurfaceList: null
491555
492556 function init() {
493557 if (shellLoader.active) {
@@ -526,8 +590,14 @@ Rectangle {
526590
527591 waitForGreeterToStabilize();
528592
593+ // from StageTestCase
529594 topLevelSurfaceList = findInvisibleChild(shell, "topLevelSurfaceList");
530595 verify(topLevelSurfaceList);
596+ stage = findChild(shell, "stage");
597+
598+ // wait until unity8-dash is fully loaded
599+ tryCompare(topLevelSurfaceList, "count", 1);
600+ waitUntilAppWindowIsFullyLoaded(topLevelSurfaceList.idAt(0));
531601 }
532602
533603 function loadDesktopShellWithApps() {
@@ -597,22 +667,23 @@ Rectangle {
597667
598668 GSettingsController.setLifecycleExemptAppids([]);
599669 GSettingsController.setPictureUri("");
670+ }
671+
672+ function ensureInputMethodSurface() {
673+ var surfaceManager = findInvisibleChild(shell, "surfaceManager");
674+ verify(surfaceManager);
675+ surfaceManager.createInputMethodSurface();
600676
601− // there should be only unity8-dash window over there
602− tryCompare(ApplicationManager, "count", 1);
603− compare(ApplicationManager.get(0).appId, "unity8-dash");
677+ tryCompareFunction(function() { return root.topLevelSurfaceList.inputMethodSurface !== null }, true);
604678 }
605679
606680 function test_snapDecisionDismissalReturnsFocus() {
607681 loadShell("phone");
608682 swipeAwayGreeter();
609683 var notifications = findChild(shell, "notificationList");
610− var appSurfaceId = topLevelSurfaceList.nextId;
611− var app = ApplicationManager.startApplication("camera-app");
612− waitUntilAppWindowIsFullyLoaded(appSurfaceId);
613− var stage = findChild(shell, "stage")
684+ var appDelegate = startApplication("camera-app");
614685
615− var appSurface = app.surfaceList.get(0);
686+ var appSurface = appDelegate.surface;
616687 verify(appSurface);
617688
618689 tryCompare(appSurface, "activeFocus", true);
@@ -801,59 +872,38 @@ Rectangle {
801872 }
802873
803874 function setLightDMMockMode(mode) {
804− LightDM.Users.mock.userMode = mode;
875+ LightDMController.userMode = mode;
805876 }
806877
807878 function test_showInputMethod() {
808879 loadShell("phone");
809880 swipeAwayGreeter();
881+ ensureInputMethodSurface();
882+
810883 var item = findChild(shell, "inputMethod");
811− var surface = SurfaceManager.inputMethodSurface;
884+ var surface = topLevelSurfaceList.inputMethodSurface;
812885
813− surface.setState(Mir.MinimizedState);
886+ surface.requestState(Mir.MinimizedState);
814887 tryCompare(item, "visible", false);
815888
816− surface.setState(Mir.RestoredState);
889+ surface.requestState(Mir.RestoredState);
817890 tryCompare(item, "visible", true);
818891
819− surface.setState(Mir.MinimizedState);
892+ surface.requestState(Mir.MinimizedState);
820893 tryCompare(item, "visible", false);
821894
822− surface.setState(Mir.MaximizedState);
895+ surface.requestState(Mir.MaximizedState);
823896 tryCompare(item, "visible", true);
824897
825− surface.setState(Mir.MinimizedState);
898+ surface.requestState(Mir.MinimizedState);
826899 tryCompare(item, "visible", false);
827900 }
828901
829− function findAppWindowForSurfaceId(surfaceId) {
830− // for PhoneStage and TabletStage
831− var delegate = findChild(shell, "spreadDelegate_" + surfaceId, 0 /* timeout */);
832− if (!delegate) {
833− // for DesktopStage
834− delegate = findChild(shell, "appDelegate_" + surfaceId);
835− }
836− verify(delegate);
837− var appWindow = findChild(delegate, "appWindow");
838− return appWindow;
839− }
840−
841− // Wait until the ApplicationWindow for the given Application object is fully loaded
842− // (ie, the real surface has replaced the splash screen)
843− function waitUntilAppWindowIsFullyLoaded(surfaceId) {
844− var appWindow = findAppWindowForSurfaceId(surfaceId);
845− var appWindowStateGroup = findInvisibleChild(appWindow, "applicationWindowStateGroup");
846− tryCompareFunction(function() { return appWindowStateGroup.state === "surface" }, true);
847− waitUntilTransitionsEnd(appWindowStateGroup);
848− }
849−
850902 function test_surfaceLosesActiveFocusWhilePanelIsOpen() {
851903 loadShell("phone");
852904 swipeAwayGreeter();
853− var appSurfaceId = topLevelSurfaceList.nextId;
854− var app = ApplicationManager.startApplication("dialer-app");
855− waitUntilAppWindowIsFullyLoaded(appSurfaceId);
856− var appSurface = app.surfaceList.get(0);
905+ var appDelegate = startApplication("dialer-app");
906+ var appSurface = appDelegate.surface;
857907 verify(appSurface);
858908
859909 tryCompare(appSurface, "activeFocus", true);
@@ -896,15 +946,9 @@ Rectangle {
896946 shell.usageScenario = data.usageScenario;
897947 swipeAwayGreeter();
898948
899− var webAppSurfaceId = topLevelSurfaceList.nextId;
900− var webApp = ApplicationManager.startApplication("webbrowser-app");
901− verify(webApp);
902− waitUntilAppWindowIsFullyLoaded(webAppSurfaceId);
949+ var appDelegate = startApplication("webbrowser-app");
903950
904− var webAppSurface = webApp.surfaceList.get(topLevelSurfaceList.indexForId(webAppSurfaceId));
905− verify(webAppSurface);
906−
907− tryCompare(webAppSurface, "activeFocus", true);
951+ tryCompare(appDelegate.surface, "activeFocus", true);
908952 }
909953
910954 function test_launchedAppKeepsActiveFocusOnUsageModeChange() {
@@ -2120,7 +2164,6 @@ Rectangle {
21202164 var firstAppInLauncher = LauncherModel.get(0).appId;
21212165 compare(launcher.state, data.launcherLocked ? "visible": "");
21222166 compare(launcherPanel.highlightIndex, -2);
2123− compare(ApplicationManager.focusedApplicationId, "unity8-dash");
21242167
21252168 // Use Super + Tab Tab to cycle to the first entry in the launcher
21262169 keyPress(Qt.Key_Super_L, Qt.MetaModifier);
@@ -2250,7 +2293,7 @@ Rectangle {
22502293
22512294 compare(launcher.lockedVisible, true);
22522295
2253− app.surfaceList.get(0).state = Mir.FullscreenState;
2296+ app.surfaceList.get(0).requestState(Mir.FullscreenState);
22542297
22552298 tryCompare(launcher, "lockedVisible", false);
22562299 }
@@ -2381,13 +2424,13 @@ Rectangle {
23812424 var gallerySurfaceId = topLevelSurfaceList.nextId;
23822425 var galleryApp = ApplicationManager.startApplication("gallery-app");
23832426 waitUntilAppWindowIsFullyLoaded(gallerySurfaceId);
2384− compare(ApplicationManager.focusedApplicationId, "gallery-app");
2427+ compare(topLevelSurfaceList.applicationAt(0).appId, "gallery-app");
23852428
23862429 dragLauncherIntoView();
23872430 tap(findChild(shell, "buttonShowDashHome"));
23882431
23892432 tryCompare(broadcastHomeSpy, "count", 1);
2390− compare(ApplicationManager.focusedApplicationId, "gallery-app"); // confirm we didn't focus dash
2433+ compare(topLevelSurfaceList.applicationAt(0).appId, "gallery-app"); // confirm we didn't raise dash
23912434
23922435 var coverPage = findChild(shell, "coverPage");
23932436 tryCompare(coverPage, "showProgress", 0);
@@ -2432,12 +2475,12 @@ Rectangle {
24322475 tryCompare(appSurface, "keymap", "sk");
24332476
24342477 // try to create a prompt surface, verify it also has the same keymap
2435− app.promptSurfaceList.createSurface();
2478+ app.createPromptSurface();
24362479 var promptSurface = app.promptSurfaceList.get(0);
24372480 verify(promptSurface);
24382481 tryCompare(appSurface, "keymap", promptSurface.keymap);
24392482 // ... and that the controller's surface keymap is also the same
2440− tryCompare(MirFocusController.focusedSurface, "keymap", "sk");
2483+ tryCompare(topLevelSurfaceList.focusedWindow.surface, "keymap", "sk");
24412484 app.promptSurfaceList.get(0).close();
24422485
24432486 // switch to next keymap, should go to "cz+qwerty"
@@ -2449,7 +2492,7 @@ Rectangle {
24492492 tryCompare(appSurface, "keymap", "fr");
24502493
24512494 // go to e.g. desktop stage
2452− loadShell("desktop");
2495+ shellLoader.state = "desktop";
24532496 shell.usageScenario = "desktop";
24542497 waitForRendering(shell);
24552498
@@ -2497,11 +2540,13 @@ Rectangle {
24972540 var appContainer = findChild(shell, "appContainer");
24982541 var appDelegate = findChild(appContainer, "appDelegate_" + appSurfaceId);
24992542 verify(appDelegate);
2500− appDelegate.maximize();
2543+ var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
2544+ mouseClick(maximizeButton);
2545+
25012546 tryCompare(appDelegate, "state", "maximized");
25022547
25032548 mouseDrag(panel, panel.width/3, panel.height/2, 0, shell.height/3, Qt.LeftButton, Qt.NoModifier, 500);
2504− tryCompare(appDelegate, "state", "normal");
2549+ tryCompare(appDelegate, "state", "restored");
25052550 }
25062551
25072552 function test_fullShellModeHasNoInitialGreeter() {
@@ -2555,7 +2600,6 @@ Rectangle {
25552600 return [
25562601 {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: units.gu(5), oskEnabled: true},
25572602 {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
2558− {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
25592603 {tag: "osk not on this screen", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: units.gu(10), oskEnabled: false},
25602604 ]
25612605 }
@@ -2565,26 +2609,28 @@ Rectangle {
25652609 shell.usageScenario = "desktop";
25662610 waitForRendering(shell);
25672611 swipeAwayGreeter();
2612+ ensureInputMethodSurface();
25682613 shell.oskEnabled = data.oskEnabled;
25692614
2570− var oldOSKState = SurfaceManager.inputMethodSurface.state;
2571− SurfaceManager.inputMethodSurface.state = Mir.RestoredState;
2615+ var oldOSKState = topLevelSurfaceList.inputMethodSurface.state;
2616+ topLevelSurfaceList.inputMethodSurface.requestState(Mir.RestoredState);
25722617 var appRepeater = findChild(shell, "appRepeater");
25732618 var dashAppDelegate = appRepeater.itemAt(0);
25742619 verify(dashAppDelegate);
2575− dashAppDelegate.requestedHeight = data.windowHeight;
2576− dashAppDelegate.requestedY = data.windowY;
2577− SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
2620+
2621+ dashAppDelegate.windowedHeight = data.windowHeight;
2622+ dashAppDelegate.windowedY = data.windowY;
2623+ topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
25782624 var initialY = dashAppDelegate.y;
25792625 print("intial", initialY, "panel", PanelState.panelHeight);
25802626 verify(initialY > PanelState.panelHeight);
25812627
2582− SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
2628+ topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
25832629 tryCompare(dashAppDelegate, "y", data.targetDisplacement);
25842630
2585− SurfaceManager.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
2631+ topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
25862632 tryCompare(dashAppDelegate, "y", initialY);
2587− SurfaceManager.inputMethodSurface.state = oldOSKState;
2633+ topLevelSurfaceList.inputMethodSurface.requestState(oldOSKState);
25882634 }
25892635
25902636 function test_cursorHidingWithFullscreenApp() {
tests/qmltests/tst_ShellWithPin.qml+20−10
@@ -19,6 +19,7 @@ import Ubuntu.Components 1.3
1919import QtTest 1.0
2020import AccountsService 0.1
2121import GSettings 1.0
22+import LightDMController 0.1
2223import LightDM.FullLightDM 0.1 as LightDM
2324import Ubuntu.SystemImage 0.1
2425import Ubuntu.Telephony 0.1 as Telephony
@@ -35,7 +36,7 @@ Item {
3536
3637 Component.onCompleted: {
3738 // must set the mock mode before loading the Shell
38− LightDM.Users.mock.userMode = "single-pin";
39+ LightDMController.userMode = "single-pin";
3940 shellLoader.active = true;
4041 }
4142
@@ -141,7 +142,7 @@ Item {
141142 }
142143 }
143144
144− UT.UnityTestCase {
145+ UT.StageTestCase {
145146 id: testCase
146147 name: "ShellWithPin"
147148 when: windowShown
@@ -160,11 +161,20 @@ Item {
160161 var panel = findChild(launcher, "launcherPanel");
161162 verify(!!panel);
162163 panel.dismissTimer = fakeDismissTimer;
164+
165+ // from StageTestCase
166+ stage = findChild(shell, "stage");
167+ topLevelSurfaceList = findInvisibleChild(shell, "topLevelSurfaceList");
168+ verify(topLevelSurfaceList);
169+
170+ startApplication("unity8-dash");
163171 }
164172
165173 function cleanup() {
166174 tryCompare(shell, "waitingOnGreeter", false); // make sure greeter didn't leave us in disabled state
167175
176+ topLevelSurfaceList = null;
177+
168178 shellLoader.itemDestroyed = false
169179
170180 shellLoader.active = false
@@ -300,13 +310,15 @@ Item {
300310 }
301311
302312 function test_emergencyCallCrash() {
313+ var dialerSurfaceId = topLevelSurfaceList.nextId;
303314 var greeter = findChild(shell, "greeter");
304315 var emergencyButton = findChild(greeter, "emergencyCallLabel");
305316 tap(emergencyButton)
306−
317+ tryCompare(topLevelSurfaceList, "count", 2);
318+ waitUntilAppWindowIsFullyLoaded(dialerSurfaceId);
307319
308320 tryCompare(greeter, "shown", false);
309− killApps() // kill dialer-app, as if it crashed
321+ ApplicationManager.stopApplication("dialer-app"); // kill dialer-app, as if it crashed
310322 tryCompare(greeter, "shown", true);
311323 tryCompare(findChild(greeter, "lockscreen"), "shown", true);
312324 tryCompare(findChild(greeter, "coverPage"), "shown", false);
@@ -318,7 +330,7 @@ Item {
318330 tap(emergencyButton)
319331
320332 tryCompare(greeter, "shown", false);
321− ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
333+ startApplication("gallery-app");
322334 tryCompare(greeter, "shown", true);
323335 }
324336
@@ -405,7 +417,7 @@ Item {
405417 compare(stage.usageScenario, "phone");
406418
407419 // And when we kill the app, we go back to locked tablet mode
408− killApps()
420+ ApplicationManager.stopApplication("dialer-app");
409421 var greeter = findChild(shell, "greeter")
410422 tryCompare(greeter, "fullyShown", true)
411423 compare(stage.usageScenario, "tablet");
@@ -456,7 +468,7 @@ Item {
456468 tryCompare(greeter, "shown", false);
457469 tryCompare(LightDM.Greeter, "active", false);
458470
459− ApplicationManager.startApplication("dialer-app", ApplicationManager.NoFlag);
471+ startApplication("dialer-app");
460472 tryCompare(ApplicationManager, "focusedApplicationId", "dialer-app");
461473 callManager.foregroundCall = phoneCall;
462474
@@ -475,9 +487,7 @@ Item {
475487 function test_focusRequestedHidesCoverPage() {
476488 showGreeter();
477489
478− var app = ApplicationManager.startApplication("gallery-app");
479− // wait until the app is fully loaded (ie, real surface replaces splash screen)
480− tryCompareFunction(function() { return app.session !== null && app.surfaceList.count > 0 }, true);
490+ startApplication("gallery-app");
481491
482492 // New app hides coverPage?
483493 var greeter = findChild(shell, "greeter");
tests/utils/modules/Unity/Test/StageTestCase.qmlnew file+68
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright 2016 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 Unity.Application 0.1
19+import Ubuntu.Components 1.3
20+
21+UnityTestCase {
22+ // set from outside
23+ property Item stage
24+ property QtObject topLevelSurfaceList: null
25+
26+ /*
27+ Wait until the ApplicationWindow for the given surface id (from TopLevelWindowModel) is fully loaded
28+ (ie, the real surface has replaced the splash screen)
29+ */
30+ function waitUntilAppWindowIsFullyLoaded(surfaceId) {
31+ var appDelegate = findChild(stage, "appDelegate_" + surfaceId);
32+ verify(appDelegate);
33+ var appWindow = findChild(appDelegate, "appWindow");
34+ verify(appWindow);
35+ var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
36+ verify(appWindowStates);
37+ tryCompare(appWindowStates, "state", "surface");
38+ waitUntilTransitionsEnd(appWindowStates);
39+ }
40+
41+ /*
42+ Returns the appDelegate of the first surface created by the app with the specified appId
43+ */
44+ function startApplication(appId) {
45+ try {
46+ var app = ApplicationManager.findApplication(appId);
47+ if (app) {
48+ for (var i = 0; i < topLevelSurfaceList.count; i++) {
49+ if (topLevelSurfaceList.applicationAt(i).appId === appId) {
50+ var appRepeater = findChild(stage, "appRepeater");
51+ verify(appRepeater);
52+ return appRepeater.itemAt(i);
53+ }
54+ }
55+ }
56+
57+ var surfaceId = topLevelSurfaceList.nextId;
58+ app = ApplicationManager.startApplication(appId);
59+ verify(app);
60+ waitUntilAppWindowIsFullyLoaded(surfaceId);
61+ compare(app.surfaceList.count, 1);
62+
63+ return findChild(stage, "appDelegate_" + surfaceId);
64+ } catch(err) {
65+ throw new Error("startApplication("+appId+") called from line " + util.callerLine(1) + " failed!");
66+ }
67+ }
68+}
tests/utils/modules/Unity/Test/UnityTestCase.qml+4−5
@@ -638,18 +638,17 @@ TestCase {
638638 }
639639
640640 /*
641− kill all (fake) running apps but unity8-dash, bringing Unity.Application back to its initial state
641+ kill all (fake) running apps, bringing Unity.Application back to its initial state
642642 */
643643 function killApps() {
644− while (ApplicationManager.count > 1) {
645− var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
646− var application = ApplicationManager.get(appIndex);
644+ while (ApplicationManager.count > 0) {
645+ var application = ApplicationManager.get(0);
647646 ApplicationManager.stopApplication(application.appId);
648647 // wait until all zombie surfaces are gone. As MirSurfaceItems hold references over them.
649648 // They won't be gone until those surface items are destroyed.
650649 tryCompareFunction(function() { return application.surfaceList.count }, 0);
651650 tryCompare(application, "state", ApplicationInfo.Stopped);
652651 }
653− compare(ApplicationManager.count, 1);
652+ compare(ApplicationManager.count, 0);
654653 }
655654}
tests/utils/modules/Unity/Test/qmldir+2−1
@@ -3,4 +3,5 @@ plugin UnityTestQml
33typeinfo Test.qmltypes
44
55UnityTestCase 0.1 UnityTestCase.qml
6−MouseTouchEmulationCheckbox 0.1 MouseTouchEmulationCheckbox.qml
6+StageTestCase 0.1 StageTestCase.qml
7+MouseTouchEmulationCheckbox 0.1 MouseTouchEmulationCheckbox.qml