appentry.cpp 10.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/***************************************************************************
 *   Copyright (C) 201 by Eike Hein <hein@kde.org>                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 ***************************************************************************/

20
#include <config-workspace.h>
21
22
23
24
25
26
27
28
29
30
#include "appentry.h"
#include "actionlist.h"
#include "appsmodel.h"
#include "containmentinterface.h"

#include <config-X11.h>

#include <QProcess>
#include <QQmlPropertyMap>
#include <QStandardPaths>
Alexander Lohnau's avatar
Alexander Lohnau committed
31
#include <QFileInfo>
32
33
34
35
36
37
38
#if HAVE_X11
#include <QX11Info>
#endif

#include <KActivities/ResourceInstance>
#include <KConfigGroup>
#include <KJob>
39
#include <KIO/ApplicationLauncherJob>
40
41
#include <KLocalizedString>
#include <KMimeTypeTrader>
42
#include <KNotificationJobUiDelegate>
43
44
45
46
47
48
#include <KRun>
#include <KSycoca>
#include <KShell>
#include <KSharedConfig>
#include <KStartupInfo>

49
#include <Plasma/Plasma>
David Edmundson's avatar
David Edmundson committed
50

51
52
53
54
55
56
57
58
59
60
61
62
63
AppEntry::AppEntry(AbstractModel *owner, KService::Ptr service, NameFormat nameFormat)
: AbstractEntry(owner)
, m_service(service)
{
    if (m_service) {
        init(nameFormat);
    }
}

AppEntry::AppEntry(AbstractModel *owner, const QString &id) : AbstractEntry(owner)
{
    const QUrl url(id);

64
    if (url.scheme() == QLatin1String("preferred")) {
65
66
        m_service = defaultAppByName(url.host());
        m_id = id;
67
68
69
70
71
72
73
74
75
        m_con = QObject::connect(KSycoca::self(), QOverload<>::of(&KSycoca::databaseChanged), owner, [this, owner, id](){
            KSharedConfig::openConfig()->reparseConfiguration();
            m_service = defaultAppByName(QUrl(id).host());
            if (m_service) {
                init((NameFormat)owner->rootModel()->property("appNameFormat").toInt());
                m_icon = QIcon();
                Q_EMIT owner->layoutChanged();
            }
        });
76
77
78
79
80
    } else {
        m_service = KService::serviceByStorageId(id);
    }

    if (m_service) {
81
        init((NameFormat)owner->rootModel()->property("appNameFormat").toInt());
82
83
84
85
86
87
    }
}

void AppEntry::init(NameFormat nameFormat)
{
    m_name = nameFromService(m_service, nameFormat);
88
89
90
91
92
93

    if (nameFormat == GenericNameOnly) {
        m_description = nameFromService(m_service, NameOnly);
    } else {
        m_description = nameFromService(m_service, GenericNameOnly);
    }
94
95
96
97
98
99
100
101
102
}

bool AppEntry::isValid() const
{
    return m_service;
}

QIcon AppEntry::icon() const
{
103
    if (m_icon.isNull()) {
Alexander Lohnau's avatar
Alexander Lohnau committed
104
105
106
107
108
        if (QFileInfo::exists(m_service->icon())) {
            m_icon = QIcon(m_service->icon());
        } else {
            m_icon = QIcon::fromTheme(m_service->icon(), QIcon::fromTheme(QStringLiteral("unknown")));
        }
109
    }
110
111
112
113
114
115
116
117
    return m_icon;
}

QString AppEntry::name() const
{
    return m_name;
}

Eike Hein's avatar
Eike Hein committed
118
119
QString AppEntry::description() const
{
120
    return m_description;
Eike Hein's avatar
Eike Hein committed
121
122
}

123
124
125
126
127
128
129
130
131
132
133
134
135
136
KService::Ptr AppEntry::service() const
{
    return m_service;
}

QString AppEntry::id() const
{
    if (!m_id.isEmpty()) {
        return m_id;
    }

    return m_service->storageId();
}

137
138
139
140
141
QString AppEntry::menuId() const
{
    return m_service->menuId();
}

142
143
QUrl AppEntry::url() const
{
144
    return QUrl::fromLocalFile(Kicker::resolvedServiceEntryPath(m_service));
145
146
147
148
149
150
151
152
153
154
155
}

bool AppEntry::hasActions() const
{
    return true;
}

QVariantList AppEntry::actions() const
{
    QVariantList actionList;

156
157
    actionList << Kicker::jumpListActions(m_service);
    if (!actionList.isEmpty()) {
158
159
160
        actionList << Kicker::createSeparatorActionItem();
    }

Eike Hein's avatar
Eike Hein committed
161
162
    QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value<QObject *>();

163
164
165
166
    bool systemImmutable = false;
    if (appletInterface) {
        systemImmutable = (appletInterface->property("immutability").toInt() == Plasma::Types::SystemImmutable);
    }
167

168
    const QVariantList &addLauncherActions = Kicker::createAddLauncherActionList(appletInterface, m_service);
169
    if (!systemImmutable && !addLauncherActions.isEmpty()) {
170
171
        actionList << addLauncherActions
                   << Kicker::createSeparatorActionItem();
172
173
174
175
176
    }

    const QVariantList &recentDocuments = Kicker::recentDocumentActions(m_service);
    if (!recentDocuments.isEmpty()) {
        actionList << recentDocuments << Kicker::createSeparatorActionItem();
177
178
    }

179
180
181
182
183
184
    // Don't allow adding launchers, editing, hiding, or uninstalling applications
    // when system is immutable.
    if (systemImmutable) {
        return actionList;
    }

185
    if (m_service->isApplication()) {
186
187
188
        actionList << Kicker::createSeparatorActionItem();
        actionList << Kicker::editApplicationAction(m_service);
        actionList << Kicker::appstreamActions(m_service);
189
    }
190

191
192
    if (appletInterface) {
        QQmlPropertyMap *appletConfig = qobject_cast<QQmlPropertyMap *>(appletInterface->property("configuration").value<QObject *>());
Eike Hein's avatar
Eike Hein committed
193

194
195
        if (appletConfig && appletConfig->contains(QLatin1String("hiddenApplications")) && qobject_cast<AppsModel *>(m_owner)) {
            const QStringList &hiddenApps = appletConfig->value(QLatin1String("hiddenApplications")).toStringList();
196

197
            if (!hiddenApps.contains(m_service->menuId())) {
198
                QVariantMap hideAction = Kicker::createActionItem(i18n("Hide Application"), QStringLiteral("view-hidden"), QStringLiteral("hideApplication"));
199
200
                actionList << hideAction;
            }
201
202
203
204
205
206
207
208
        }
    }

    return actionList;
}

