File indexing completed on 2024-04-28 05:36:51

0001 /*
0002  * SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "outputsmodel.h"
0008 #include <KLocalizedString>
0009 #include <QGuiApplication>
0010 #include <QIcon>
0011 
0012 OutputsModel::OutputsModel(Options o, QObject *parent)
0013     : QAbstractListModel(parent)
0014 {
0015     const auto screens = qGuiApp->screens();
0016 
0017     // Only show the full workspace if there's several outputs
0018     if (screens.count() > 1 && (o & WorkspaceIncluded)) {
0019         m_outputs << Output{Output::Workspace, nullptr, i18n("Full Workspace"), "Workspace", {}};
0020     }
0021 
0022     if (o & VirtualIncluded) {
0023         static quint64 i = 0;
0024         m_outputs << Output{Output::Virtual, nullptr, i18n("New Virtual Output"), QStringLiteral("Virtual%1").arg(i++), {}};
0025     }
0026 
0027     if (o & RegionIncluded) {
0028         m_outputs << Output{Output::Region, nullptr, i18n("Rectangular Region"), "Region", {}};
0029     }
0030 
0031     for (const auto screen : screens) {
0032         auto model = screen->model();
0033         Output::OutputType type = Output::Unknown;
0034 
0035         static const auto embedded = {
0036             QLatin1String("LVDS"),
0037             QLatin1String("IDP"),
0038             QLatin1String("EDP"),
0039             QLatin1String("LCD"),
0040         };
0041 
0042         for (const auto &prefix : embedded) {
0043             if (model.startsWith(prefix, Qt::CaseInsensitive)) {
0044                 type = Output::OutputType::Laptop;
0045                 model = i18n("Laptop screen");
0046                 break;
0047             }
0048         }
0049 
0050         if (type == Output::OutputType::Unknown) {
0051             if (model.contains(QLatin1String("TV"))) {
0052                 type = Output::OutputType::Television;
0053             } else {
0054                 type = Output::OutputType::Monitor;
0055             }
0056         }
0057 
0058         const QPoint pos = screen->geometry().topLeft();
0059         m_outputs << Output(type, screen, model, QStringLiteral("%1x%2").arg(pos.x()).arg(pos.y()), screen->name());
0060     }
0061 }
0062 
0063 OutputsModel::~OutputsModel() = default;
0064 
0065 int OutputsModel::rowCount(const QModelIndex &parent) const
0066 {
0067     return parent.isValid() ? 0 : m_outputs.count();
0068 }
0069 
0070 QHash<int, QByteArray> OutputsModel::roleNames() const
0071 {
0072     return QHash<int, QByteArray>{
0073         {Qt::DisplayRole, "display"},
0074         {Qt::DecorationRole, "decoration"},
0075         {Qt::CheckStateRole, "checked"},
0076         {ScreenRole, "screen"},
0077         {NameRole, "name"},
0078     };
0079 }
0080 
0081 QVariant OutputsModel::data(const QModelIndex &index, int role) const
0082 {
0083     if (!checkIndex(index, CheckIndexOption::IndexIsValid)) {
0084         return {};
0085     }
0086 
0087     const auto &output = m_outputs[index.row()];
0088     switch (role) {
0089     case ScreenRole:
0090         return QVariant::fromValue(output.screen());
0091         return 0;
0092     case NameRole:
0093         return output.name();
0094     case Qt::DecorationRole:
0095         return QIcon::fromTheme(output.iconName());
0096     case Qt::DisplayRole:
0097         return output.display();
0098     case Qt::CheckStateRole:
0099         return m_selectedRows.contains(index.row()) ? Qt::Checked : Qt::Unchecked;
0100     }
0101     return {};
0102 }
0103 
0104 bool OutputsModel::setData(const QModelIndex &index, const QVariant &value, int role)
0105 {
0106     if (!checkIndex(index, CheckIndexOption::IndexIsValid) || role != Qt::CheckStateRole) {
0107         return false;
0108     }
0109 
0110     if (index.data(Qt::CheckStateRole) == value) {
0111         return true;
0112     }
0113 
0114     if (value == Qt::Checked) {
0115         m_selectedRows.insert(index.row());
0116     } else {
0117         m_selectedRows.remove(index.row());
0118     }
0119     Q_EMIT dataChanged(index, index, {role});
0120     if (m_selectedRows.count() <= 1) {
0121         Q_EMIT hasSelectionChanged();
0122     }
0123     return true;
0124 }
0125 
0126 const Output &OutputsModel::outputAt(int row) const
0127 {
0128     return m_outputs[row];
0129 }
0130 
0131 void OutputsModel::clearSelection()
0132 {
0133     if (m_selectedRows.isEmpty())
0134         return;
0135 
0136     auto selected = m_selectedRows;
0137     m_selectedRows.clear();
0138     for (int i = 0, c = rowCount({}); i < c; ++i) {
0139         if (selected.contains(i)) {
0140             const auto idx = index(i, 0);
0141             Q_EMIT dataChanged(idx, idx, {Qt::CheckStateRole});
0142         }
0143     }
0144     Q_EMIT hasSelectionChanged();
0145 }
0146 
0147 QList<Output> OutputsModel::selectedOutputs() const
0148 {
0149     QList<Output> ret;
0150     ret.reserve(m_selectedRows.count());
0151     for (auto x : std::as_const(m_selectedRows)) {
0152         ret << m_outputs[x];
0153     }
0154     return ret;
0155 }