File indexing completed on 2024-11-10 04:57:19

0001 /*
0002     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "windowmodel.h"
0008 #include "core/output.h"
0009 #include "core/outputbackend.h"
0010 #include "virtualdesktops.h"
0011 #include "window.h"
0012 #include "workspace.h"
0013 
0014 namespace KWin
0015 {
0016 
0017 WindowModel::WindowModel(QObject *parent)
0018     : QAbstractListModel(parent)
0019 {
0020     connect(workspace(), &Workspace::windowAdded, this, &WindowModel::handleWindowAdded);
0021     connect(workspace(), &Workspace::windowRemoved, this, &WindowModel::handleWindowRemoved);
0022 
0023     m_windows = workspace()->windows();
0024     for (Window *window : std::as_const(m_windows)) {
0025         setupWindowConnections(window);
0026     }
0027 }
0028 
0029 void WindowModel::markRoleChanged(Window *window, int role)
0030 {
0031     const QModelIndex row = index(m_windows.indexOf(window), 0);
0032     Q_EMIT dataChanged(row, row, {role});
0033 }
0034 
0035 void WindowModel::setupWindowConnections(Window *window)
0036 {
0037     connect(window, &Window::desktopsChanged, this, [this, window]() {
0038         markRoleChanged(window, DesktopRole);
0039     });
0040     connect(window, &Window::outputChanged, this, [this, window]() {
0041         markRoleChanged(window, OutputRole);
0042     });
0043     connect(window, &Window::activitiesChanged, this, [this, window]() {
0044         markRoleChanged(window, ActivityRole);
0045     });
0046 }
0047 
0048 void WindowModel::handleWindowAdded(Window *window)
0049 {
0050     beginInsertRows(QModelIndex(), m_windows.count(), m_windows.count());
0051     m_windows.append(window);
0052     endInsertRows();
0053 
0054     setupWindowConnections(window);
0055 }
0056 
0057 void WindowModel::handleWindowRemoved(Window *window)
0058 {
0059     const int index = m_windows.indexOf(window);
0060     Q_ASSERT(index != -1);
0061 
0062     beginRemoveRows(QModelIndex(), index, index);
0063     m_windows.removeAt(index);
0064     endRemoveRows();
0065 }
0066 
0067 QHash<int, QByteArray> WindowModel::roleNames() const
0068 {
0069     return {
0070         {Qt::DisplayRole, QByteArrayLiteral("display")},
0071         {WindowRole, QByteArrayLiteral("window")},
0072         {OutputRole, QByteArrayLiteral("output")},
0073         {DesktopRole, QByteArrayLiteral("desktop")},
0074         {ActivityRole, QByteArrayLiteral("activity")},
0075     };
0076 }
0077 
0078 QVariant WindowModel::data(const QModelIndex &index, int role) const
0079 {
0080     if (!index.isValid() || index.row() < 0 || index.row() >= m_windows.count()) {
0081         return QVariant();
0082     }
0083 
0084     Window *window = m_windows[index.row()];
0085     switch (role) {
0086     case Qt::DisplayRole:
0087     case WindowRole:
0088         return QVariant::fromValue(window);
0089     case OutputRole:
0090         return QVariant::fromValue(window->output());
0091     case DesktopRole:
0092         return QVariant::fromValue(window->desktops());
0093     case ActivityRole:
0094         return window->activities();
0095     default:
0096         return QVariant();
0097     }
0098 }
0099 
0100 int WindowModel::rowCount(const QModelIndex &parent) const
0101 {
0102     return parent.isValid() ? 0 : m_windows.count();
0103 }
0104 
0105 WindowFilterModel::WindowFilterModel(QObject *parent)
0106     : QSortFilterProxyModel(parent)
0107 {
0108 }
0109 
0110 WindowModel *WindowFilterModel::windowModel() const
0111 {
0112     return m_windowModel;
0113 }
0114 
0115 void WindowFilterModel::setWindowModel(WindowModel *windowModel)
0116 {
0117     if (windowModel == m_windowModel) {
0118         return;
0119     }
0120     m_windowModel = windowModel;
0121     setSourceModel(m_windowModel);
0122     Q_EMIT windowModelChanged();
0123 }
0124 
0125 QString WindowFilterModel::activity() const
0126 {
0127     return m_activity.value_or(QString());
0128 }
0129 
0130 void WindowFilterModel::setActivity(const QString &activity)
0131 {
0132     if (m_activity != activity) {
0133         m_activity = activity;
0134         Q_EMIT activityChanged();
0135         invalidateFilter();
0136     }
0137 }
0138 
0139 void WindowFilterModel::resetActivity()
0140 {
0141     if (m_activity.has_value()) {
0142         m_activity.reset();
0143         Q_EMIT activityChanged();
0144         invalidateFilter();
0145     }
0146 }
0147 
0148 VirtualDesktop *WindowFilterModel::desktop() const
0149 {
0150     return m_desktop;
0151 }
0152 
0153 void WindowFilterModel::setDesktop(VirtualDesktop *desktop)
0154 {
0155     if (m_desktop != desktop) {
0156         m_desktop = desktop;
0157         Q_EMIT desktopChanged();
0158         invalidateFilter();
0159     }
0160 }
0161 
0162 void WindowFilterModel::resetDesktop()
0163 {
0164     setDesktop(nullptr);
0165 }
0166 
0167 QString WindowFilterModel::filter() const
0168 {
0169     return m_filter;
0170 }
0171 
0172 void WindowFilterModel::setFilter(const QString &filter)
0173 {
0174     if (filter == m_filter) {
0175         return;
0176     }
0177     m_filter = filter;
0178     Q_EMIT filterChanged();
0179     invalidateFilter();
0180 }
0181 
0182 QString WindowFilterModel::screenName() const
0183 {
0184     return m_output ? m_output->name() : QString();
0185 }
0186 
0187 void WindowFilterModel::setScreenName(const QString &screen)
0188 {
0189     Output *output = kwinApp()->outputBackend()->findOutput(screen);
0190     if (m_output != output) {
0191         m_output = output;
0192         Q_EMIT screenNameChanged();
0193         invalidateFilter();
0194     }
0195 }
0196 
0197 void WindowFilterModel::resetScreenName()
0198 {
0199     if (m_output) {
0200         m_output = nullptr;
0201         Q_EMIT screenNameChanged();
0202         invalidateFilter();
0203     }
0204 }
0205 
0206 WindowFilterModel::WindowTypes WindowFilterModel::windowType() const
0207 {
0208     return m_windowType.value_or(WindowTypes());
0209 }
0210 
0211 void WindowFilterModel::setWindowType(WindowTypes windowType)
0212 {
0213     if (m_windowType != windowType) {
0214         m_windowType = windowType;
0215         Q_EMIT windowTypeChanged();
0216         invalidateFilter();
0217     }
0218 }
0219 
0220 void WindowFilterModel::resetWindowType()
0221 {
0222     if (m_windowType.has_value()) {
0223         m_windowType.reset();
0224         Q_EMIT windowTypeChanged();
0225         invalidateFilter();
0226     }
0227 }
0228 
0229 void WindowFilterModel::setMinimizedWindows(bool show)
0230 {
0231     if (m_showMinimizedWindows == show) {
0232         return;
0233     }
0234 
0235     m_showMinimizedWindows = show;
0236     invalidateFilter();
0237     Q_EMIT minimizedWindowsChanged();
0238 }
0239 
0240 bool WindowFilterModel::minimizedWindows() const
0241 {
0242     return m_showMinimizedWindows;
0243 }
0244 
0245 bool WindowFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
0246 {
0247     if (!m_windowModel) {
0248         return false;
0249     }
0250     const QModelIndex index = m_windowModel->index(sourceRow, 0, sourceParent);
0251     if (!index.isValid()) {
0252         return false;
0253     }
0254     const QVariant data = index.data();
0255     if (!data.isValid()) {
0256         // an invalid QVariant is valid data
0257         return true;
0258     }
0259 
0260     Window *window = qvariant_cast<Window *>(data);
0261     if (!window) {
0262         return false;
0263     }
0264 
0265     if (m_activity.has_value()) {
0266         if (!window->isOnActivity(*m_activity)) {
0267             return false;
0268         }
0269     }
0270 
0271     if (m_desktop) {
0272         if (!window->isOnDesktop(m_desktop)) {
0273             return false;
0274         }
0275     }
0276 
0277     if (m_output) {
0278         if (!window->isOnOutput(m_output)) {
0279             return false;
0280         }
0281     }
0282 
0283     if (m_windowType.has_value()) {
0284         if (!(windowTypeMask(window) & *m_windowType)) {
0285             return false;
0286         }
0287     }
0288 
0289     if (!m_filter.isEmpty()) {
0290         if (window->caption().contains(m_filter, Qt::CaseInsensitive)) {
0291             return true;
0292         }
0293         if (window->windowRole().contains(m_filter, Qt::CaseInsensitive)) {
0294             return true;
0295         }
0296         if (window->resourceName().contains(m_filter, Qt::CaseInsensitive)) {
0297             return true;
0298         }
0299         if (window->resourceClass().contains(m_filter, Qt::CaseInsensitive)) {
0300             return true;
0301         }
0302         return false;
0303     }
0304 
0305     if (!m_showMinimizedWindows) {
0306         return !window->isMinimized();
0307     }
0308     return true;
0309 }
0310 
0311 WindowFilterModel::WindowTypes WindowFilterModel::windowTypeMask(Window *window) const
0312 {
0313     WindowTypes mask;
0314     if (window->isNormalWindow()) {
0315         mask |= WindowType::Normal;
0316     } else if (window->isDialog()) {
0317         mask |= WindowType::Dialog;
0318     } else if (window->isDock()) {
0319         mask |= WindowType::Dock;
0320     } else if (window->isDesktop()) {
0321         mask |= WindowType::Desktop;
0322     } else if (window->isNotification()) {
0323         mask |= WindowType::Notification;
0324     } else if (window->isCriticalNotification()) {
0325         mask |= WindowType::CriticalNotification;
0326     }
0327     return mask;
0328 }
0329 
0330 } // namespace KWin
0331 
0332 #include "moc_windowmodel.cpp"