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