You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
921 lines
26 KiB
921 lines
26 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
#include "scripting_model.h" |
|
#include <config-kwin.h> |
|
#ifdef KWIN_BUILD_ACTIVITIES |
|
#include "activities.h" |
|
#endif |
|
#include "x11client.h" |
|
#include "screens.h" |
|
#include "workspace.h" |
|
#include "wayland_server.h" |
|
|
|
namespace KWin { |
|
namespace ScriptingClientModel { |
|
|
|
static quint32 nextId() { |
|
static quint32 counter = 0; |
|
return ++counter; |
|
} |
|
|
|
ClientLevel::ClientLevel(ClientModel *model, AbstractLevel *parent) |
|
: AbstractLevel(model, parent) |
|
{ |
|
connect(Workspace::self(), &Workspace::clientAdded, this, &ClientLevel::clientAdded); |
|
connect(Workspace::self(), &Workspace::clientRemoved, this, &ClientLevel::clientRemoved); |
|
connect(model, SIGNAL(exclusionsChanged()), SLOT(reInit())); |
|
if (waylandServer()) { |
|
connect(waylandServer(), &WaylandServer::shellClientAdded, this, &ClientLevel::clientAdded); |
|
} |
|
} |
|
|
|
ClientLevel::~ClientLevel() |
|
{ |
|
} |
|
|
|
void ClientLevel::clientAdded(AbstractClient *client) |
|
{ |
|
setupClientConnections(client); |
|
checkClient(client); |
|
} |
|
|
|
void ClientLevel::clientRemoved(AbstractClient *client) |
|
{ |
|
removeClient(client); |
|
} |
|
|
|
void ClientLevel::setupClientConnections(AbstractClient *client) |
|
{ |
|
auto check = [this, client] { |
|
checkClient(client); |
|
}; |
|
connect(client, &AbstractClient::desktopChanged, this, check); |
|
connect(client, &AbstractClient::screenChanged, this, check); |
|
connect(client, &AbstractClient::activitiesChanged, this, check); |
|
connect(client, &AbstractClient::windowHidden, this, check); |
|
connect(client, &AbstractClient::windowShown, this, check); |
|
} |
|
|
|
void ClientLevel::checkClient(AbstractClient *client) |
|
{ |
|
const bool shouldInclude = !exclude(client) && shouldAdd(client); |
|
const bool contains = containsClient(client); |
|
|
|
if (shouldInclude && !contains) { |
|
addClient(client); |
|
} else if (!shouldInclude && contains) { |
|
removeClient(client); |
|
} |
|
} |
|
|
|
bool ClientLevel::exclude(AbstractClient *client) const |
|
{ |
|
ClientModel::Exclusions exclusions = model()->exclusions(); |
|
if (exclusions == ClientModel::NoExclusion) { |
|
return false; |
|
} |
|
if (exclusions & ClientModel::DesktopWindowsExclusion) { |
|
if (client->isDesktop()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::DockWindowsExclusion) { |
|
if (client->isDock()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::UtilityWindowsExclusion) { |
|
if (client->isUtility()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::SpecialWindowsExclusion) { |
|
if (client->isSpecialWindow()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::SkipTaskbarExclusion) { |
|
if (client->skipTaskbar()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::SkipPagerExclusion) { |
|
if (client->skipPager()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::SwitchSwitcherExclusion) { |
|
if (client->skipSwitcher()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::OtherDesktopsExclusion) { |
|
if (!client->isOnCurrentDesktop()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::OtherActivitiesExclusion) { |
|
if (!client->isOnCurrentActivity()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::MinimizedExclusion) { |
|
if (client->isMinimized()) { |
|
return true; |
|
} |
|
} |
|
if (exclusions & ClientModel::NotAcceptingFocusExclusion) { |
|
if (!client->wantsInput()) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool ClientLevel::shouldAdd(AbstractClient *client) const |
|
{ |
|
if (restrictions() == ClientModel::NoRestriction) { |
|
return true; |
|
} |
|
if (restrictions() & ClientModel::ActivityRestriction) { |
|
if (!client->isOnActivity(activity())) { |
|
return false; |
|
} |
|
} |
|
if (restrictions() & ClientModel::VirtualDesktopRestriction) { |
|
if (!client->isOnDesktop(virtualDesktop())) { |
|
return false; |
|
} |
|
} |
|
if (restrictions() & ClientModel::ScreenRestriction) { |
|
if (client->screen() != int(screen())) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
void ClientLevel::addClient(AbstractClient *client) |
|
{ |
|
if (containsClient(client)) { |
|
return; |
|
} |
|
emit beginInsert(m_clients.count(), m_clients.count(), id()); |
|
m_clients.insert(nextId(), client); |
|
emit endInsert(); |
|
} |
|
|
|
void ClientLevel::removeClient(AbstractClient *client) |
|
{ |
|
int index = 0; |
|
auto it = m_clients.begin(); |
|
for (; it != m_clients.end(); ++it, ++index) { |
|
if (it.value() == client) { |
|
break; |
|
} |
|
} |
|
if (it == m_clients.end()) { |
|
return; |
|
} |
|
emit beginRemove(index, index, id()); |
|
m_clients.erase(it); |
|
emit endRemove(); |
|
} |
|
|
|
void ClientLevel::init() |
|
{ |
|
const QList<X11Client *> &clients = Workspace::self()->clientList(); |
|
for (auto it = clients.begin(); it != clients.end(); ++it) { |
|
X11Client *client = *it; |
|
setupClientConnections(client); |
|
if (!exclude(client) && shouldAdd(client)) { |
|
m_clients.insert(nextId(), client); |
|
} |
|
} |
|
} |
|
|
|
void ClientLevel::reInit() |
|
{ |
|
const QList<X11Client *> &clients = Workspace::self()->clientList(); |
|
for (auto it = clients.begin(); it != clients.end(); ++it) { |
|
checkClient((*it)); |
|
} |
|
if (waylandServer()) { |
|
const auto &clients = waylandServer()->clients(); |
|
for (auto *c : clients) { |
|
checkClient(c); |
|
} |
|
} |
|
} |
|
|
|
quint32 ClientLevel::idForRow(int row) const |
|
{ |
|
if (row >= m_clients.size()) { |
|
return 0; |
|
} |
|
auto it = m_clients.constBegin(); |
|
for (int i=0; i<row; ++i) { |
|
++it; |
|
} |
|
return it.key(); |
|
} |
|
|
|
bool ClientLevel::containsId(quint32 id) const |
|
{ |
|
return m_clients.contains(id); |
|
} |
|
|
|
int ClientLevel::rowForId(quint32 id) const |
|
{ |
|
int row = 0; |
|
for (auto it = m_clients.constBegin(); |
|
it != m_clients.constEnd(); |
|
++it, ++row) { |
|
if (it.key() == id) { |
|
return row; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
AbstractClient *ClientLevel::clientForId(quint32 child) const |
|
{ |
|
auto it = m_clients.constFind(child); |
|
if (it == m_clients.constEnd()) { |
|
return nullptr; |
|
} |
|
return it.value(); |
|
} |
|
|
|
bool ClientLevel::containsClient(AbstractClient *client) const |
|
{ |
|
for (auto it = m_clients.constBegin(); |
|
it != m_clients.constEnd(); |
|
++it) { |
|
if (it.value() == client) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
const AbstractLevel *ClientLevel::levelForId(quint32 id) const |
|
{ |
|
if (id == AbstractLevel::id()) { |
|
return this; |
|
} |
|
return nullptr; |
|
} |
|
|
|
AbstractLevel *ClientLevel::parentForId(quint32 child) const |
|
{ |
|
if (child == id()) { |
|
return parentLevel(); |
|
} |
|
if (m_clients.contains(child)) { |
|
return const_cast<ClientLevel*>(this); |
|
} |
|
return nullptr; |
|
} |
|
|
|
AbstractLevel *AbstractLevel::create(const QList< ClientModel::LevelRestriction > &restrictions, ClientModel::LevelRestrictions parentRestrictions, ClientModel *model, AbstractLevel *parent) |
|
{ |
|
if (restrictions.isEmpty() || restrictions.first() == ClientModel::NoRestriction) { |
|
ClientLevel *leaf = new ClientLevel(model, parent); |
|
leaf->setRestrictions(parentRestrictions); |
|
if (!parent) { |
|
leaf->setParent(model); |
|
} |
|
return leaf; |
|
} |
|
// create a level |
|
QList<ClientModel::LevelRestriction> childRestrictions(restrictions); |
|
ClientModel::LevelRestriction restriction = childRestrictions.takeFirst(); |
|
ClientModel::LevelRestrictions childrenRestrictions = restriction | parentRestrictions; |
|
ForkLevel *currentLevel = new ForkLevel(childRestrictions, model, parent); |
|
currentLevel->setRestrictions(childrenRestrictions); |
|
currentLevel->setRestriction(restriction); |
|
if (!parent) { |
|
currentLevel->setParent(model); |
|
} |
|
switch (restriction) { |
|
case ClientModel::ActivityRestriction: { |
|
#ifdef KWIN_BUILD_ACTIVITIES |
|
if (Activities::self()) { |
|
const QStringList &activities = Activities::self()->all(); |
|
for (QStringList::const_iterator it = activities.begin(); it != activities.end(); ++it) { |
|
AbstractLevel *childLevel = create(childRestrictions, childrenRestrictions, model, currentLevel); |
|
if (!childLevel) { |
|
continue; |
|
} |
|
childLevel->setActivity(*it); |
|
currentLevel->addChild(childLevel); |
|
} |
|
} |
|
break; |
|
#else |
|
return nullptr; |
|
#endif |
|
} |
|
case ClientModel::ScreenRestriction: |
|
for (int i=0; i<screens()->count(); ++i) { |
|
AbstractLevel *childLevel = create(childRestrictions, childrenRestrictions, model, currentLevel); |
|
if (!childLevel) { |
|
continue; |
|
} |
|
childLevel->setScreen(i); |
|
currentLevel->addChild(childLevel); |
|
} |
|
break; |
|
case ClientModel::VirtualDesktopRestriction: |
|
for (uint i=1; i<=VirtualDesktopManager::self()->count(); ++i) { |
|
AbstractLevel *childLevel = create(childRestrictions, childrenRestrictions, model, currentLevel); |
|
if (!childLevel) { |
|
continue; |
|
} |
|
childLevel->setVirtualDesktop(i); |
|
currentLevel->addChild(childLevel); |
|
} |
|
break; |
|
default: |
|
// invalid |
|
return nullptr; |
|
} |
|
|
|
return currentLevel; |
|
} |
|
|
|
AbstractLevel::AbstractLevel(ClientModel *model, AbstractLevel *parent) |
|
: QObject(parent) |
|
, m_model(model) |
|
, m_parent(parent) |
|
, m_screen(0) |
|
, m_virtualDesktop(0) |
|
, m_activity() |
|
, m_restriction(ClientModel::ClientModel::NoRestriction) |
|
, m_restrictions(ClientModel::NoRestriction) |
|
, m_id(nextId()) |
|
{ |
|
} |
|
|
|
AbstractLevel::~AbstractLevel() |
|
{ |
|
} |
|
|
|
void AbstractLevel::setRestriction(ClientModel::LevelRestriction restriction) |
|
{ |
|
m_restriction = restriction; |
|
} |
|
|
|
void AbstractLevel::setActivity(const QString &activity) |
|
{ |
|
m_activity = activity; |
|
} |
|
|
|
void AbstractLevel::setScreen(uint screen) |
|
{ |
|
m_screen = screen; |
|
} |
|
|
|
void AbstractLevel::setVirtualDesktop(uint virtualDesktop) |
|
{ |
|
m_virtualDesktop = virtualDesktop; |
|
} |
|
|
|
void AbstractLevel::setRestrictions(ClientModel::LevelRestrictions restrictions) |
|
{ |
|
m_restrictions = restrictions; |
|
} |
|
|
|
ForkLevel::ForkLevel(const QList<ClientModel::LevelRestriction> &childRestrictions, ClientModel *model, AbstractLevel *parent) |
|
: AbstractLevel(model, parent) |
|
, m_childRestrictions(childRestrictions) |
|
{ |
|
connect(VirtualDesktopManager::self(), SIGNAL(countChanged(uint,uint)), SLOT(desktopCountChanged(uint,uint))); |
|
connect(screens(), SIGNAL(countChanged(int,int)), SLOT(screenCountChanged(int,int))); |
|
#ifdef KWIN_BUILD_ACTIVITIES |
|
if (Activities *activities = Activities::self()) { |
|
connect(activities, SIGNAL(added(QString)), SLOT(activityAdded(QString))); |
|
connect(activities, SIGNAL(removed(QString)), SLOT(activityRemoved(QString))); |
|
} |
|
#endif |
|
} |
|
|
|
ForkLevel::~ForkLevel() |
|
{ |
|
} |
|
|
|
void ForkLevel::desktopCountChanged(uint previousCount, uint newCount) |
|
{ |
|
if (restriction() != ClientModel::ClientModel::VirtualDesktopRestriction) { |
|
return; |
|
} |
|
if (previousCount != uint(count())) { |
|
return; |
|
} |
|
if (previousCount > newCount) { |
|
// desktops got removed |
|
emit beginRemove(newCount, previousCount-1, id()); |
|
while (uint(m_children.count()) > newCount) { |
|
delete m_children.takeLast(); |
|
} |
|
emit endRemove(); |
|
} else { |
|
// desktops got added |
|
emit beginInsert(previousCount, newCount-1, id()); |
|
for (uint i=previousCount+1; i<=newCount; ++i) { |
|
AbstractLevel *childLevel = AbstractLevel::create(m_childRestrictions, restrictions(), model(), this); |
|
if (!childLevel) { |
|
continue; |
|
} |
|
childLevel->setVirtualDesktop(i); |
|
childLevel->init(); |
|
addChild(childLevel); |
|
} |
|
emit endInsert(); |
|
} |
|
} |
|
|
|
void ForkLevel::screenCountChanged(int previousCount, int newCount) |
|
{ |
|
if (restriction() != ClientModel::ClientModel::ClientModel::ScreenRestriction) { |
|
return; |
|
} |
|
if (newCount == previousCount || previousCount != count()) { |
|
return; |
|
} |
|
|
|
if (previousCount > newCount) { |
|
// screens got removed |
|
emit beginRemove(newCount, previousCount-1, id()); |
|
while (m_children.count() > newCount) { |
|
delete m_children.takeLast(); |
|
} |
|
emit endRemove(); |
|
} else { |
|
// screens got added |
|
emit beginInsert(previousCount, newCount-1, id()); |
|
for (int i=previousCount; i<newCount; ++i) { |
|
AbstractLevel *childLevel = AbstractLevel::create(m_childRestrictions, restrictions(), model(), this); |
|
if (!childLevel) { |
|
continue; |
|
} |
|
childLevel->setScreen(i); |
|
childLevel->init(); |
|
addChild(childLevel); |
|
} |
|
emit endInsert(); |
|
} |
|
} |
|
|
|
void ForkLevel::activityAdded(const QString &activityId) |
|
{ |
|
#ifdef KWIN_BUILD_ACTIVITIES |
|
if (restriction() != ClientModel::ClientModel::ActivityRestriction) { |
|
return; |
|
} |
|
// verify that our children do not contain this activity |
|
foreach (AbstractLevel *child, m_children) { |
|
if (child->activity() == activityId) { |
|
return; |
|
} |
|
} |
|
emit beginInsert(m_children.count(), m_children.count(), id()); |
|
AbstractLevel *childLevel = AbstractLevel::create(m_childRestrictions, restrictions(), model(), this); |
|
if (!childLevel) { |
|
emit endInsert(); |
|
return; |
|
} |
|
childLevel->setActivity(activityId); |
|
childLevel->init(); |
|
addChild(childLevel); |
|
emit endInsert(); |
|
#else |
|
Q_UNUSED(activityId) |
|
#endif |
|
} |
|
|
|
void ForkLevel::activityRemoved(const QString &activityId) |
|
{ |
|
#ifdef KWIN_BUILD_ACTIVITIES |
|
if (restriction() != ClientModel::ClientModel::ActivityRestriction) { |
|
return; |
|
} |
|
for (int i=0; i<m_children.length(); ++i) { |
|
if (m_children.at(i)->activity() == activityId) { |
|
emit beginRemove(i, i, id()); |
|
delete m_children.takeAt(i); |
|
emit endRemove(); |
|
break; |
|
} |
|
} |
|
#else |
|
Q_UNUSED(activityId) |
|
#endif |
|
} |
|
|
|
int ForkLevel::count() const |
|
{ |
|
return m_children.count(); |
|
} |
|
|
|
void ForkLevel::addChild(AbstractLevel *child) |
|
{ |
|
m_children.append(child); |
|
connect(child, SIGNAL(beginInsert(int,int,quint32)), SIGNAL(beginInsert(int,int,quint32))); |
|
connect(child, SIGNAL(beginRemove(int,int,quint32)), SIGNAL(beginRemove(int,int,quint32))); |
|
connect(child, SIGNAL(endInsert()), SIGNAL(endInsert())); |
|
connect(child, SIGNAL(endRemove()), SIGNAL(endRemove())); |
|
} |
|
|
|
void ForkLevel::setActivity(const QString &activity) |
|
{ |
|
AbstractLevel::setActivity(activity); |
|
for (QList<AbstractLevel*>::iterator it = m_children.begin(); it != m_children.end(); ++it) { |
|
(*it)->setActivity(activity); |
|
} |
|
} |
|
|
|
void ForkLevel::setScreen(uint screen) |
|
{ |
|
AbstractLevel::setScreen(screen); |
|
for (QList<AbstractLevel*>::iterator it = m_children.begin(); it != m_children.end(); ++it) { |
|
(*it)->setScreen(screen); |
|
} |
|
} |
|
|
|
void ForkLevel::setVirtualDesktop(uint virtualDesktop) |
|
{ |
|
AbstractLevel::setVirtualDesktop(virtualDesktop); |
|
for (QList<AbstractLevel*>::iterator it = m_children.begin(); it != m_children.end(); ++it) { |
|
(*it)->setVirtualDesktop(virtualDesktop); |
|
} |
|
} |
|
|
|
void ForkLevel::init() |
|
{ |
|
for (QList<AbstractLevel*>::iterator it = m_children.begin(); it != m_children.end(); ++it) { |
|
(*it)->init(); |
|
} |
|
} |
|
|
|
quint32 ForkLevel::idForRow(int row) const |
|
{ |
|
if (row >= m_children.length()) { |
|
return 0; |
|
} |
|
return m_children.at(row)->id(); |
|
} |
|
|
|
const AbstractLevel *ForkLevel::levelForId(quint32 id) const |
|
{ |
|
if (id == AbstractLevel::id()) { |
|
return this; |
|
} |
|
for (QList<AbstractLevel*>::const_iterator it = m_children.constBegin(); it != m_children.constEnd(); ++it) { |
|
if (const AbstractLevel *child = (*it)->levelForId(id)) { |
|
return child; |
|
} |
|
} |
|
// not found |
|
return nullptr; |
|
} |
|
|
|
AbstractLevel *ForkLevel::parentForId(quint32 child) const |
|
{ |
|
if (child == id()) { |
|
return parentLevel(); |
|
} |
|
for (QList<AbstractLevel*>::const_iterator it = m_children.constBegin(); it != m_children.constEnd(); ++it) { |
|
if (AbstractLevel *parent = (*it)->parentForId(child)) { |
|
return parent; |
|
} |
|
} |
|
// not found |
|
return nullptr; |
|
} |
|
|
|
int ForkLevel::rowForId(quint32 child) const |
|
{ |
|
if (id() == child) { |
|
return 0; |
|
} |
|
for (int i=0; i<m_children.count(); ++i) { |
|
if (m_children.at(i)->id() == child) { |
|
return i; |
|
} |
|
} |
|
// do recursion |
|
for (QList<AbstractLevel*>::const_iterator it = m_children.constBegin(); it != m_children.constEnd(); ++it) { |
|
int row = (*it)->rowForId(child); |
|
if (row != -1) { |
|
return row; |
|
} |
|
} |
|
// not found |
|
return -1; |
|
} |
|
|
|
AbstractClient *ForkLevel::clientForId(quint32 child) const |
|
{ |
|
for (QList<AbstractLevel*>::const_iterator it = m_children.constBegin(); it != m_children.constEnd(); ++it) { |
|
if (AbstractClient *client = (*it)->clientForId(child)) { |
|
return client; |
|
} |
|
} |
|
// not found |
|
return nullptr; |
|
} |
|
|
|
ClientModel::ClientModel(QObject *parent) |
|
: QAbstractItemModel(parent) |
|
, m_root(nullptr) |
|
, m_exclusions(NoExclusion) |
|
{ |
|
} |
|
|
|
ClientModel::~ClientModel() |
|
{ |
|
} |
|
|
|
void ClientModel::setLevels(QList< ClientModel::LevelRestriction > restrictions) |
|
{ |
|
beginResetModel(); |
|
if (m_root) { |
|
delete m_root; |
|
} |
|
m_root = AbstractLevel::create(restrictions, NoRestriction, this); |
|
connect(m_root, SIGNAL(beginInsert(int,int,quint32)), SLOT(levelBeginInsert(int,int,quint32))); |
|
connect(m_root, SIGNAL(beginRemove(int,int,quint32)), SLOT(levelBeginRemove(int,int,quint32))); |
|
connect(m_root, SIGNAL(endInsert()), SLOT(levelEndInsert())); |
|
connect(m_root, SIGNAL(endRemove()), SLOT(levelEndRemove())); |
|
m_root->init(); |
|
endResetModel(); |
|
} |
|
|
|
void ClientModel::setExclusions(ClientModel::Exclusions exclusions) |
|
{ |
|
if (exclusions == m_exclusions) { |
|
return; |
|
} |
|
m_exclusions = exclusions; |
|
emit exclusionsChanged(); |
|
} |
|
|
|
QVariant ClientModel::data(const QModelIndex &index, int role) const |
|
{ |
|
if (!index.isValid() || index.column() != 0) { |
|
return QVariant(); |
|
} |
|
if (const AbstractLevel *level = getLevel(index)) { |
|
LevelRestriction restriction = level->restriction(); |
|
if (restriction == ActivityRestriction && (role == Qt::DisplayRole || role == ActivityRole)) { |
|
return level->activity(); |
|
} else if (restriction == VirtualDesktopRestriction && (role == Qt::DisplayRole || role == DesktopRole)) { |
|
return level->virtualDesktop(); |
|
} else if (restriction ==ScreenRestriction && (role == Qt::DisplayRole || role == ScreenRole)) { |
|
return level->screen(); |
|
} else { |
|
return QVariant(); |
|
} |
|
} |
|
if (role == Qt::DisplayRole || role == ClientRole) { |
|
if (AbstractClient *client = m_root->clientForId(index.internalId())) { |
|
return QVariant::fromValue(client); |
|
} |
|
} |
|
return QVariant(); |
|
} |
|
|
|
int ClientModel::columnCount(const QModelIndex &parent) const |
|
{ |
|
Q_UNUSED(parent) |
|
return 1; |
|
} |
|
|
|
int ClientModel::rowCount(const QModelIndex &parent) const |
|
{ |
|
if (!m_root) { |
|
return 0; |
|
} |
|
if (!parent.isValid()) { |
|
return m_root->count(); |
|
} |
|
if (const AbstractLevel *level = getLevel(parent)) { |
|
if (level->id() != parent.internalId()) { |
|
// not a real level - no children |
|
return 0; |
|
} |
|
return level->count(); |
|
} |
|
return 0; |
|
} |
|
|
|
QHash<int, QByteArray> ClientModel::roleNames() const |
|
{ |
|
return { |
|
{ Qt::DisplayRole, QByteArrayLiteral("display") }, |
|
{ ClientRole, QByteArrayLiteral("client") }, |
|
{ ScreenRole, QByteArrayLiteral("screen") }, |
|
{ DesktopRole, QByteArrayLiteral("desktop") }, |
|
{ ActivityRole, QByteArrayLiteral("activity") }, |
|
}; |
|
} |
|
|
|
QModelIndex ClientModel::parent(const QModelIndex &child) const |
|
{ |
|
if (!child.isValid() || child.column() != 0) { |
|
return QModelIndex(); |
|
} |
|
return parentForId(child.internalId()); |
|
} |
|
|
|
QModelIndex ClientModel::parentForId(quint32 childId) const |
|
{ |
|
if (childId == m_root->id()) { |
|
// asking for parent of our toplevel |
|
return QModelIndex(); |
|
} |
|
if (AbstractLevel *parentLevel = m_root->parentForId(childId)) { |
|
if (parentLevel == m_root) { |
|
return QModelIndex(); |
|
} |
|
const int row = m_root->rowForId(parentLevel->id()); |
|
if (row == -1) { |
|
// error |
|
return QModelIndex(); |
|
} |
|
return createIndex(row, 0, parentLevel->id()); |
|
} |
|
return QModelIndex(); |
|
} |
|
|
|
QModelIndex ClientModel::index(int row, int column, const QModelIndex &parent) const |
|
{ |
|
if (column != 0 || row < 0 || !m_root) { |
|
return QModelIndex(); |
|
} |
|
if (!parent.isValid()) { |
|
if (row >= rowCount()) { |
|
return QModelIndex(); |
|
} |
|
return createIndex(row, 0, m_root->idForRow(row)); |
|
} |
|
const AbstractLevel *parentLevel = getLevel(parent); |
|
if (!parentLevel) { |
|
return QModelIndex(); |
|
} |
|
if (row >= parentLevel->count()) { |
|
return QModelIndex(); |
|
} |
|
const quint32 id = parentLevel->idForRow(row); |
|
if (id == 0) { |
|
return QModelIndex(); |
|
} |
|
return createIndex(row, column, id); |
|
} |
|
|
|
const AbstractLevel *ClientModel::getLevel(const QModelIndex &index) const |
|
{ |
|
if (!index.isValid()) { |
|
return m_root; |
|
} |
|
return m_root->levelForId(index.internalId()); |
|
} |
|
|
|
void ClientModel::levelBeginInsert(int rowStart, int rowEnd, quint32 id) |
|
{ |
|
const int row = m_root->rowForId(id); |
|
QModelIndex parent; |
|
if (row != -1) { |
|
parent = createIndex(row, 0, id); |
|
} |
|
beginInsertRows(parent, rowStart, rowEnd); |
|
} |
|
|
|
void ClientModel::levelBeginRemove(int rowStart, int rowEnd, quint32 id) |
|
{ |
|
const int row = m_root->rowForId(id); |
|
QModelIndex parent; |
|
if (row != -1) { |
|
parent = createIndex(row, 0, id); |
|
} |
|
beginRemoveRows(parent, rowStart, rowEnd); |
|
} |
|
|
|
void ClientModel::levelEndInsert() |
|
{ |
|
endInsertRows(); |
|
} |
|
|
|
void ClientModel::levelEndRemove() |
|
{ |
|
endRemoveRows(); |
|
} |
|
|
|
#define CLIENT_MODEL_WRAPPER(name, levels) \ |
|
name::name(QObject *parent) \ |
|
: ClientModel(parent) \ |
|
{ \ |
|
setLevels(levels); \ |
|
} \ |
|
name::~name() {} |
|
|
|
CLIENT_MODEL_WRAPPER(SimpleClientModel, QList<LevelRestriction>()) |
|
CLIENT_MODEL_WRAPPER(ClientModelByScreen, QList<LevelRestriction>() << ScreenRestriction) |
|
CLIENT_MODEL_WRAPPER(ClientModelByScreenAndDesktop, QList<LevelRestriction>() << ScreenRestriction << VirtualDesktopRestriction) |
|
#undef CLIENT_MODEL_WRAPPER |
|
|
|
ClientFilterModel::ClientFilterModel(QObject *parent) |
|
: QSortFilterProxyModel(parent) |
|
, m_clientModel(nullptr) |
|
{ |
|
} |
|
|
|
ClientFilterModel::~ClientFilterModel() |
|
{ |
|
} |
|
|
|
void ClientFilterModel::setClientModel(ClientModel *clientModel) |
|
{ |
|
if (clientModel == m_clientModel) { |
|
return; |
|
} |
|
m_clientModel = clientModel; |
|
setSourceModel(m_clientModel); |
|
emit clientModelChanged(); |
|
} |
|
|
|
void ClientFilterModel::setFilter(const QString &filter) |
|
{ |
|
if (filter == m_filter) { |
|
return; |
|
} |
|
m_filter = filter; |
|
emit filterChanged(); |
|
invalidateFilter(); |
|
} |
|
|
|
bool ClientFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const |
|
{ |
|
if (!m_clientModel) { |
|
return false; |
|
} |
|
if (m_filter.isEmpty()) { |
|
return true; |
|
} |
|
QModelIndex index = m_clientModel->index(sourceRow, 0, sourceParent); |
|
if (!index.isValid()) { |
|
return false; |
|
} |
|
QVariant data = index.data(); |
|
if (!data.isValid()) { |
|
// an invalid QVariant is valid data |
|
return true; |
|
} |
|
// TODO: introduce a type as a data role and properly check, this seems dangerous |
|
if (data.type() == QVariant::Int || data.type() == QVariant::UInt || data.type() == QVariant::String) { |
|
// we do not filter out screen, desktop and activity |
|
return true; |
|
} |
|
X11Client *client = qvariant_cast<KWin::X11Client *>(data); |
|
if (!client) { |
|
return false; |
|
} |
|
if (client->caption().contains(m_filter, Qt::CaseInsensitive)) { |
|
return true; |
|
} |
|
const QString windowRole(QString::fromUtf8(client->windowRole())); |
|
if (windowRole.contains(m_filter, Qt::CaseInsensitive)) { |
|
return true; |
|
} |
|
const QString resourceName(QString::fromUtf8(client->resourceName())); |
|
if (resourceName.contains(m_filter, Qt::CaseInsensitive)) { |
|
return true; |
|
} |
|
const QString resourceClass(QString::fromUtf8(client->resourceClass())); |
|
if (resourceClass.contains(m_filter, Qt::CaseInsensitive)) { |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
} // namespace Scripting |
|
} // namespace KWin
|
|
|