File indexing completed on 2024-05-19 16:34:53

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2009 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 // own
0011 #include "clientmodel.h"
0012 // tabbox
0013 #include "tabboxconfig.h"
0014 // Qt
0015 #include <QIcon>
0016 #include <QUuid>
0017 // TODO: remove with Qt 5, only for HTML escaping the caption
0018 #include <QTextDocument>
0019 // other
0020 #include <cmath>
0021 
0022 namespace KWin
0023 {
0024 namespace TabBox
0025 {
0026 
0027 ClientModel::ClientModel(QObject *parent)
0028     : QAbstractItemModel(parent)
0029 {
0030 }
0031 
0032 ClientModel::~ClientModel()
0033 {
0034 }
0035 
0036 QVariant ClientModel::data(const QModelIndex &index, int role) const
0037 {
0038     if (!index.isValid()) {
0039         return QVariant();
0040     }
0041 
0042     if (m_clientList.isEmpty()) {
0043         return QVariant();
0044     }
0045 
0046     int clientIndex = index.row();
0047     if (clientIndex >= m_clientList.count()) {
0048         return QVariant();
0049     }
0050     QSharedPointer<TabBoxClient> client = m_clientList[clientIndex].toStrongRef();
0051     if (!client) {
0052         return QVariant();
0053     }
0054     switch (role) {
0055     case Qt::DisplayRole:
0056     case CaptionRole: {
0057         QString caption = client->caption();
0058         if (Qt::mightBeRichText(caption)) {
0059             caption = caption.toHtmlEscaped();
0060         }
0061         return caption;
0062     }
0063     case ClientRole:
0064         return QVariant::fromValue<void *>(client.data());
0065     case DesktopNameRole: {
0066         return tabBox->desktopName(client.data());
0067     }
0068     case WIdRole:
0069         return client->internalId();
0070     case MinimizedRole:
0071         return client->isMinimized();
0072     case CloseableRole:
0073         // clients that claim to be first are not closeable
0074         return client->isCloseable() && !client->isFirstInTabBox();
0075     case IconRole:
0076         return client->icon();
0077     default:
0078         return QVariant();
0079     }
0080 }
0081 
0082 QString ClientModel::longestCaption() const
0083 {
0084     QString caption;
0085     for (const QWeakPointer<TabBoxClient> &clientPointer : std::as_const(m_clientList)) {
0086         QSharedPointer<TabBoxClient> client = clientPointer.toStrongRef();
0087         if (!client) {
0088             continue;
0089         }
0090         if (client->caption().size() > caption.size()) {
0091             caption = client->caption();
0092         }
0093     }
0094     return caption;
0095 }
0096 
0097 int ClientModel::columnCount(const QModelIndex &parent) const
0098 {
0099     return 1;
0100 }
0101 
0102 int ClientModel::rowCount(const QModelIndex &parent) const
0103 {
0104     if (parent.isValid()) {
0105         return 0;
0106     }
0107     return m_clientList.count();
0108 }
0109 
0110 QModelIndex ClientModel::parent(const QModelIndex &child) const
0111 {
0112     return QModelIndex();
0113 }
0114 
0115 QModelIndex ClientModel::index(int row, int column, const QModelIndex &parent) const
0116 {
0117     if (row < 0 || column != 0 || parent.isValid()) {
0118         return QModelIndex();
0119     }
0120     int index = row * columnCount();
0121     if (index >= m_clientList.count() && !m_clientList.isEmpty()) {
0122         return QModelIndex();
0123     }
0124     return createIndex(row, 0);
0125 }
0126 
0127 QHash<int, QByteArray> ClientModel::roleNames() const
0128 {
0129     return {
0130         {CaptionRole, QByteArrayLiteral("caption")},
0131         {DesktopNameRole, QByteArrayLiteral("desktopName")},
0132         {MinimizedRole, QByteArrayLiteral("minimized")},
0133         {WIdRole, QByteArrayLiteral("windowId")},
0134         {CloseableRole, QByteArrayLiteral("closeable")},
0135         {IconRole, QByteArrayLiteral("icon")},
0136     };
0137 }
0138 
0139 QModelIndex ClientModel::index(QWeakPointer<TabBoxClient> client) const
0140 {
0141     if (!m_clientList.contains(client)) {
0142         return QModelIndex();
0143     }
0144     int index = m_clientList.indexOf(client);
0145     int row = index / columnCount();
0146     int column = index % columnCount();
0147     return createIndex(row, column);
0148 }
0149 
0150 void ClientModel::createClientList(bool partialReset)
0151 {
0152     createClientList(tabBox->currentDesktop(), partialReset);
0153 }
0154 
0155 void ClientModel::createFocusChainClientList(int desktop,
0156     const QSharedPointer<TabBoxClient> &start, TabBoxClientList &stickyClients)
0157 {
0158     auto c = start;
0159     if (!tabBox->isInFocusChain(c.data())) {
0160         QSharedPointer<TabBoxClient> firstClient = tabBox->firstClientFocusChain().toStrongRef();
0161         if (firstClient) {
0162             c = firstClient;
0163         }
0164     }
0165     auto stop = c;
0166     do {
0167         QSharedPointer<TabBoxClient> add = tabBox->clientToAddToList(c.data(), desktop);
0168         if (!add.isNull()) {
0169             m_mutableClientList += add;
0170             if (add.data()->isFirstInTabBox()) {
0171                 stickyClients << add;
0172             }
0173         }
0174         c = tabBox->nextClientFocusChain(c.data());
0175     } while (c && c != stop);
0176 }
0177 
0178 void ClientModel::createStackingOrderClientList(int desktop,
0179     const QSharedPointer<TabBoxClient> &start, TabBoxClientList &stickyClients)
0180 {
0181     // TODO: needs improvement
0182     const TabBoxClientList stacking = tabBox->stackingOrder();
0183     auto c = stacking.first().toStrongRef();
0184     auto stop = c;
0185     int index = 0;
0186     while (c) {
0187         QSharedPointer<TabBoxClient> add = tabBox->clientToAddToList(c.data(), desktop);
0188         if (!add.isNull()) {
0189             if (start == add.data()) {
0190                 m_mutableClientList.removeAll(add);
0191                 m_mutableClientList.prepend(add);
0192             } else {
0193                 m_mutableClientList += add;
0194             }
0195             if (add.data()->isFirstInTabBox()) {
0196                 stickyClients << add;
0197             }
0198         }
0199         if (index >= stacking.size() - 1) {
0200             c = nullptr;
0201         } else {
0202             c = stacking[++index];
0203         }
0204 
0205         if (c == stop) {
0206             break;
0207         }
0208     }
0209 }
0210 
0211 void ClientModel::createClientList(int desktop, bool partialReset)
0212 {
0213     auto start = tabBox->activeClient().toStrongRef();
0214     // TODO: new clients are not added at correct position
0215     if (partialReset && !m_mutableClientList.isEmpty()) {
0216         QSharedPointer<TabBoxClient> firstClient = m_mutableClientList.constFirst();
0217         if (firstClient) {
0218             start = firstClient;
0219         }
0220     }
0221 
0222     m_mutableClientList.clear();
0223     TabBoxClientList stickyClients;
0224 
0225     switch (tabBox->config().clientSwitchingMode()) {
0226     case TabBoxConfig::FocusChainSwitching: {
0227         createFocusChainClientList(desktop, start, stickyClients);
0228         break;
0229     }
0230     case TabBoxConfig::StackingOrderSwitching: {
0231         createStackingOrderClientList(desktop, start, stickyClients);
0232         break;
0233     }
0234     }
0235 
0236     if (tabBox->config().orderMinimizedMode() == TabBoxConfig::GroupByMinimized) {
0237         // Put all non-minimized included clients first.
0238         std::stable_partition(m_mutableClientList.begin(), m_mutableClientList.end(), [](const auto &client) {
0239             return !client.toStrongRef()->isMinimized();
0240         });
0241     }
0242 
0243     for (const QWeakPointer<TabBoxClient> &c : std::as_const(stickyClients)) {
0244         m_mutableClientList.removeAll(c);
0245         m_mutableClientList.prepend(c);
0246     }
0247     if (tabBox->config().clientApplicationsMode() != TabBoxConfig::AllWindowsCurrentApplication
0248         && (tabBox->config().showDesktopMode() == TabBoxConfig::ShowDesktopClient || m_mutableClientList.isEmpty())) {
0249         QWeakPointer<TabBoxClient> desktopClient = tabBox->desktopClient();
0250         if (!desktopClient.isNull()) {
0251             m_mutableClientList.append(desktopClient);
0252         }
0253     }
0254 
0255     if (m_clientList == m_mutableClientList) {
0256         return;
0257     }
0258 
0259     beginResetModel();
0260     m_clientList = m_mutableClientList;
0261     endResetModel();
0262 }
0263 
0264 void ClientModel::close(int i)
0265 {
0266     QModelIndex ind = index(i, 0);
0267     if (!ind.isValid()) {
0268         return;
0269     }
0270     QSharedPointer<TabBoxClient> client = m_mutableClientList.at(i).toStrongRef();
0271     if (client) {
0272         client->close();
0273     }
0274 }
0275 
0276 void ClientModel::activate(int i)
0277 {
0278     QModelIndex ind = index(i, 0);
0279     if (!ind.isValid()) {
0280         return;
0281     }
0282     tabBox->setCurrentIndex(ind);
0283     tabBox->activateAndClose();
0284 }
0285 
0286 } // namespace Tabbox
0287 } // namespace KWin