2013-03-06 08:42:45 +00:00
|
|
|
/********************************************************************
|
|
|
|
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"
|
2014-11-21 15:48:39 +00:00
|
|
|
#include <config-kwin.h>
|
|
|
|
#ifdef KWIN_BUILD_ACTIVITIES
|
2013-04-04 14:14:12 +00:00
|
|
|
#include "activities.h"
|
2014-11-21 15:48:39 +00:00
|
|
|
#endif
|
2019-09-24 08:48:08 +00:00
|
|
|
#include "x11client.h"
|
2013-04-03 10:19:27 +00:00
|
|
|
#include "screens.h"
|
2013-04-26 09:52:15 +00:00
|
|
|
#include "workspace.h"
|
2019-08-30 21:36:58 +00:00
|
|
|
#include "xdgshellclient.h"
|
2015-06-06 17:05:14 +00:00
|
|
|
#include "wayland_server.h"
|
2013-03-06 08:42:45 +00:00
|
|
|
|
|
|
|
namespace KWin {
|
|
|
|
namespace ScriptingClientModel {
|
|
|
|
|
|
|
|
static quint32 nextId() {
|
|
|
|
static quint32 counter = 0;
|
|
|
|
return ++counter;
|
|
|
|
}
|
|
|
|
|
|
|
|
ClientLevel::ClientLevel(ClientModel *model, AbstractLevel *parent)
|
|
|
|
: AbstractLevel(model, parent)
|
|
|
|
{
|
2015-06-06 16:48:11 +00:00
|
|
|
connect(Workspace::self(), &Workspace::clientAdded, this, &ClientLevel::clientAdded);
|
2015-04-30 08:51:58 +00:00
|
|
|
connect(Workspace::self(), &Workspace::clientRemoved, this, &ClientLevel::clientRemoved);
|
2013-03-06 08:42:45 +00:00
|
|
|
connect(model, SIGNAL(exclusionsChanged()), SLOT(reInit()));
|
2015-06-06 17:05:14 +00:00
|
|
|
if (waylandServer()) {
|
|
|
|
connect(waylandServer(), &WaylandServer::shellClientAdded, this, &ClientLevel::clientAdded);
|
|
|
|
}
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ClientLevel::~ClientLevel()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
void ClientLevel::clientAdded(AbstractClient *client)
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
|
|
|
setupClientConnections(client);
|
|
|
|
checkClient(client);
|
|
|
|
}
|
|
|
|
|
2015-04-30 08:51:58 +00:00
|
|
|
void ClientLevel::clientRemoved(AbstractClient *client)
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
2015-06-06 16:48:11 +00:00
|
|
|
removeClient(client);
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
void ClientLevel::setupClientConnections(AbstractClient *client)
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
2015-06-06 16:48:11 +00:00
|
|
|
auto check = [this, client] {
|
|
|
|
checkClient(client);
|
|
|
|
};
|
|
|
|
connect(client, &AbstractClient::desktopChanged, this, check);
|
|
|
|
connect(client, &AbstractClient::screenChanged, this, check);
|
|
|
|
connect(client, &AbstractClient::activitiesChanged, this, check);
|
2015-06-10 15:45:59 +00:00
|
|
|
connect(client, &AbstractClient::windowHidden, this, check);
|
|
|
|
connect(client, &AbstractClient::windowShown, this, check);
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
void ClientLevel::checkClient(AbstractClient *client)
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
|
|
|
const bool shouldInclude = !exclude(client) && shouldAdd(client);
|
|
|
|
const bool contains = containsClient(client);
|
|
|
|
|
|
|
|
if (shouldInclude && !contains) {
|
|
|
|
addClient(client);
|
|
|
|
} else if (!shouldInclude && contains) {
|
|
|
|
removeClient(client);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
bool ClientLevel::exclude(AbstractClient *client) const
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
bool ClientLevel::shouldAdd(AbstractClient *client) const
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
void ClientLevel::addClient(AbstractClient *client)
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
|
|
|
if (containsClient(client)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
emit beginInsert(m_clients.count(), m_clients.count(), id());
|
|
|
|
m_clients.insert(nextId(), client);
|
|
|
|
emit endInsert();
|
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
void ClientLevel::removeClient(AbstractClient *client)
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
|
|
|
int index = 0;
|
2015-06-06 16:48:11 +00:00
|
|
|
auto it = m_clients.begin();
|
2013-03-06 08:42:45 +00:00
|
|
|
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 ClientList &clients = Workspace::self()->clientList();
|
|
|
|
for (ClientList::const_iterator it = clients.begin(); it != clients.end(); ++it) {
|
2019-09-24 08:48:08 +00:00
|
|
|
X11Client *client = *it;
|
2013-03-06 08:42:45 +00:00
|
|
|
setupClientConnections(client);
|
|
|
|
if (!exclude(client) && shouldAdd(client)) {
|
|
|
|
m_clients.insert(nextId(), client);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientLevel::reInit()
|
|
|
|
{
|
|
|
|
const ClientList &clients = Workspace::self()->clientList();
|
|
|
|
for (ClientList::const_iterator it = clients.begin(); it != clients.end(); ++it) {
|
|
|
|
checkClient((*it));
|
|
|
|
}
|
2015-06-06 17:05:14 +00:00
|
|
|
if (waylandServer()) {
|
|
|
|
const auto &clients = waylandServer()->clients();
|
|
|
|
for (auto *c : clients) {
|
|
|
|
checkClient(c);
|
|
|
|
}
|
|
|
|
}
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
quint32 ClientLevel::idForRow(int row) const
|
|
|
|
{
|
|
|
|
if (row >= m_clients.size()) {
|
|
|
|
return 0;
|
|
|
|
}
|
2015-06-06 16:48:11 +00:00
|
|
|
auto it = m_clients.constBegin();
|
2013-03-06 08:42:45 +00:00
|
|
|
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;
|
2015-06-06 16:48:11 +00:00
|
|
|
for (auto it = m_clients.constBegin();
|
2013-03-06 08:42:45 +00:00
|
|
|
it != m_clients.constEnd();
|
|
|
|
++it, ++row) {
|
|
|
|
if (it.key() == id) {
|
|
|
|
return row;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
AbstractClient *ClientLevel::clientForId(quint32 child) const
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
2015-06-06 16:48:11 +00:00
|
|
|
auto it = m_clients.constFind(child);
|
2013-03-06 08:42:45 +00:00
|
|
|
if (it == m_clients.constEnd()) {
|
2014-02-24 15:33:40 +00:00
|
|
|
return nullptr;
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
return it.value();
|
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
bool ClientLevel::containsClient(AbstractClient *client) const
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
2015-06-06 16:48:11 +00:00
|
|
|
for (auto it = m_clients.constBegin();
|
2013-03-06 08:42:45 +00:00
|
|
|
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;
|
|
|
|
}
|
2014-02-24 15:33:40 +00:00
|
|
|
return nullptr;
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AbstractLevel *ClientLevel::parentForId(quint32 child) const
|
|
|
|
{
|
|
|
|
if (child == id()) {
|
|
|
|
return parentLevel();
|
|
|
|
}
|
|
|
|
if (m_clients.contains(child)) {
|
|
|
|
return const_cast<ClientLevel*>(this);
|
|
|
|
}
|
2014-02-24 15:33:40 +00:00
|
|
|
return nullptr;
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2015-07-07 09:48:42 +00:00
|
|
|
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);
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#else
|
2014-02-24 15:33:40 +00:00
|
|
|
return nullptr;
|
2013-03-06 08:42:45 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
case ClientModel::ScreenRestriction:
|
2013-04-03 10:19:27 +00:00
|
|
|
for (int i=0; i<screens()->count(); ++i) {
|
2013-03-06 08:42:45 +00:00
|
|
|
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
|
2014-02-24 15:33:40 +00:00
|
|
|
return nullptr;
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)));
|
2013-04-03 10:19:27 +00:00
|
|
|
connect(screens(), SIGNAL(countChanged(int,int)), SLOT(screenCountChanged(int,int)));
|
2013-03-06 08:42:45 +00:00
|
|
|
#ifdef KWIN_BUILD_ACTIVITIES
|
2015-07-07 09:48:42 +00:00
|
|
|
if (Activities *activities = Activities::self()) {
|
|
|
|
connect(activities, SIGNAL(added(QString)), SLOT(activityAdded(QString)));
|
|
|
|
connect(activities, SIGNAL(removed(QString)), SLOT(activityRemoved(QString)));
|
|
|
|
}
|
2013-03-06 08:42:45 +00:00
|
|
|
#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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-03 10:19:27 +00:00
|
|
|
void ForkLevel::screenCountChanged(int previousCount, int newCount)
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
|
|
|
if (restriction() != ClientModel::ClientModel::ClientModel::ScreenRestriction) {
|
|
|
|
return;
|
|
|
|
}
|
2013-04-03 10:19:27 +00:00
|
|
|
if (newCount == previousCount || previousCount != count()) {
|
2013-03-06 08:42:45 +00:00
|
|
|
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();
|
2014-11-21 15:48:39 +00:00
|
|
|
#else
|
|
|
|
Q_UNUSED(activityId)
|
2013-03-06 08:42:45 +00:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
}
|
2014-11-21 15:48:39 +00:00
|
|
|
#else
|
|
|
|
Q_UNUSED(activityId)
|
2013-03-06 08:42:45 +00:00
|
|
|
#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
|
2014-02-24 15:33:40 +00:00
|
|
|
return nullptr;
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2014-02-24 15:33:40 +00:00
|
|
|
return nullptr;
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-06-06 16:48:11 +00:00
|
|
|
AbstractClient *ForkLevel::clientForId(quint32 child) const
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
|
|
|
for (QList<AbstractLevel*>::const_iterator it = m_children.constBegin(); it != m_children.constEnd(); ++it) {
|
2015-06-06 16:48:11 +00:00
|
|
|
if (AbstractClient *client = (*it)->clientForId(child)) {
|
2013-03-06 08:42:45 +00:00
|
|
|
return client;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// not found
|
2014-02-24 15:33:40 +00:00
|
|
|
return nullptr;
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ClientModel::ClientModel(QObject *parent)
|
|
|
|
: QAbstractItemModel(parent)
|
2014-02-24 15:33:40 +00:00
|
|
|
, m_root(nullptr)
|
2013-03-06 08:42:45 +00:00
|
|
|
, 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) {
|
2015-06-06 16:48:11 +00:00
|
|
|
if (AbstractClient *client = m_root->clientForId(index.internalId())) {
|
2019-09-18 08:08:17 +00:00
|
|
|
return QVariant::fromValue(client);
|
2013-03-06 08:42:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-07-09 21:56:00 +00:00
|
|
|
QHash<int, QByteArray> ClientModel::roleNames() const
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
{ Qt::DisplayRole, QByteArrayLiteral("display") },
|
|
|
|
{ ClientRole, QByteArrayLiteral("client") },
|
|
|
|
{ ScreenRole, QByteArrayLiteral("screen") },
|
|
|
|
{ DesktopRole, QByteArrayLiteral("desktop") },
|
|
|
|
{ ActivityRole, QByteArrayLiteral("activity") },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2013-03-06 08:42:45 +00:00
|
|
|
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)
|
2014-02-24 15:33:40 +00:00
|
|
|
, m_clientModel(nullptr)
|
2013-03-06 08:42:45 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2019-09-24 08:48:08 +00:00
|
|
|
X11Client *client = qvariant_cast<KWin::X11Client *>(data);
|
2013-03-06 08:42:45 +00:00
|
|
|
if (!client) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (client->caption().contains(m_filter, Qt::CaseInsensitive)) {
|
|
|
|
return true;
|
|
|
|
}
|
2013-07-23 05:02:52 +00:00
|
|
|
const QString windowRole(QString::fromUtf8(client->windowRole()));
|
2013-03-06 08:42:45 +00:00
|
|
|
if (windowRole.contains(m_filter, Qt::CaseInsensitive)) {
|
|
|
|
return true;
|
|
|
|
}
|
2013-07-23 05:02:52 +00:00
|
|
|
const QString resourceName(QString::fromUtf8(client->resourceName()));
|
2013-03-06 08:42:45 +00:00
|
|
|
if (resourceName.contains(m_filter, Qt::CaseInsensitive)) {
|
|
|
|
return true;
|
|
|
|
}
|
2013-07-23 05:02:52 +00:00
|
|
|
const QString resourceClass(QString::fromUtf8(client->resourceClass()));
|
2013-03-06 08:42:45 +00:00
|
|
|
if (resourceClass.contains(m_filter, Qt::CaseInsensitive)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Scripting
|
|
|
|
} // namespace KWin
|