appentry.cpp 9.76 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
67
68
69
70
71
        m_service = defaultAppByName(url.host());
        m_id = id;
    } else {
        m_service = KService::serviceByStorageId(id);
    }

    if (m_service) {
72
        init((NameFormat)owner->rootModel()->property("appNameFormat").toInt());
73
74
75
76
77
78
    }
}

void AppEntry::init(NameFormat nameFormat)
{
    m_name = nameFromService(m_service, nameFormat);
79
80
81
82
83
84

    if (nameFormat == GenericNameOnly) {
        m_description = nameFromService(m_service, NameOnly);
    } else {
        m_description = nameFromService(m_service, GenericNameOnly);
    }
85
86
87
88
89
90
91
92
93
}

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

QIcon AppEntry::icon() const
{
94
    if (m_icon.isNull()) {
Alexander Lohnau's avatar
Alexander Lohnau committed
95
96
97
98
99
        if (QFileInfo::exists(m_service->icon())) {
            m_icon = QIcon(m_service->icon());
        } else {
            m_icon = QIcon::fromTheme(m_service->icon(), QIcon::fromTheme(QStringLiteral("unknown")));
        }
100
    }
101
102
103
104
105
106
107
108
    return m_icon;
}

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

Eike Hein's avatar
Eike Hein committed
109
110
QString AppEntry::description() const
{
111
    return m_description;
Eike Hein's avatar
Eike Hein committed
112
113
}

114
115
116
117
118
119
120
121
122
123
124
125
126
127
KService::Ptr AppEntry::service() const
{
    return m_service;
}

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

    return m_service->storageId();
}

128
129
130
131
132
QString AppEntry::menuId() const
{
    return m_service->menuId();
}

133
134
QUrl AppEntry::url() const
{
135
    return QUrl::fromLocalFile(Kicker::resolvedServiceEntryPath(m_service));
136
137
138
139
140
141
142
143
144
145
146
}

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

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

147
148
    actionList << Kicker::jumpListActions(m_service);
    if (!actionList.isEmpty()) {
149
150
151
        actionList << Kicker::createSeparatorActionItem();
    }

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

154
155
156
157
    bool systemImmutable = false;
    if (appletInterface) {
        systemImmutable = (appletInterface->property("immutability").toInt() == Plasma::Types::SystemImmutable);
    }
158

159
    const QVariantList &addLauncherActions = Kicker::createAddLauncherActionList(appletInterface, m_service);
160
    if (!systemImmutable && !addLauncherActions.isEmpty()) {
161
162
        actionList << addLauncherActions
                   << Kicker::createSeparatorActionItem();
163
164
165
166
167
    }

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

170
171
172
173
174
175
    // Don't allow adding launchers, editing, hiding, or uninstalling applications
    // when system is immutable.
    if (systemImmutable) {
        return actionList;
    }

176
    if (m_service->isApplication()) {
177
178
179
        actionList << Kicker::createSeparatorActionItem();
        actionList << Kicker::editApplicationAction(m_service);
        actionList << Kicker::appstreamActions(m_service);
180
    }
181

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

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

188
            if (!hiddenApps.contains(m_service->menuId())) {
189
                QVariantMap hideAction = Kicker::createActionItem(i18n("Hide Application"), QStringLiteral("view-hidden"), QStringLiteral("hideApplication"));
190
191
                actionList << hideAction;
            }
192
193
194
195
196
197
198
199
        }
    }

    return actionList;
}

bool AppEntry::run(const QString& actionId, const QVariant &argument)
{
200
201
202
203
    if (!m_service->isValid()) {
        return false;
    }

204
205
206
207
208
209
210
211
    if (actionId.isEmpty()) {
        quint32 timeStamp = 0;

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

213
214
215
216
217
        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();
218

219
        KActivities::ResourceInstance::notifyAccessed(QUrl(QStringLiteral("applications:") + m_service->storageId()),
220
                QStringLiteral("org.kde.plasma.kicker"));
221
222

        return true;
Eike Hein's avatar
Eike Hein committed
223
224
225
226
    }

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

227
228
    if (Kicker::handleAddLauncherAction(actionId, appletInterface, m_service)) {
        return true;
229
230
231
    } else if (Kicker::handleEditApplicationAction(actionId, m_service)) {
        return true;
    } else if (Kicker::handleAppstreamActions(actionId, argument)) {
232
        return true;
233
    } else if (actionId == QLatin1String("_kicker_jumpListAction")) {
234
        return KRun::run(argument.toString(), {}, nullptr, m_service->name(), m_service->icon());
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
    }

    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"));
268
        } else if (browser.startsWith(QLatin1Char('!'))) {
269
            browser.remove(0, 1);
270
271
272
273
274
275
276
277
278
        }

        return KService::serviceByStorageId(browser);
    }

    return KService::Ptr();
}

AppGroupEntry::AppGroupEntry(AppsModel *parentModel, KServiceGroup::Ptr group,
279
    bool paginate, int pageSize, bool flat, bool sorted, bool separators, int appNameFormat) : AbstractGroupEntry(parentModel),
280
    m_group(group)
281
{
282
283
    AppsModel* model = new AppsModel(group->entryPath(), paginate, pageSize, flat,
        sorted, separators, parentModel);
284
285
286
    model->setAppNameFormat(appNameFormat);
    m_childModel = model;

287
    QObject::connect(parentModel, &AppsModel::cleared, model, &AppsModel::deleteLater);
288
289
290
291
292
293
294
295
296
297
298
299

    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
{
300
    if (m_icon.isNull()) {
301
        m_icon = QIcon::fromTheme(m_group->icon(), QIcon::fromTheme(QStringLiteral("unknown")));
302
    }
303
304
305
306
307
    return m_icon;
}

QString AppGroupEntry::name() const
{
308
    return m_group->caption();
309
310
311
312
313
314
315
316
317
318
}

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

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