File indexing completed on 2024-05-05 05:38:34

0001 /*
0002     SPDX-FileCopyrightText: 2016 Eike Hein <hein@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "taskfilterproxymodel.h"
0008 #include "abstracttasksmodel.h"
0009 
0010 #include "launchertasksmodel_p.h"
0011 
0012 #include "config-X11.h"
0013 #if HAVE_X11
0014 #include <QGuiApplication>
0015 #include <QScreen>
0016 
0017 #include <KWindowSystem>
0018 #endif
0019 
0020 namespace TaskManager
0021 {
0022 class Q_DECL_HIDDEN TaskFilterProxyModel::Private
0023 {
0024 public:
0025     Private(TaskFilterProxyModel *q);
0026 
0027     AbstractTasksModelIface *sourceTasksModel = nullptr;
0028 
0029     QVariant virtualDesktop;
0030     QRect screenGeometry;
0031     QRect regionGeometry;
0032     QString activity;
0033 
0034     bool filterByVirtualDesktop = false;
0035     bool filterByScreen = false;
0036     bool filterByActivity = false;
0037     RegionFilterMode::Mode filterByRegion = RegionFilterMode::Mode::Disabled;
0038     bool filterMinimized = false;
0039     bool filterNotMinimized = false;
0040     bool filterNotMaximized = false;
0041     bool filterHidden = false;
0042     bool filterSkipTaskbar = true;
0043     bool filterSkipPager = false;
0044 
0045     bool demandingAttentionSkipsFilters = true;
0046 };
0047 
0048 TaskFilterProxyModel::Private::Private(TaskFilterProxyModel *)
0049 {
0050 }
0051 
0052 TaskFilterProxyModel::TaskFilterProxyModel(QObject *parent)
0053     : QSortFilterProxyModel(parent)
0054     , d(new Private(this))
0055 {
0056 }
0057 
0058 TaskFilterProxyModel::~TaskFilterProxyModel()
0059 {
0060 }
0061 
0062 void TaskFilterProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
0063 {
0064     d->sourceTasksModel = dynamic_cast<AbstractTasksModelIface *>(sourceModel);
0065 
0066     QSortFilterProxyModel::setSourceModel(sourceModel);
0067 }
0068 
0069 QVariant TaskFilterProxyModel::virtualDesktop() const
0070 {
0071     return d->virtualDesktop;
0072 }
0073 
0074 void TaskFilterProxyModel::setVirtualDesktop(const QVariant &desktop)
0075 {
0076     if (d->virtualDesktop != desktop) {
0077         d->virtualDesktop = desktop;
0078 
0079         if (d->filterByVirtualDesktop) {
0080             invalidateFilter();
0081         }
0082 
0083         Q_EMIT virtualDesktopChanged();
0084     }
0085 }
0086 
0087 QRect TaskFilterProxyModel::screenGeometry() const
0088 {
0089     return d->screenGeometry;
0090 }
0091 
0092 void TaskFilterProxyModel::setScreenGeometry(const QRect &geometry)
0093 {
0094     if (d->screenGeometry != geometry) {
0095         d->screenGeometry = geometry;
0096 
0097         if (d->filterByScreen) {
0098             invalidateFilter();
0099         }
0100 
0101         Q_EMIT screenGeometryChanged();
0102     }
0103 }
0104 
0105 QRect TaskFilterProxyModel::regionGeometry() const
0106 {
0107     return d->regionGeometry;
0108 }
0109 
0110 void TaskFilterProxyModel::setRegionGeometry(const QRect &geometry)
0111 {
0112     if (d->regionGeometry == geometry) {
0113         return;
0114     }
0115     d->regionGeometry = geometry;
0116 
0117     if (d->filterByRegion != RegionFilterMode::Mode::Disabled) {
0118         invalidateFilter();
0119     }
0120 
0121     Q_EMIT regionGeometryChanged();
0122 }
0123 
0124 QString TaskFilterProxyModel::activity() const
0125 {
0126     return d->activity;
0127 }
0128 
0129 void TaskFilterProxyModel::setActivity(const QString &activity)
0130 {
0131     if (d->activity != activity) {
0132         d->activity = activity;
0133 
0134         if (d->filterByActivity) {
0135             invalidateFilter();
0136         }
0137 
0138         Q_EMIT activityChanged();
0139     }
0140 }
0141 
0142 bool TaskFilterProxyModel::filterByVirtualDesktop() const
0143 {
0144     return d->filterByVirtualDesktop;
0145 }
0146 
0147 void TaskFilterProxyModel::setFilterByVirtualDesktop(bool filter)
0148 {
0149     if (d->filterByVirtualDesktop != filter) {
0150         d->filterByVirtualDesktop = filter;
0151 
0152         invalidateFilter();
0153 
0154         Q_EMIT filterByVirtualDesktopChanged();
0155     }
0156 }
0157 
0158 bool TaskFilterProxyModel::filterByScreen() const
0159 {
0160     return d->filterByScreen;
0161 }
0162 
0163 void TaskFilterProxyModel::setFilterByScreen(bool filter)
0164 {
0165     if (d->filterByScreen != filter) {
0166         d->filterByScreen = filter;
0167 
0168         invalidateFilter();
0169 
0170         Q_EMIT filterByScreenChanged();
0171     }
0172 }
0173 
0174 bool TaskFilterProxyModel::filterByActivity() const
0175 {
0176     return d->filterByActivity;
0177 }
0178 
0179 void TaskFilterProxyModel::setFilterByActivity(bool filter)
0180 {
0181     if (d->filterByActivity != filter) {
0182         d->filterByActivity = filter;
0183 
0184         invalidateFilter();
0185 
0186         Q_EMIT filterByActivityChanged();
0187     }
0188 }
0189 
0190 RegionFilterMode::Mode TaskFilterProxyModel::filterByRegion() const
0191 {
0192     return d->filterByRegion;
0193 }
0194 
0195 void TaskFilterProxyModel::setFilterByRegion(RegionFilterMode::Mode mode)
0196 {
0197     if (d->filterByRegion == mode) {
0198         return;
0199     }
0200 
0201     d->filterByRegion = mode;
0202     invalidateFilter();
0203     Q_EMIT filterByActivityChanged();
0204 }
0205 
0206 bool TaskFilterProxyModel::filterMinimized() const
0207 {
0208     return d->filterMinimized;
0209 }
0210 
0211 void TaskFilterProxyModel::setFilterMinimized(bool filter)
0212 {
0213     if (d->filterMinimized == filter) {
0214         return;
0215     }
0216 
0217     d->filterMinimized = filter;
0218     invalidateFilter();
0219 
0220     Q_EMIT filterMinimizedChanged();
0221 }
0222 
0223 bool TaskFilterProxyModel::filterNotMinimized() const
0224 {
0225     return d->filterNotMinimized;
0226 }
0227 
0228 void TaskFilterProxyModel::setFilterNotMinimized(bool filter)
0229 {
0230     if (d->filterNotMinimized != filter) {
0231         d->filterNotMinimized = filter;
0232 
0233         invalidateFilter();
0234 
0235         Q_EMIT filterNotMinimizedChanged();
0236     }
0237 }
0238 
0239 bool TaskFilterProxyModel::filterNotMaximized() const
0240 {
0241     return d->filterNotMaximized;
0242 }
0243 
0244 void TaskFilterProxyModel::setFilterNotMaximized(bool filter)
0245 {
0246     if (d->filterNotMaximized != filter) {
0247         d->filterNotMaximized = filter;
0248 
0249         invalidateFilter();
0250 
0251         Q_EMIT filterNotMaximizedChanged();
0252     }
0253 }
0254 
0255 bool TaskFilterProxyModel::filterHidden() const
0256 {
0257     return d->filterHidden;
0258 }
0259 
0260 void TaskFilterProxyModel::setFilterHidden(bool filter)
0261 {
0262     if (d->filterHidden != filter) {
0263         d->filterHidden = filter;
0264 
0265         invalidateFilter();
0266 
0267         Q_EMIT filterHiddenChanged();
0268     }
0269 }
0270 
0271 bool TaskFilterProxyModel::filterSkipTaskbar() const
0272 {
0273     return d->filterSkipTaskbar;
0274 }
0275 
0276 void TaskFilterProxyModel::setFilterSkipTaskbar(bool filter)
0277 {
0278     if (d->filterSkipTaskbar != filter) {
0279         d->filterSkipTaskbar = filter;
0280 
0281         invalidateFilter();
0282 
0283         Q_EMIT filterSkipTaskbarChanged();
0284     }
0285 }
0286 
0287 bool TaskFilterProxyModel::filterSkipPager() const
0288 {
0289     return d->filterSkipPager;
0290 }
0291 
0292 void TaskFilterProxyModel::setFilterSkipPager(bool filter)
0293 {
0294     if (d->filterSkipPager != filter) {
0295         d->filterSkipPager = filter;
0296 
0297         invalidateFilter();
0298 
0299         Q_EMIT filterSkipPagerChanged();
0300     }
0301 }
0302 
0303 bool TaskFilterProxyModel::demandingAttentionSkipsFilters() const
0304 {
0305     return d->demandingAttentionSkipsFilters;
0306 }
0307 
0308 void TaskFilterProxyModel::setDemandingAttentionSkipsFilters(bool skip)
0309 {
0310     if (d->demandingAttentionSkipsFilters != skip) {
0311         d->demandingAttentionSkipsFilters = skip;
0312 
0313         invalidateFilter();
0314 
0315         Q_EMIT demandingAttentionSkipsFiltersChanged();
0316     }
0317 }
0318 
0319 QModelIndex TaskFilterProxyModel::mapIfaceToSource(const QModelIndex &index) const
0320 {
0321     return mapToSource(index);
0322 }
0323 
0324 bool TaskFilterProxyModel::acceptsRow(int sourceRow) const
0325 {
0326     const QModelIndex &sourceIdx = sourceModel()->index(sourceRow, 0);
0327 
0328     if (!sourceIdx.isValid()) {
0329         return false;
0330     }
0331 
0332     // Filter tasks that are not to be shown on the task bar.
0333     if (d->filterSkipTaskbar && sourceIdx.data(AbstractTasksModel::SkipTaskbar).toBool()) {
0334         return false;
0335     }
0336 
0337     // Filter tasks that are not to be shown on the pager.
0338     if (d->filterSkipPager && sourceIdx.data(AbstractTasksModel::SkipPager).toBool()) {
0339         return false;
0340     }
0341 
0342     // Filter by virtual desktop.
0343     if (d->filterByVirtualDesktop && !d->virtualDesktop.isNull()) {
0344         if (!sourceIdx.data(AbstractTasksModel::IsOnAllVirtualDesktops).toBool()
0345             && (!d->demandingAttentionSkipsFilters || !sourceIdx.data(AbstractTasksModel::IsDemandingAttention).toBool())) {
0346             const QVariantList &virtualDesktops = sourceIdx.data(AbstractTasksModel::VirtualDesktops).toList();
0347 
0348             if (!virtualDesktops.isEmpty() && !virtualDesktops.contains(d->virtualDesktop)) {
0349                 return false;
0350             }
0351         }
0352     }
0353 
0354     // Filter by screen.
0355     if (d->filterByScreen && d->screenGeometry.isValid()) {
0356         const QRect &screenGeometry = sourceIdx.data(AbstractTasksModel::ScreenGeometry).toRect();
0357 
0358         if (screenGeometry.isValid() && screenGeometry != d->screenGeometry) {
0359             return false;
0360         }
0361     }
0362 
0363     // Filter by region
0364     if (d->filterByRegion != RegionFilterMode::Mode::Disabled && d->regionGeometry.isValid()) {
0365         QRect windowGeometry = sourceIdx.data(AbstractTasksModel::Geometry).toRect();
0366 
0367         QRect regionGeometry = d->regionGeometry;
0368 #if HAVE_X11
0369         if (static const bool isX11 = KWindowSystem::isPlatformX11(); isX11 && windowGeometry.isValid()) {
0370             // On X11, in regionGeometry, the original point of the topLeft position belongs to the device coordinate system
0371             // but the size belongs to the logical coordinate system (which means the reported size is already divided by DPR)
0372             // Converting regionGeometry to device coordinate system is better than converting windowGeometry to logical
0373             // coordinate system because the window may span multiple screens, while the region is always on one screen.
0374             const double devicePixelRatio = qGuiApp->devicePixelRatio();
0375             const QPoint screenTopLeft = d->screenGeometry.topLeft();
0376             const QPoint regionTopLeft =
0377                 screenTopLeft + QPoint(regionGeometry.x() - screenTopLeft.x(), regionGeometry.y() - screenTopLeft.y()) * devicePixelRatio;
0378             regionGeometry = QRect(regionTopLeft, regionGeometry.size() * devicePixelRatio);
0379         }
0380 #endif
0381         switch (d->filterByRegion) {
0382         case RegionFilterMode::Mode::Inside: {
0383             if (!regionGeometry.contains(windowGeometry)) {
0384                 return false;
0385             }
0386             break;
0387         }
0388         case RegionFilterMode::Mode::Intersect: {
0389             if (!regionGeometry.intersects(windowGeometry)) {
0390                 return false;
0391             }
0392             break;
0393         }
0394         case RegionFilterMode::Mode::Outside: {
0395             if (regionGeometry.contains(windowGeometry)) {
0396                 return false;
0397             }
0398             break;
0399         }
0400         default:
0401             break;
0402         }
0403     }
0404 
0405     // Filter by activity.
0406     if (d->filterByActivity && !d->activity.isEmpty()) {
0407         if (!d->demandingAttentionSkipsFilters || !sourceIdx.data(AbstractTasksModel::IsDemandingAttention).toBool()) {
0408             const QVariant &activities = sourceIdx.data(AbstractTasksModel::Activities);
0409 
0410             if (!activities.isNull()) {
0411                 const QStringList l = activities.toStringList();
0412 
0413                 if (!l.isEmpty() && !l.contains(NULL_UUID) && !l.contains(d->activity)) {
0414                     return false;
0415                 }
0416             }
0417         }
0418     }
0419 
0420     // Filter not minimized.
0421     if (d->filterNotMinimized) {
0422         bool isMinimized = sourceIdx.data(AbstractTasksModel::IsMinimized).toBool();
0423 
0424         if (!isMinimized) {
0425             return false;
0426         }
0427     }
0428 
0429     // Filter out minimized windows
0430     if (d->filterMinimized) {
0431         const bool isMinimized = sourceIdx.data(AbstractTasksModel::IsMinimized).toBool();
0432 
0433         if (isMinimized) {
0434             return false;
0435         }
0436     }
0437 
0438     // Filter not maximized.
0439     if (d->filterNotMaximized) {
0440         bool isMaximized = sourceIdx.data(AbstractTasksModel::IsMaximized).toBool();
0441 
0442         if (!isMaximized) {
0443             return false;
0444         }
0445     }
0446 
0447     // Filter hidden.
0448     if (d->filterHidden) {
0449         bool isHidden = sourceIdx.data(AbstractTasksModel::IsHidden).toBool();
0450 
0451         if (isHidden) {
0452             return false;
0453         }
0454     }
0455 
0456     return true;
0457 }
0458 
0459 bool TaskFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
0460 {
0461     Q_UNUSED(sourceParent)
0462 
0463     return acceptsRow(sourceRow);
0464 }
0465 
0466 }