bool AppEntry::run(const QString& actionId, const QVariant &argument)
{
209
210
211
212
    if (!m_service->isValid()) {
        return false;
    }

213
214
215
216
217
218
219
220
    if (actionId.isEmpty()) {
        quint32 timeStamp = 0;

#if HAVE_X11
        if (QX11Info::isPlatformX11()) {
            timeStamp = QX11Info::appUserTime();
        }
#endif
221

222
223
224
225
226
        auto *job = new KIO::ApplicationLauncherJob(m_service);
        job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
        job->setRunFlags(KIO::ApplicationLauncherJob::DeleteTemporaryFiles);
        job->setStartupId(KStartupInfo::createNewStartupIdForTimestamp(timeStamp));
        job->start();
227

228
        KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + m_service->storageId()),
229
                QStringLiteral("org.kde.plasma.kicker"));
230
231

        return true;
Eike Hein's avatar
Eike Hein committed
232
233
234
235
    }

    QObject *appletInterface = m_owner->rootModel()->property("appletInterface").value<QObject *>();

236
237
    if (Kicker::handleAddLauncherAction(actionId, appletInterface, m_service)) {
        return true;
238
239
240
    } else if (Kicker::handleEditApplicationAction(actionId, m_service)) {
        return true;
    } else if (Kicker::handleAppstreamActions(actionId, argument)) {
241
        return true;
242
    } else if (actionId == QLatin1String("_kicker_jumpListAction")) {
243
        return KRun::run(argument.toString(), {}, nullptr, m_service->name(), m_service->icon());
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
    }

    return Kicker::handleRecentDocumentAction(m_service, actionId, argument);
}

QString AppEntry::nameFromService(const KService::Ptr service, NameFormat nameFormat)
{
    const QString &name = service->name();
    QString genericName = service->genericName();

    if (genericName.isEmpty()) {
        genericName = service->comment();
    }

    if (nameFormat == NameOnly || genericName.isEmpty() || name == genericName) {
        return name;
    } else if (nameFormat == GenericNameOnly) {
        return genericName;
    } else if (nameFormat == NameAndGenericName) {
        return i18nc("App name (Generic name)", "%1 (%2)", name, genericName);
    } else {
        return i18nc("Generic name (App name)", "%1 (%2)", genericName, name);
    }
}

KService::Ptr AppEntry::defaultAppByName(const QString& name)
{
    if (name == QLatin1String("browser")) {
        KConfigGroup config(KSharedConfig::openConfig(), "General");
        QString browser = config.readPathEntry("BrowserApplication", QString());

        if (browser.isEmpty()) {
            return KMimeTypeTrader::self()->preferredService(QLatin1String("text/html"));
277
        } else if (browser.startsWith(QLatin1Char('!'))) {
278
            browser.remove(0, 1);
279
280
281
282
283
284
285
286
        }

        return KService::serviceByStorageId(browser);
    }

    return KService::Ptr();
}

287
288
289
290
291
292
AppEntry::~AppEntry() {
    if (m_con){
        QObject::disconnect(m_con);
    }
}

293
AppGroupEntry::AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group,
294
    bool paginate, int pageSize, bool flat, bool sorted, bool separators, int appNameFormat) : AbstractGroupEntry(parentModel),
295
    m_group(group)
296
{
297
298
    AppsModel* model = new AppsModel(group->entryPath(), paginate, pageSize, flat,
        sorted, separators, parentModel);
299
300
301
    model->setAppNameFormat(appNameFormat);
    m_childModel = model;

302
    QObject::connect(parentModel, &AppsModel::cleared, model, &AppsModel::deleteLater);
303
304
305
306
307
308
309
310
311
312
313
314

    QObject::connect(model, &AppsModel::countChanged,
        [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } }
    );

    QObject::connect(model, &AppsModel::hiddenEntriesChanged,
        [parentModel, this] { if (parentModel) { parentModel->entryChanged(this); } }
    );
}

QIcon AppGroupEntry::icon() const
{
315
    if (m_icon.isNull()) {
316
        m_icon = QIcon::fromTheme(m_group->icon(), QIcon::fromTheme(QStringLiteral("unknown")));
317
    }
318
319
320
321
322
    return m_icon;
}

QString AppGroupEntry::name() const
{
323
    return m_group->caption();
324
325
326
327
328
329
330
331
332
333
}

bool AppGroupEntry::hasChildren() const
{
    return m_childModel && m_childModel->count() > 0;
}

AbstractModel *AppGroupEntry::childModel() const {
    return m_childModel;
}