1 /*
2     SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
3     SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "computermodel.h"
9 #include "actionlist.h"
10 #include "simplefavoritesmodel.h"
11 
12 #include <QIcon>
13 
14 #include <KAuthorized>
15 #include <KConcatenateRowsProxyModel>
16 #include <KFilePlacesModel>
17 #include <KLocalizedString>
18 #include <KRun>
19 #include <Solid/Device>
20 
21 #include "krunner_interface.h"
22 
FilteredPlacesModel(QObject * parent)23 FilteredPlacesModel::FilteredPlacesModel(QObject *parent)
24     : QSortFilterProxyModel(parent)
25     , m_placesModel(new KFilePlacesModel(this))
26 {
27     setSourceModel(m_placesModel);
28     sort(0);
29 }
30 
~FilteredPlacesModel()31 FilteredPlacesModel::~FilteredPlacesModel()
32 {
33 }
34 
url(const QModelIndex & index) const35 QUrl FilteredPlacesModel::url(const QModelIndex &index) const
36 {
37     return KFilePlacesModel::convertedUrl(m_placesModel->url(mapToSource(index)));
38 }
39 
isDevice(const QModelIndex & index) const40 bool FilteredPlacesModel::isDevice(const QModelIndex &index) const
41 {
42     return m_placesModel->isDevice(mapToSource(index));
43 }
44 
deviceForIndex(const QModelIndex & index) const45 Solid::Device FilteredPlacesModel::deviceForIndex(const QModelIndex &index) const
46 {
47     return m_placesModel->deviceForIndex(mapToSource(index));
48 }
49 
filterAcceptsRow(int sourceRow,const QModelIndex & sourceParent) const50 bool FilteredPlacesModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
51 {
52     const QModelIndex index = m_placesModel->index(sourceRow, 0, sourceParent);
53 
54     return !m_placesModel->isHidden(index) && !m_placesModel->data(index, KFilePlacesModel::FixedDeviceRole).toBool();
55 }
56 
lessThan(const QModelIndex & left,const QModelIndex & right) const57 bool FilteredPlacesModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
58 {
59     bool lDevice = m_placesModel->isDevice(left);
60     bool rDevice = m_placesModel->isDevice(right);
61 
62     if (lDevice && !rDevice) {
63         return false;
64     } else if (!lDevice && rDevice) {
65         return true;
66     }
67 
68     return (left.row() < right.row());
69 }
70 
RunCommandModel(QObject * parent)71 RunCommandModel::RunCommandModel(QObject *parent)
72     : AbstractModel(parent)
73 {
74 }
75 
~RunCommandModel()76 RunCommandModel::~RunCommandModel()
77 {
78 }
79 
description() const80 QString RunCommandModel::description() const
81 {
82     return QString();
83 }
84 
data(const QModelIndex & index,int role) const85 QVariant RunCommandModel::data(const QModelIndex &index, int role) const
86 {
87     if (!index.isValid()) {
88         return QVariant();
89     }
90 
91     if (role == Qt::DisplayRole) {
92         return i18n("Show KRunner");
93     } else if (role == Qt::DecorationRole) {
94         return QIcon::fromTheme(QStringLiteral("plasma-search"));
95     } else if (role == Kicker::DescriptionRole) {
96         return i18n("Search, calculate, or run a command");
97     } else if (role == Kicker::GroupRole) {
98         return i18n("Applications");
99     }
100 
101     return QVariant();
102 }
103 
rowCount(const QModelIndex & parent) const104 int RunCommandModel::rowCount(const QModelIndex &parent) const
105 {
106     return parent.isValid() ? 0 : (KAuthorized::authorize(QStringLiteral("run_command")) ? 1 : 0);
107 }
108 
trigger(int row,const QString & actionId,const QVariant & argument)109 Q_INVOKABLE bool RunCommandModel::trigger(int row, const QString &actionId, const QVariant &argument)
110 {
111     Q_UNUSED(actionId)
112     Q_UNUSED(argument)
113 
114     if (row == 0 && KAuthorized::authorize(QStringLiteral("run_command"))) {
115         org::kde::krunner::App krunner(QStringLiteral("org.kde.krunner"), QStringLiteral("/App"), QDBusConnection::sessionBus());
116         krunner.display();
117 
118         return true;
119     }
120 
121     return false;
122 }
123 
ComputerModel(QObject * parent)124 ComputerModel::ComputerModel(QObject *parent)
125     : ForwardingModel(parent)
126     , m_concatProxy(new KConcatenateRowsProxyModel(this))
127     , m_runCommandModel(new RunCommandModel(this))
128     , m_systemAppsModel(new SimpleFavoritesModel(this))
129     , m_filteredPlacesModel(new FilteredPlacesModel(this))
130     , m_appNameFormat(AppEntry::NameOnly)
131     , m_appletInterface(nullptr)
132 {
133     connect(m_systemAppsModel, &SimpleFavoritesModel::favoritesChanged, this, &ComputerModel::systemApplicationsChanged);
134     m_systemAppsModel->setFavorites(QStringList() << QStringLiteral("systemsettings.desktop"));
135 
136     m_concatProxy->addSourceModel(m_runCommandModel);
137     m_concatProxy->addSourceModel(m_systemAppsModel);
138     m_concatProxy->addSourceModel(m_filteredPlacesModel);
139 
140     setSourceModel(m_concatProxy);
141 }
142 
~ComputerModel()143 ComputerModel::~ComputerModel()
144 {
145 }
146 
description() const147 QString ComputerModel::description() const
148 {
149     return i18n("Computer");
150 }
151 
appNameFormat() const152 int ComputerModel::appNameFormat() const
153 {
154     return m_appNameFormat;
155 }
156 
setAppNameFormat(int format)157 void ComputerModel::setAppNameFormat(int format)
158 {
159     if (m_appNameFormat != (AppEntry::NameFormat)format) {
160         m_appNameFormat = (AppEntry::NameFormat)format;
161 
162         m_systemAppsModel->refresh();
163 
164         emit appNameFormatChanged();
165     }
166 }
167 
appletInterface() const168 QObject *ComputerModel::appletInterface() const
169 {
170     return m_appletInterface;
171 }
172 
setAppletInterface(QObject * appletInterface)173 void ComputerModel::setAppletInterface(QObject *appletInterface)
174 {
175     if (m_appletInterface != appletInterface) {
176         m_appletInterface = appletInterface;
177 
178         emit appletInterfaceChanged();
179     }
180 }
181 
systemApplications() const182 QStringList ComputerModel::systemApplications() const
183 {
184     return m_systemAppsModel->favorites();
185 }
186 
setSystemApplications(const QStringList & apps)187 void ComputerModel::setSystemApplications(const QStringList &apps)
188 {
189     m_systemAppsModel->setFavorites(apps);
190 }
191 
data(const QModelIndex & index,int role) const192 QVariant ComputerModel::data(const QModelIndex &index, int role) const
193 {
194     if (!index.isValid()) {
195         return QVariant();
196     }
197 
198     const QModelIndex sourceIndex = m_concatProxy->mapToSource(m_concatProxy->index(index.row(), index.column()));
199 
200     bool isPlace = (sourceIndex.model() == m_filteredPlacesModel);
201 
202     if (isPlace) {
203         if (role == Kicker::DescriptionRole) {
204             if (m_filteredPlacesModel->isDevice(sourceIndex)) {
205                 Solid::Device device = m_filteredPlacesModel->deviceForIndex(sourceIndex);
206                 Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
207 
208                 if (access) {
209                     return access->filePath();
210                 } else {
211                     return QString();
212                 }
213             }
214         } else if (role == Kicker::FavoriteIdRole) {
215             if (!m_filteredPlacesModel->isDevice(sourceIndex)) {
216                 return m_filteredPlacesModel->url(sourceIndex);
217             }
218         } else if (role == Kicker::UrlRole) {
219             return m_filteredPlacesModel->url(sourceIndex);
220         } else if (role == Kicker::GroupRole) {
221             return sourceIndex.data(KFilePlacesModel::GroupRole).toString();
222         } else if (role == Qt::DisplayRole || role == Qt::DecorationRole) {
223             return sourceIndex.data(role);
224         }
225     } else if (role == Kicker::GroupRole) {
226         return i18n("Applications");
227     } else {
228         return sourceIndex.data(role);
229     }
230 
231     return QVariant();
232 }
233 
trigger(int row,const QString & actionId,const QVariant & argument)234 bool ComputerModel::trigger(int row, const QString &actionId, const QVariant &argument)
235 {
236     const QModelIndex sourceIndex = m_concatProxy->mapToSource(m_concatProxy->index(row, 0));
237 
238     if (sourceIndex.model() == m_filteredPlacesModel) {
239         const QUrl &url = m_filteredPlacesModel->url(sourceIndex);
240 
241         if (url.isValid()) {
242             new KRun(url, nullptr);
243 
244             return true;
245         }
246 
247         Solid::Device device = m_filteredPlacesModel->deviceForIndex(sourceIndex);
248         Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
249 
250         if (access && !access->isAccessible()) {
251             connect(access, &Solid::StorageAccess::setupDone, this, &ComputerModel::onSetupDone);
252             access->setup();
253 
254             return true;
255         }
256     } else {
257         AbstractModel *model = nullptr;
258 
259         if (sourceIndex.model() == m_systemAppsModel) {
260             model = m_systemAppsModel;
261         } else {
262             model = m_runCommandModel;
263         }
264 
265         return model->trigger(sourceIndex.row(), actionId, argument);
266     }
267 
268     return false;
269 }
270 
onSetupDone(Solid::ErrorType error,QVariant errorData,const QString & udi)271 void ComputerModel::onSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi)
272 {
273     Q_UNUSED(errorData);
274 
275     if (error != Solid::NoError) {
276         return;
277     }
278 
279     Solid::Device device(udi);
280     Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
281 
282     Q_ASSERT(access);
283 
284     new KRun(QUrl::fromLocalFile(access->filePath()), nullptr);
285 }
286