diff --git a/CMakeLists.txt b/CMakeLists.txt
index f0795b4873..3a6f8ce317 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -149,6 +149,7 @@ if(KWIN_BUILD_SCRIPTING)
scripting/scriptedeffect.cpp
scripting/scriptingutils.cpp
scripting/timer.cpp
+ scripting/scripting_model.cpp
)
endif()
diff --git a/scripting/scripting.cpp b/scripting/scripting.cpp
index e124827174..1d71944f75 100644
--- a/scripting/scripting.cpp
+++ b/scripting/scripting.cpp
@@ -24,6 +24,7 @@ along with this program. If not, see .
#include "meta.h"
#include "scriptingutils.h"
#include "workspace_wrapper.h"
+#include "scripting_model.h"
#include "../client.h"
#include "../thumbnailitem.h"
#include "../options.h"
@@ -560,6 +561,11 @@ void KWin::DeclarativeScript::run()
kdeclarative.setupBindings();
installScriptFunctions(kdeclarative.scriptEngine());
qmlRegisterType("org.kde.kwin", 0, 1, "ThumbnailItem");
+ qmlRegisterType();
+ qmlRegisterType("org.kde.kwin", 0, 1, "ClientModel");
+ qmlRegisterType("org.kde.kwin", 0, 1, "ClientModelByScreen");
+ qmlRegisterType("org.kde.kwin", 0, 1, "ClientModelByScreenAndDesktop");
+ qmlRegisterType("org.kde.kwin", 0, 1, "ClientFilterModel");
qmlRegisterType();
m_view->rootContext()->setContextProperty("options", options);
diff --git a/scripting/scripting_model.cpp b/scripting/scripting_model.cpp
new file mode 100644
index 0000000000..b2d05756dd
--- /dev/null
+++ b/scripting/scripting_model.cpp
@@ -0,0 +1,903 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin
+
+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 .
+*********************************************************************/
+#include "scripting_model.h"
+#include "client.h"
+// Qt
+#include
+
+#include
+
+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(), SIGNAL(clientAdded(KWin::Client*)), SLOT(clientAdded(KWin::Client*)));
+ connect(Workspace::self(), SIGNAL(clientRemoved(KWin::Client*)), SLOT(clientRemoved(KWin::Client*)));
+ connect(model, SIGNAL(exclusionsChanged()), SLOT(reInit()));
+}
+
+ClientLevel::~ClientLevel()
+{
+}
+
+void ClientLevel::clientAdded(Client *client)
+{
+ setupClientConnections(client);
+ checkClient(client);
+}
+
+void ClientLevel::clientRemoved(Client *client)
+{
+ removeClient(client);
+}
+
+void ClientLevel::setupClientConnections(Client *client)
+{
+ connect(client, SIGNAL(desktopChanged()), SLOT(checkClient()));
+ connect(client, SIGNAL(screenChanged()), SLOT(checkClient()));
+ connect(client, SIGNAL(activitiesChanged(KWin::Toplevel*)), SLOT(checkClient()));
+}
+
+void ClientLevel::checkClient()
+{
+ checkClient(static_cast(sender()));
+}
+
+void ClientLevel::checkClient(Client *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(Client *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::NonSelectedWindowTabExclusion) {
+ if (!client->isCurrentTab()) {
+ return true;
+ }
+ }
+ if (exclusions & ClientModel::NotAcceptingFocusExclusion) {
+ if (!client->wantsInput()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ClientLevel::shouldAdd(Client *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(Client *client)
+{
+ if (containsClient(client)) {
+ return;
+ }
+ emit beginInsert(m_clients.count(), m_clients.count(), id());
+ m_clients.insert(nextId(), client);
+ emit endInsert();
+}
+
+void ClientLevel::removeClient(Client *client)
+{
+ int index = 0;
+ QMap::iterator 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 ClientList &clients = Workspace::self()->clientList();
+ for (ClientList::const_iterator it = clients.begin(); it != clients.end(); ++it) {
+ Client *client = *it;
+ 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));
+ }
+}
+
+quint32 ClientLevel::idForRow(int row) const
+{
+ if (row >= m_clients.size()) {
+ return 0;
+ }
+ QMap::const_iterator it = m_clients.constBegin();
+ for (int i=0; i::const_iterator it = m_clients.constBegin();
+ it != m_clients.constEnd();
+ ++it, ++row) {
+ if (it.key() == id) {
+ return row;
+ }
+ }
+ return -1;
+}
+
+Client *ClientLevel::clientForId(quint32 child) const
+{
+ QMap::const_iterator it = m_clients.constFind(child);
+ if (it == m_clients.constEnd()) {
+ return NULL;
+ }
+ return it.value();
+}
+
+bool ClientLevel::containsClient(Client *client) const
+{
+ for (QMap::const_iterator 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 NULL;
+}
+
+AbstractLevel *ClientLevel::parentForId(quint32 child) const
+{
+ if (child == id()) {
+ return parentLevel();
+ }
+ if (m_clients.contains(child)) {
+ return const_cast(this);
+ }
+ return NULL;
+}
+
+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 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
+ const QStringList &activities = Workspace::self()->activityList();
+ 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 NULL;
+#endif
+ }
+ case ClientModel::ScreenRestriction:
+ for (int i=0; inumScreens(); ++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 NULL;
+ }
+
+ 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 &childRestrictions, ClientModel *model, AbstractLevel *parent)
+ : AbstractLevel(model, parent)
+ , m_childRestrictions(childRestrictions)
+{
+ connect(VirtualDesktopManager::self(), SIGNAL(countChanged(uint,uint)), SLOT(desktopCountChanged(uint,uint)));
+ connect(QApplication::desktop(), SIGNAL(screenCountChanged(int)), SLOT(screenCountChanged(int)));
+#ifdef KWIN_BUILD_ACTIVITIES
+ connect(Workspace::self(), SIGNAL(activityAdded(QString)), SLOT(activityAdded(QString)));
+ connect(Workspace::self(), SIGNAL(activityRemoved(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 newCount)
+{
+ if (restriction() != ClientModel::ClientModel::ClientModel::ScreenRestriction) {
+ return;
+ }
+ const int previousCount = m_children.count();
+ if (newCount == previousCount) {
+ 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; isetScreen(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();
+#endif
+}
+
+void ForkLevel::activityRemoved(const QString &activityId)
+{
+#ifdef KWIN_BUILD_ACTIVITIES
+ if (restriction() != ClientModel::ClientModel::ActivityRestriction) {
+ return;
+ }
+ for (int i=0; iactivity() == activityId) {
+ emit beginRemove(i, i, id());
+ delete m_children.takeAt(i);
+ emit endRemove();
+ break;
+ }
+ }
+#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::iterator it = m_children.begin(); it != m_children.end(); ++it) {
+ (*it)->setActivity(activity);
+ }
+}
+
+void ForkLevel::setScreen(uint screen)
+{
+ AbstractLevel::setScreen(screen);
+ for (QList::iterator it = m_children.begin(); it != m_children.end(); ++it) {
+ (*it)->setScreen(screen);
+ }
+}
+
+void ForkLevel::setVirtualDesktop(uint virtualDesktop)
+{
+ AbstractLevel::setVirtualDesktop(virtualDesktop);
+ for (QList::iterator it = m_children.begin(); it != m_children.end(); ++it) {
+ (*it)->setVirtualDesktop(virtualDesktop);
+ }
+}
+
+void ForkLevel::init()
+{
+ for (QList::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::const_iterator it = m_children.constBegin(); it != m_children.constEnd(); ++it) {
+ if (const AbstractLevel *child = (*it)->levelForId(id)) {
+ return child;
+ }
+ }
+ // not found
+ return NULL;
+}
+
+AbstractLevel *ForkLevel::parentForId(quint32 child) const
+{
+ if (child == id()) {
+ return parentLevel();
+ }
+ for (QList::const_iterator it = m_children.constBegin(); it != m_children.constEnd(); ++it) {
+ if (AbstractLevel *parent = (*it)->parentForId(child)) {
+ return parent;
+ }
+ }
+ // not found
+ return NULL;
+}
+
+int ForkLevel::rowForId(quint32 child) const
+{
+ if (id() == child) {
+ return 0;
+ }
+ for (int i=0; iid() == child) {
+ return i;
+ }
+ }
+ // do recursion
+ for (QList::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;
+}
+
+Client *ForkLevel::clientForId(quint32 child) const
+{
+ for (QList::const_iterator it = m_children.constBegin(); it != m_children.constEnd(); ++it) {
+ if (Client *client = (*it)->clientForId(child)) {
+ return client;
+ }
+ }
+ // not found
+ return NULL;
+}
+
+ClientModel::ClientModel(QObject *parent)
+ : QAbstractItemModel(parent)
+ , m_root(NULL)
+ , m_exclusions(NoExclusion)
+{
+ QHash roleNames;
+ roleNames.insert(Qt::DisplayRole, "display");
+ roleNames.insert(ClientRole, "client");
+ roleNames.insert(ScreenRole, "screen");
+ roleNames.insert(DesktopRole, "desktop");
+ roleNames.insert(ActivityRole, "activity");
+ setRoleNames(roleNames);
+}
+
+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 (Client *client = m_root->clientForId(index.internalId())) {
+ return qVariantFromValue(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;
+}
+
+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())
+CLIENT_MODEL_WRAPPER(ClientModelByScreen, QList() << ScreenRestriction)
+CLIENT_MODEL_WRAPPER(ClientModelByScreenAndDesktop, QList() << ScreenRestriction << VirtualDesktopRestriction)
+#undef CLIENT_MODEL_WRAPPER
+
+ClientFilterModel::ClientFilterModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+ , m_clientModel(NULL)
+{
+}
+
+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;
+ }
+ Client *client = qvariant_cast(data);
+ if (!client) {
+ return false;
+ }
+ if (client->caption().contains(m_filter, Qt::CaseInsensitive)) {
+ return true;
+ }
+ const QString windowRole(client->windowRole());
+ if (windowRole.contains(m_filter, Qt::CaseInsensitive)) {
+ return true;
+ }
+ const QString resourceName(client->resourceName());
+ if (resourceName.contains(m_filter, Qt::CaseInsensitive)) {
+ return true;
+ }
+ const QString resourceClass(client->resourceClass());
+ if (resourceClass.contains(m_filter, Qt::CaseInsensitive)) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace Scripting
+} // namespace KWin
diff --git a/scripting/scripting_model.h b/scripting/scripting_model.h
new file mode 100644
index 0000000000..d4306c058e
--- /dev/null
+++ b/scripting/scripting_model.h
@@ -0,0 +1,380 @@
+/********************************************************************
+ KWin - the KDE window manager
+ This file is part of the KDE project.
+
+Copyright (C) 2013 Martin Gräßlin
+
+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 .
+*********************************************************************/
+#ifndef KWIN_SCRIPTING_MODEL_H
+#define KWIN_SCRIPTING_MODEL_H
+
+#include
+#include
+#include
+
+namespace KWin {
+class Client;
+
+namespace ScriptingClientModel {
+
+class AbstractLevel;
+
+class ClientModel : public QAbstractItemModel
+{
+ Q_OBJECT
+ Q_ENUMS(Exclude)
+ Q_ENUMS(LevelRestriction)
+ Q_PROPERTY(Exclusions exclusions READ exclusions WRITE setExclusions NOTIFY exclusionsChanged)
+public:
+ enum Exclusion {
+ NoExclusion = 0,
+ // window types
+ DesktopWindowsExclusion = 1 << 0,
+ DockWindowsExclusion = 1 << 1,
+ UtilityWindowsExclusion = 1 << 2,
+ SpecialWindowsExclusion = 1 << 3,
+ // windows with flags
+ SkipTaskbarExclusion = 1 << 4,
+ SkipPagerExclusion = 1 << 5,
+ SwitchSwitcherExclusion = 1 << 6,
+ // based on state
+ OtherDesktopsExclusion = 1 << 7,
+ OtherActivitiesExclusion = 1 << 8,
+ MinimizedExclusion = 1 << 9,
+ NonSelectedWindowTabExclusion = 1 << 10,
+ NotAcceptingFocusExclusion = 1 << 11
+ };
+ Q_DECLARE_FLAGS(Exclusions, Exclusion)
+ Q_FLAGS(Exclusions)
+ enum LevelRestriction {
+ NoRestriction = 0,
+ VirtualDesktopRestriction = 1 << 0,
+ ScreenRestriction = 1 << 1,
+ ActivityRestriction = 1 << 2
+ };
+ Q_DECLARE_FLAGS(LevelRestrictions, LevelRestriction)
+ Q_FLAGS(LevelRestrictions)
+ explicit ClientModel(QObject *parent);
+ virtual ~ClientModel();
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ virtual QModelIndex parent(const QModelIndex &child) const;
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+
+ void setExclusions(ClientModel::Exclusions exclusions);
+ Exclusions exclusions() const;
+
+Q_SIGNALS:
+ void exclusionsChanged();
+
+private Q_SLOTS:
+ void levelBeginInsert(int rowStart, int rowEnd, quint32 parentId);
+ void levelEndInsert();
+ void levelBeginRemove(int rowStart, int rowEnd, quint32 parentId);
+ void levelEndRemove();
+
+protected:
+ enum ClientModelRoles {
+ ClientRole = Qt::UserRole,
+ ScreenRole,
+ DesktopRole,
+ ActivityRole
+ };
+ void setLevels(QList restrictions);
+
+private:
+ QModelIndex parentForId(quint32 childId) const;
+ const AbstractLevel *getLevel(const QModelIndex &index) const;
+ AbstractLevel *m_root;
+ Exclusions m_exclusions;
+};
+
+/**
+ * @brief The data structure of the Model.
+ *
+ * The model is implemented as a Tree consisting of AbstractLevels as the levels of the tree.
+ * A non leaf level is represented by the inheriting class @link ForkLevel, the last level above a
+ * leaf is represented by the inheriting class @link ClientLevel, which contains the Clients - each
+ * Client is one leaf.
+ *
+ * In case the tree would only consist of Clients - leafs - it has always one ClientLevel as the root
+ * of the tree.
+ *
+ * The number of levels in the tree is controlled by the LevelRestrictions. For each existing
+ * LevelRestriction a new Level is created, if there are no more restrictions a ClientLevel is created.
+ *
+ * To build up the tree the static factory method @link create has to be used. It will recursively
+ * build up the tree. After the tree has been build up use @link init to initialize the tree which
+ * will add the Clients to the ClientLevel.
+ *
+ * Each element of the tree has a unique id which can be used by the QAbstractItemModel as the
+ * internal id for its QModelIndex. Note: the ids have no ordering, if trying to get a specific element
+ * the tree performs a depth-first search.
+ *
+ */
+class AbstractLevel : public QObject
+{
+ Q_OBJECT
+public:
+ virtual ~AbstractLevel();
+ virtual int count() const = 0;
+ virtual void init() = 0;
+ virtual quint32 idForRow(int row) const = 0;
+
+ uint screen() const;
+ uint virtualDesktop() const;
+ const QString &activity() const;
+ ClientModel::LevelRestrictions restrictions() const;
+ void setRestrictions(ClientModel::LevelRestrictions restrictions);
+ ClientModel::LevelRestriction restriction() const;
+ void setRestriction(ClientModel::LevelRestriction restriction);
+ quint32 id() const;
+ AbstractLevel *parentLevel() const;
+ virtual const AbstractLevel *levelForId(quint32 id) const = 0;
+ virtual AbstractLevel *parentForId(quint32 child) const = 0;
+ virtual int rowForId(quint32 child) const = 0;
+ virtual Client *clientForId(quint32 child) const = 0;
+
+ virtual void setScreen(uint screen);
+ virtual void setVirtualDesktop(uint virtualDesktop);
+ virtual void setActivity(const QString &activity);
+
+ static AbstractLevel *create(const QList &restrictions, ClientModel::LevelRestrictions parentRestrictions, ClientModel *model, AbstractLevel *parent = NULL);
+
+Q_SIGNALS:
+ void beginInsert(int rowStart, int rowEnd, quint32 parentId);
+ void endInsert();
+ void beginRemove(int rowStart, int rowEnd, quint32 parentId);
+ void endRemove();
+protected:
+ AbstractLevel(ClientModel *model, AbstractLevel *parent);
+ ClientModel *model() const;
+private:
+ ClientModel *m_model;
+ AbstractLevel *m_parent;
+ uint m_screen;
+ uint m_virtualDesktop;
+ QString m_activity;
+ ClientModel::LevelRestriction m_restriction;
+ ClientModel::LevelRestrictions m_restrictions;
+ quint32 m_id;
+};
+
+class ForkLevel : public AbstractLevel
+{
+ Q_OBJECT
+public:
+ ForkLevel(const QList &childRestrictions, ClientModel *model, AbstractLevel *parent);
+ virtual ~ForkLevel();
+ virtual int count() const;
+ virtual void init();
+ virtual quint32 idForRow(int row) const;
+ void addChild(AbstractLevel *child);
+ virtual void setScreen(uint screen);
+ virtual void setVirtualDesktop(uint virtualDesktop);
+ virtual void setActivity(const QString &activity);
+ virtual const AbstractLevel *levelForId(quint32 id) const;
+ virtual AbstractLevel *parentForId(quint32 child) const;
+ virtual int rowForId(quint32 child) const;
+ virtual Client *clientForId(quint32 child) const;
+private Q_SLOTS:
+ void desktopCountChanged(uint previousCount, uint newCount);
+ void screenCountChanged(int newCount);
+ void activityAdded(const QString &id);
+ void activityRemoved(const QString &id);
+private:
+ QList m_children;
+ QList m_childRestrictions;
+};
+
+/**
+ * @brief The actual leafs of the model's tree containing the Client's in this branch of the tree.
+ *
+ * This class groups all the Clients of one branch of the tree and takes care of updating the tree
+ * when a Client changes its state in a way that it should be excluded/included or gets added or
+ * removed.
+ *
+ * The Clients in this group are not sorted in any particular way. It's a simple list which only
+ * gets added to. If some sorting should be applied, use a QSortFilterProxyModel.
+ */
+class ClientLevel : public AbstractLevel
+{
+ Q_OBJECT
+public:
+ explicit ClientLevel(ClientModel *model, AbstractLevel *parent);
+ virtual ~ClientLevel();
+
+ void init();
+
+ int count() const;
+ quint32 idForRow(int row) const;
+ bool containsId(quint32 id) const;
+ int rowForId(quint32 row) const;
+ Client *clientForId(quint32 child) const;
+ virtual const AbstractLevel *levelForId(quint32 id) const;
+ virtual AbstractLevel *parentForId(quint32 child) const;
+public Q_SLOTS:
+ void clientAdded(KWin::Client *client);
+ void clientRemoved(KWin::Client *client);
+private Q_SLOTS:
+ // uses sender()
+ void checkClient();
+ void reInit();
+private:
+ void checkClient(KWin::Client *client);
+ void setupClientConnections(Client *client);
+ void addClient(Client *client);
+ void removeClient(Client *client);
+ bool shouldAdd(Client *client) const;
+ bool exclude(Client *client) const;
+ bool containsClient(Client *client) const;
+ QMap m_clients;
+};
+
+class SimpleClientModel : public ClientModel
+{
+ Q_OBJECT
+public:
+ SimpleClientModel(QObject *parent = NULL);
+ virtual ~SimpleClientModel();
+};
+
+class ClientModelByScreen : public ClientModel
+{
+ Q_OBJECT
+public:
+ ClientModelByScreen(QObject *parent = NULL);
+ virtual ~ClientModelByScreen();
+};
+
+class ClientModelByScreenAndDesktop : public ClientModel
+{
+ Q_OBJECT
+public:
+ ClientModelByScreenAndDesktop(QObject *parent = NULL);
+ virtual ~ClientModelByScreenAndDesktop();
+};
+
+/**
+ * @brief Custom QSortFilterProxyModel to filter on Client caption, role and class.
+ *
+ */
+class ClientFilterModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+ Q_PROPERTY(KWin::ScriptingClientModel::ClientModel *clientModel READ clientModel WRITE setClientModel NOTIFY clientModelChanged)
+ Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged)
+public:
+ ClientFilterModel(QObject *parent = 0);
+ virtual ~ClientFilterModel();
+ ClientModel *clientModel() const;
+ const QString &filter() const;
+
+public Q_SLOTS:
+ void setClientModel(ClientModel *clientModel);
+ void setFilter(const QString &filter);
+
+protected:
+ virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
+
+Q_SIGNALS:
+ void clientModelChanged();
+ void filterChanged();
+
+private:
+ ClientModel *m_clientModel;
+ QString m_filter;
+};
+
+inline
+int ClientLevel::count() const
+{
+ return m_clients.count();
+}
+
+inline
+const QString &AbstractLevel::activity() const
+{
+ return m_activity;
+}
+
+inline
+AbstractLevel *AbstractLevel::parentLevel() const
+{
+ return m_parent;
+}
+
+inline
+ClientModel *AbstractLevel::model() const
+{
+ return m_model;
+}
+
+inline
+uint AbstractLevel::screen() const
+{
+ return m_screen;
+}
+
+inline
+uint AbstractLevel::virtualDesktop() const
+{
+ return m_virtualDesktop;
+}
+
+inline
+ClientModel::LevelRestriction AbstractLevel::restriction() const
+{
+ return m_restriction;
+}
+
+inline
+ClientModel::LevelRestrictions AbstractLevel::restrictions() const
+{
+ return m_restrictions;
+}
+
+inline
+quint32 AbstractLevel::id() const
+{
+ return m_id;
+}
+
+inline
+ClientModel::Exclusions ClientModel::exclusions() const
+{
+ return m_exclusions;
+}
+
+inline
+ClientModel *ClientFilterModel::clientModel() const
+{
+ return m_clientModel;
+}
+
+inline
+const QString &ClientFilterModel::filter() const
+{
+ return m_filter;
+}
+
+} // namespace Scripting
+} // namespace KWin
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::ScriptingClientModel::ClientModel::Exclusions)
+Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::ScriptingClientModel::ClientModel::LevelRestrictions)
+
+#endif // KWIN_SCRIPTING_MODEL_H