File indexing completed on 2024-05-19 05:32:26

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 #include "clientmodel.h"
0011 #include "tabboxconfig.h"
0012 #include "window.h"
0013 
0014 #include <KLocalizedString>
0015 
0016 #include <QIcon>
0017 #include <QUuid>
0018 
0019 #include <cmath>
0020 
0021 namespace KWin
0022 {
0023 namespace TabBox
0024 {
0025 
0026 ClientModel::ClientModel(QObject *parent)
0027     : QAbstractItemModel(parent)
0028 {
0029 }
0030 
0031 ClientModel::~ClientModel()
0032 {
0033 }
0034 
0035 QVariant ClientModel::data(const QModelIndex &index, int role) const
0036 {
0037     if (!index.isValid()) {
0038         return QVariant();
0039     }
0040 
0041     if (m_clientList.isEmpty()) {
0042         return QVariant();
0043     }
0044 
0045     int clientIndex = index.row();
0046     if (clientIndex >= m_clientList.count()) {
0047         return QVariant();
0048     }
0049     Window *client = m_clientList[clientIndex];
0050     if (!client) {
0051         return QVariant();
0052     }
0053     switch (role) {
0054     case Qt::DisplayRole:
0055     case CaptionRole: {
0056         if (client->isDesktop()) {
0057             return i18nc("Special entry in alt+tab list for minimizing all windows",
0058                          "Show Desktop");
0059         }
0060         return client->caption();
0061     }
0062     case ClientRole:
0063         return QVariant::fromValue<void *>(client);
0064     case DesktopNameRole: {
0065         return tabBox->desktopName(client);
0066     }
0067     case WIdRole:
0068         return client->internalId();
0069     case MinimizedRole:
0070         return client->isMinimized();
0071     case CloseableRole:
0072         return client->isCloseable();
0073     case IconRole:
0074         if (client->isDesktop()) {
0075             return QIcon::fromTheme(QStringLiteral("user-desktop"));
0076         }
0077         return client->icon();
0078     default:
0079         return QVariant();
0080     }
0081 }
0082 
0083 QString ClientModel::longestCaption() const
0084 {
0085     QString caption;
0086     for (Window *window : std::as_const(m_clientList)) {
0087         if (window->caption().size() > caption.size()) {
0088             caption = window->caption();
0089         }
0090     }
0091     return caption;
0092 }
0093 
0094 int ClientModel::columnCount(const QModelIndex &parent) const
0095 {
0096     return 1;
0097 }
0098 
0099 int ClientModel::rowCount(const QModelIndex &parent) const
0100 {
0101     if (parent.isValid()) {
0102         return 0;
0103     }
0104     return m_clientList.count();
0105 }
0106 
0107 QModelIndex ClientModel::parent(const QModelIndex &child) const
0108 {
0109     return QModelIndex();
0110 }
0111 
0112 QModelIndex ClientModel::index(int row, int column, const QModelIndex &parent) const
0113 {
0114     if (row < 0 || column != 0 || parent.isValid()) {
0115         return QModelIndex();
0116     }
0117     int index = row * columnCount();
0118     if (index >= m_clientList.count() && !m_clientList.isEmpty()) {
0119         return QModelIndex();
0120     }
0121     return createIndex(row, 0);
0122 }
0123 
0124 QHash<int, QByteArray> ClientModel::roleNames() const
0125 {
0126     return {
0127         {CaptionRole, QByteArrayLiteral("caption")},
0128         {DesktopNameRole, QByteArrayLiteral("desktopName")},
0129         {MinimizedRole, QByteArrayLiteral("minimized")},
0130         {WIdRole, QByteArrayLiteral("windowId")},
0131         {CloseableRole, QByteArrayLiteral("closeable")},
0132         {IconRole, QByteArrayLiteral("icon")},
0133     };
0134 }
0135 
0136 QModelIndex ClientModel::index(Window *client) const
0137 {
0138     const int index = m_clientList.indexOf(client);
0139     if (index == -1) {
0140         return QModelIndex();
0141     }
0142     int row = index / columnCount();
0143     int column = index % columnCount();
0144     return createIndex(row, column);
0145 }
0146 
0147 void ClientModel::createFocusChainClientList(Window *start)
0148 {
0149     auto c = start;
0150     if (!tabBox->isInFocusChain(c)) {
0151         Window *firstClient = tabBox->firstClientFocusChain();
0152         if (firstClient) {
0153             c = firstClient;
0154         }
0155     }
0156     auto stop = c;
0157     do {
0158         Window *add = tabBox->clientToAddToList(c);
0159         if (add) {
0160             m_mutableClientList += add;
0161         }
0162         c = tabBox->nextClientFocusChain(c);
0163     } while (c && c != stop);
0164 }
0165 
0166 void ClientModel::createStackingOrderClientList(Window *start)
0167 {
0168     // TODO: needs improvement
0169     const QList<Window *> stacking = tabBox->stackingOrder();
0170     auto c = stacking.first();
0171     auto stop = c;
0172     int index = 0;
0173     while (c) {
0174         Window *add = tabBox->clientToAddToList(c);
0175         if (add) {
0176             if (start == add) {
0177                 m_mutableClientList.removeAll(add);
0178                 m_mutableClientList.prepend(add);
0179             } else {
0180                 m_mutableClientList += add;
0181             }
0182         }
0183         if (index >= stacking.size() - 1) {
0184             c = nullptr;
0185         } else {
0186             c = stacking[++index];
0187         }
0188 
0189         if (c == stop) {
0190             break;
0191         }
0192     }
0193 }
0194 
0195 void ClientModel::createClientList(bool partialReset)
0196 {
0197     auto start = tabBox->activeClient();
0198     // TODO: new clients are not added at correct position
0199     if (partialReset && !m_mutableClientList.isEmpty()) {
0200         Window *firstClient = m_mutableClientList.constFirst();
0201         if (!firstClient->isDeleted()) {
0202             start = firstClient;
0203         }
0204     }
0205 
0206     m_mutableClientList.clear();
0207 
0208     switch (tabBox->config().clientSwitchingMode()) {
0209     case TabBoxConfig::FocusChainSwitching: {
0210         createFocusChainClientList(start);
0211         break;
0212     }
0213     case TabBoxConfig::StackingOrderSwitching: {
0214         createStackingOrderClientList(start);
0215         break;
0216     }
0217     }
0218 
0219     if (tabBox->config().orderMinimizedMode() == TabBoxConfig::GroupByMinimized) {
0220         // Put all non-minimized included clients first.
0221         std::stable_partition(m_mutableClientList.begin(), m_mutableClientList.end(), [](const auto &client) {
0222             return !client->isMinimized();
0223         });
0224     }
0225 
0226     if (!m_mutableClientList.isEmpty()
0227         && tabBox->config().clientApplicationsMode() != TabBoxConfig::AllWindowsCurrentApplication
0228         && tabBox->config().showDesktopMode() == TabBoxConfig::ShowDesktopClient) {
0229         Window *desktopClient = tabBox->desktopClient();
0230         if (desktopClient) {
0231             m_mutableClientList.append(desktopClient);
0232         }
0233     }
0234 
0235     if (m_clientList == m_mutableClientList) {
0236         return;
0237     }
0238 
0239     beginResetModel();
0240     m_clientList = m_mutableClientList;
0241     endResetModel();
0242 }
0243 
0244 void ClientModel::close(int i)
0245 {
0246     QModelIndex ind = index(i, 0);
0247     if (!ind.isValid()) {
0248         return;
0249     }
0250     Window *client = m_mutableClientList.at(i);
0251     if (client) {
0252         client->closeWindow();
0253     }
0254 }
0255 
0256 void ClientModel::activate(int i)
0257 {
0258     QModelIndex ind = index(i, 0);
0259     if (!ind.isValid()) {
0260         return;
0261     }
0262     tabBox->setCurrentIndex(ind);
0263     tabBox->activateAndClose();
0264 }
0265 
0266 } // namespace Tabbox
0267 } // namespace KWin
0268 
0269 #include "moc_clientmodel.cpp"