2 * Copyright (C) 2016 Canonical, Ltd.
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.
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.
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/>.
17#include "TopLevelSurfaceList.h"
20#include <unity/shell/application/ApplicationInfoInterface.h>
21#include <unity/shell/application/MirSurfaceInterface.h>
22#include <unity/shell/application/MirSurfaceListInterface.h>
24Q_LOGGING_CATEGORY(UNITY_TOPSURFACELIST, "unity.topsurfacelist")
26#define DEBUG_MSG qCDebug(UNITY_TOPSURFACELIST).nospace().noquote() << __func__
28using namespace unity::shell::application;
30TopLevelSurfaceList::TopLevelSurfaceList(QObject *parent) :
31 QAbstractListModel(parent)
36TopLevelSurfaceList::~TopLevelSurfaceList()
41int TopLevelSurfaceList::rowCount(const QModelIndex &parent) const
43 return !parent.isValid() ? m_surfaceList.size() : 0;
46QVariant TopLevelSurfaceList::data(const QModelIndex& index, int role) const
48 if (index.row() < 0 || index.row() >= m_surfaceList.size())
51 if (role == SurfaceRole) {
52 MirSurfaceInterface *surface = m_surfaceList.at(index.row()).surface;
53 return QVariant::fromValue(surface);
54 } else if (role == ApplicationRole) {
55 return QVariant::fromValue(m_surfaceList.at(index.row()).application);
56 } else if (role == IdRole) {
57 return QVariant::fromValue(m_surfaceList.at(index.row()).id);
63void TopLevelSurfaceList::raise(MirSurfaceInterface *surface)
68 DEBUG_MSG << "(MirSurface[" << (void*)surface << "])";
70 int i = indexOf(surface);
76void TopLevelSurfaceList::prependPlaceholder(ApplicationInfoInterface *application)
78 DEBUG_MSG << "(" << application->appId() << ")";
79 prependSurfaceHelper(nullptr, application);
82void TopLevelSurfaceList::prependSurface(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
84 Q_ASSERT(surface != nullptr);
86 bool filledPlaceholder = false;
87 for (int i = 0; i < m_surfaceList.count() && !filledPlaceholder; ++i) {
88 ModelEntry &entry = m_surfaceList[i];
89 if (entry.application == application && entry.surface == nullptr) {
90 entry.surface = surface;
91 connectSurface(surface);
92 DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", filling out placeholder. after: " << toString();
93 Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
94 filledPlaceholder = true;
98 if (!filledPlaceholder) {
99 DEBUG_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
100 prependSurfaceHelper(surface, application);
104void TopLevelSurfaceList::prependSurfaceHelper(MirSurfaceInterface *surface, ApplicationInfoInterface *application)
106 beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
107 int id = generateId();
108 m_surfaceList.prepend(ModelEntry(surface, application, id));
110 connectSurface(surface);
113 DEBUG_MSG << " after " << toString();
114 Q_EMIT countChanged();
115 Q_EMIT listChanged();
118void TopLevelSurfaceList::connectSurface(MirSurfaceInterface *surface)
120 connect(surface, &MirSurfaceInterface::focusedChanged, this, [this, surface](bool focused){
122 this->raise(surface);
125 connect(surface, &MirSurfaceInterface::liveChanged, this, [this, surface](bool live){
127 onSurfaceDied(surface);
130 connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); });
133void TopLevelSurfaceList::onSurfaceDied(MirSurfaceInterface *surface)
135 int i = indexOf(surface);
140 auto application = m_surfaceList[i].application;
142 // can't be starting if it already has a surface
143 Q_ASSERT(application->state() != ApplicationInfoInterface::Starting);
145 if (application->state() == ApplicationInfoInterface::Running) {
146 m_surfaceList[i].removeOnceSurfaceDestroyed = true;
148 // assume it got killed by the out-of-memory daemon.
150 // So leave entry in the model and only remove its surface, so shell can display a screenshot
152 m_surfaceList[i].removeOnceSurfaceDestroyed = false;
156void TopLevelSurfaceList::onSurfaceDestroyed(MirSurfaceInterface *surface)
158 int i = indexOf(surface);
163 if (m_surfaceList[i].removeOnceSurfaceDestroyed) {
166 m_surfaceList[i].surface = nullptr;
167 Q_EMIT dataChanged(index(i) /* topLeft */, index(i) /* bottomRight */, QVector<int>() << SurfaceRole);
168 DEBUG_MSG << " Removed surface from entry. After: " << toString();
172void TopLevelSurfaceList::removeAt(int index)
174 beginRemoveRows(QModelIndex(), index, index);
175 m_surfaceList.removeAt(index);
177 DEBUG_MSG << " after " << toString();
178 Q_EMIT countChanged();
179 Q_EMIT listChanged();
182int TopLevelSurfaceList::indexOf(MirSurfaceInterface *surface)
184 for (int i = 0; i < m_surfaceList.count(); ++i) {
185 if (m_surfaceList.at(i).surface == surface) {
192void TopLevelSurfaceList::moveSurface(int from, int to)
194 if (from == to) return;
195 DEBUG_MSG << " from=" << from << " to=" << to;
197 if (from >= 0 && from < m_surfaceList.size() && to >= 0 && to < m_surfaceList.size()) {
199 /* When moving an item down, the destination index needs to be incremented
200 by one, as explained in the documentation:
201 http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
203 beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
204 m_surfaceList.move(from, to);
205 DEBUG_MSG << " after " << toString();
207 Q_EMIT listChanged();
211MirSurfaceInterface *TopLevelSurfaceList::surfaceAt(int index) const
213 if (index >=0 && index < m_surfaceList.count()) {
214 return m_surfaceList[index].surface;
220ApplicationInfoInterface *TopLevelSurfaceList::applicationAt(int index) const
222 if (index >=0 && index < m_surfaceList.count()) {
223 return m_surfaceList[index].application;
229int TopLevelSurfaceList::idAt(int index) const
231 if (index >=0 && index < m_surfaceList.count()) {
232 return m_surfaceList[index].id;
238int TopLevelSurfaceList::indexForId(int id) const
240 for (int i = 0; i < m_surfaceList.count(); ++i) {
241 if (m_surfaceList[i].id == id) {
248void TopLevelSurfaceList::move(int id, int toIndex)
250 DEBUG_MSG << " id=" << id << " toIndex=" << toIndex;
251 int fromIndex = indexForId(id);
252 if (fromIndex != -1) {
253 moveSurface(fromIndex, toIndex);
257int TopLevelSurfaceList::generateId()
260 m_nextId = nextFreeId(m_nextId + 1);
261 Q_EMIT nextIdChanged();
265int TopLevelSurfaceList::nextFreeId(int candidateId)
267 if (candidateId > m_maxId) {
268 return nextFreeId(1);
270 if (indexForId(candidateId) == -1) {
274 return nextFreeId(candidateId + 1);
279QString TopLevelSurfaceList::toString()
282 for (int i = 0; i < m_surfaceList.count(); ++i) {
283 auto item = m_surfaceList.at(i);
285 QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)")
287 .arg(item.application->appId())
288 .arg((qintptr)item.surface, 0, 16)
299void TopLevelSurfaceList::addApplication(ApplicationInfoInterface *application)
301 DEBUG_MSG << "(" << application->appId() << ")";
302 Q_ASSERT(!m_applications.contains(application));
303 m_applications.append(application);
305 MirSurfaceListInterface *surfaceList = application->surfaceList();
307 if (application->state() != ApplicationInfoInterface::Stopped) {
308 if (surfaceList->count() == 0) {
309 prependPlaceholder(application);
311 for (int i = 0; i < surfaceList->count(); ++i) {
312 prependSurface(surfaceList->get(i), application);
317 connect(surfaceList, &QAbstractItemModel::rowsInserted, this,
318 [this, application, surfaceList](const QModelIndex & /*parent*/, int first, int last)
320 for (int i = last; i >= first; --i) {
321 this->prependSurface(surfaceList->get(i), application);
326void TopLevelSurfaceList::removeApplication(ApplicationInfoInterface *application)
328 DEBUG_MSG << "(" << application->appId() << ")";
329 Q_ASSERT(m_applications.contains(application));
331 MirSurfaceListInterface *surfaceList = application->surfaceList();
333 disconnect(surfaceList, 0, this, 0);
336 while (i < m_surfaceList.count()) {
337 if (m_surfaceList.at(i).application == application) {
338 beginRemoveRows(QModelIndex(), i, i);
339 m_surfaceList.removeAt(i);
341 Q_EMIT countChanged();
342 Q_EMIT listChanged();
348 DEBUG_MSG << " after " << toString();
350 m_applications.removeAll(application);
353QAbstractListModel *TopLevelSurfaceList::applicationsModel() const
355 return m_applicationsModel;
358void TopLevelSurfaceList::setApplicationsModel(QAbstractListModel* value)
360 if (m_applicationsModel == value) {
364 DEBUG_MSG << "(" << value << ")";
368 if (m_applicationsModel) {
369 m_surfaceList.clear();
370 m_applications.clear();
371 disconnect(m_applicationsModel, 0, this, 0);
374 m_applicationsModel = value;
376 if (m_applicationsModel) {
377 findApplicationRole();
379 connect(m_applicationsModel, &QAbstractItemModel::rowsInserted,
380 this, [this](const QModelIndex &/*parent*/, int first, int last) {
381 for (int i = first; i <= last; ++i) {
382 auto application = getApplicationFromModelAt(i);
383 addApplication(application);
387 connect(m_applicationsModel, &QAbstractItemModel::rowsAboutToBeRemoved,
388 this, [this](const QModelIndex &/*parent*/, int first, int last) {
389 for (int i = first; i <= last; ++i) {
390 auto application = getApplicationFromModelAt(i);
391 removeApplication(application);
395 for (int i = 0; i < m_applicationsModel->rowCount(); ++i) {
396 auto application = getApplicationFromModelAt(i);
397 addApplication(application);
404ApplicationInfoInterface *TopLevelSurfaceList::getApplicationFromModelAt(int index)
406 QModelIndex modelIndex = m_applicationsModel->index(index);
408 QVariant variant = m_applicationsModel->data(modelIndex, m_applicationRole);
410 // variant.value<ApplicationInfoInterface*>() returns null for some reason.
411 return static_cast<ApplicationInfoInterface*>(variant.value<QObject*>());
414void TopLevelSurfaceList::findApplicationRole()
416 QHash<int, QByteArray> namesHash = m_applicationsModel->roleNames();
418 m_applicationRole = -1;
419 for (auto i = namesHash.begin(); i != namesHash.end() && m_applicationRole == -1; ++i) {
420 if (i.value() == "application") {
421 m_applicationRole = i.key();
425 if (m_applicationRole == -1) {
426 qFatal("TopLevelSurfaceList: applicationsModel must have a \"application\" role.");
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list