File indexing completed on 2024-04-28 16:55:47
0001 /* 0002 * SPDX-FileCopyrightText: 2018 Red Hat Inc 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 * 0006 * SPDX-FileCopyrightText: 2018 Jan Grulich <jgrulich@redhat.com> 0007 */ 0008 0009 #include "screenchooserdialog.h" 0010 #include "utils.h" 0011 0012 #include <KLocalizedString> 0013 #include <KWayland/Client/plasmawindowmanagement.h> 0014 #include <KWayland/Client/plasmawindowmodel.h> 0015 0016 #include <QCoreApplication> 0017 #include <QSettings> 0018 #include <QSortFilterProxyModel> 0019 #include <QStandardPaths> 0020 #include <QWindow> 0021 0022 class FilteredWindowModel : public QSortFilterProxyModel 0023 { 0024 Q_OBJECT 0025 Q_PROPERTY(bool hasSelection READ hasSelection NOTIFY hasSelectionChanged) 0026 public: 0027 FilteredWindowModel(QObject *parent) 0028 : QSortFilterProxyModel(parent) 0029 { 0030 } 0031 0032 bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override 0033 { 0034 if (source_parent.isValid()) 0035 return false; 0036 0037 const auto idx = sourceModel()->index(source_row, 0); 0038 using KWayland::Client::PlasmaWindowModel; 0039 0040 return !idx.data(PlasmaWindowModel::SkipTaskbar).toBool() // 0041 && !idx.data(PlasmaWindowModel::SkipSwitcher).toBool() // 0042 && idx.data(PlasmaWindowModel::Pid) != QCoreApplication::applicationPid(); 0043 } 0044 0045 QMap<int, QVariant> itemData(const QModelIndex &index) const override 0046 { 0047 using KWayland::Client::PlasmaWindowModel; 0048 auto ret = QSortFilterProxyModel::itemData(index); 0049 for (int i = PlasmaWindowModel::AppId; i <= PlasmaWindowModel::Uuid; ++i) { 0050 ret[i] = index.data(i); 0051 } 0052 return ret; 0053 } 0054 0055 bool setData(const QModelIndex &index, const QVariant &value, int role) override 0056 { 0057 if (!checkIndex(index, CheckIndexOption::IndexIsValid) || role != Qt::CheckStateRole) { 0058 return false; 0059 } 0060 0061 using KWayland::Client::PlasmaWindowModel; 0062 const QString uuid = index.data(PlasmaWindowModel::Uuid).toString(); 0063 if (value == Qt::Checked) { 0064 m_selected.insert(index); 0065 } else { 0066 m_selected.remove(index); 0067 } 0068 Q_EMIT dataChanged(index, index, {role}); 0069 if (m_selected.count() <= 1) { 0070 Q_EMIT hasSelectionChanged(); 0071 } 0072 return true; 0073 } 0074 0075 QVector<QMap<int, QVariant>> selectedWindows() const 0076 { 0077 QVector<QMap<int, QVariant>> ret; 0078 ret.reserve(m_selected.size()); 0079 for (const auto &index : m_selected) { 0080 if (index.isValid()) 0081 ret << itemData(index); 0082 } 0083 return ret; 0084 } 0085 0086 QHash<int, QByteArray> roleNames() const override 0087 { 0088 QHash<int, QByteArray> ret = sourceModel()->roleNames(); 0089 ret.insert(Qt::CheckStateRole, "checked"); 0090 return ret; 0091 } 0092 0093 QVariant data(const QModelIndex &index, int role) const override 0094 { 0095 if (!checkIndex(index, CheckIndexOption::IndexIsValid)) { 0096 return {}; 0097 } 0098 0099 switch (role) { 0100 case Qt::CheckStateRole: 0101 return m_selected.contains(index) ? Qt::Checked : Qt::Unchecked; 0102 default: 0103 return QSortFilterProxyModel::data(index, role); 0104 } 0105 return {}; 0106 } 0107 0108 bool hasSelection() 0109 { 0110 return !m_selected.isEmpty(); 0111 } 0112 0113 void clearSelection() 0114 { 0115 auto selected = m_selected; 0116 m_selected.clear(); 0117 0118 for (const auto &index : selected) { 0119 if (index.isValid()) 0120 Q_EMIT dataChanged(index, index, {Qt::CheckStateRole}); 0121 } 0122 Q_EMIT hasSelectionChanged(); 0123 } 0124 0125 Q_SIGNALS: 0126 void hasSelectionChanged(); 0127 0128 private: 0129 QSet<QPersistentModelIndex> m_selected; 0130 }; 0131 0132 ScreenChooserDialog::ScreenChooserDialog(const QString &appName, bool multiple, ScreenCastPortal::SourceTypes types) 0133 : QuickDialog() 0134 { 0135 Q_ASSERT(types != 0); 0136 0137 QVariantMap props = { 0138 {"title", i18n("Screen Sharing")}, 0139 {"multiple", multiple}, 0140 }; 0141 0142 int numberOfMonitors = 0; 0143 if (types & ScreenCastPortal::Monitor) { 0144 auto model = new OutputsModel(OutputsModel::Options(OutputsModel::WorkspaceIncluded | OutputsModel::VirtualIncluded), this); 0145 props.insert("outputsModel", QVariant::fromValue<QObject *>(model)); 0146 numberOfMonitors += model->rowCount(QModelIndex()); 0147 connect(this, &ScreenChooserDialog::clearSelection, model, &OutputsModel::clearSelection); 0148 } 0149 0150 int numberOfWindows = 0; 0151 if (types & ScreenCastPortal::Window) { 0152 auto model = new KWayland::Client::PlasmaWindowModel(WaylandIntegration::plasmaWindowManagement()); 0153 auto windowsProxy = new FilteredWindowModel(this); 0154 windowsProxy->setSourceModel(model); 0155 props.insert("windowsModel", QVariant::fromValue<QObject *>(windowsProxy)); 0156 connect(this, &ScreenChooserDialog::clearSelection, windowsProxy, &FilteredWindowModel::clearSelection); 0157 numberOfWindows += model->rowCount(QModelIndex()); 0158 } 0159 0160 const QString applicationName = Utils::applicationName(appName); 0161 0162 QString mainText; 0163 0164 // App asked for monitors and windows 0165 if (types & ScreenCastPortal::Monitor && types & ScreenCastPortal::Window) { 0166 if (appName.isEmpty()) { 0167 mainText = i18n("Choose what to share with the requesting application:"); 0168 } else { 0169 mainText = i18n("Choose what to share with %1:", applicationName); 0170 } 0171 } 0172 0173 // App only asked for monitors 0174 else if (types & ScreenCastPortal::Monitor) { 0175 if (numberOfMonitors == 1) { 0176 if (appName.isEmpty()) { 0177 mainText = i18n("Share this screen with the requesting application?"); 0178 } else { 0179 mainText = i18n("Share this screen with %1?", applicationName); 0180 } 0181 } else { 0182 if (multiple) { 0183 if (appName.isEmpty()) { 0184 mainText = i18n("Choose screens to share with the requesting application:"); 0185 } else { 0186 mainText = i18n("Choose screens to share with %1:", applicationName); 0187 } 0188 } else { 0189 if (appName.isEmpty()) { 0190 mainText = i18n("Choose which screen to share with the requesting application:"); 0191 } else { 0192 mainText = i18n("Choose which screen to share with %1:", applicationName); 0193 } 0194 } 0195 } 0196 } 0197 0198 // App only asked for windows 0199 else if (types & ScreenCastPortal::Window) { 0200 if (numberOfWindows == 1) { 0201 if (appName.isEmpty()) { 0202 mainText = i18n("Share this window with the requesting application?"); 0203 } else { 0204 mainText = i18n("Share this window with %1?", applicationName); 0205 } 0206 } else { 0207 if (multiple) { 0208 if (appName.isEmpty()) { 0209 mainText = i18n("Choose windows to share with the requesting application:"); 0210 } else { 0211 mainText = i18n("Choose windows to share with %1:", applicationName); 0212 } 0213 } else { 0214 if (appName.isEmpty()) { 0215 mainText = i18n("Choose which window to share with the requesting application:"); 0216 } else { 0217 mainText = i18n("Choose which window to share with %1:", applicationName); 0218 } 0219 } 0220 } 0221 } 0222 props.insert("mainText", mainText); 0223 0224 create(QStringLiteral("qrc:/ScreenChooserDialog.qml"), props); 0225 connect(m_theDialog, SIGNAL(clearSelection()), this, SIGNAL(clearSelection())); 0226 } 0227 0228 ScreenChooserDialog::~ScreenChooserDialog() = default; 0229 0230 QList<Output> ScreenChooserDialog::selectedOutputs() const 0231 { 0232 OutputsModel *model = dynamic_cast<OutputsModel *>(m_theDialog->property("outputsModel").value<QObject *>()); 0233 if (!model) { 0234 return {}; 0235 } 0236 return model->selectedOutputs(); 0237 } 0238 0239 QVector<QMap<int, QVariant>> ScreenChooserDialog::selectedWindows() const 0240 { 0241 FilteredWindowModel *model = dynamic_cast<FilteredWindowModel *>(m_theDialog->property("windowsModel").value<QObject *>()); 0242 if (!model) { 0243 return {}; 0244 } 0245 return model->selectedWindows(); 0246 } 0247 0248 bool ScreenChooserDialog::allowRestore() const 0249 { 0250 return m_theDialog->property("allowRestore").toBool(); 0251 } 0252 0253 #include "screenchooserdialog.moc"