You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

510 lines
14 KiB

/*
* Copyright (C) 2007 by Ivan Cukic <ivan.cukic+kde@gmail.com>
* Copyright (C) 2009 by Ana Cecília Martins <anaceciliamb@gmail.com>
* Copyright 2013 by Sebastian Kügler <sebas@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library/Lesser General Public License
* version 2, or (at your option) any later version, as published by the
* Free Software Foundation
*
* 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 Library/Lesser 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.
*/
#include "widgetexplorer.h"
#include <QQmlEngine>
#include <QQmlContext>
#include <QQmlExpression>
#include <QQmlProperty>
#include <KAuthorized>
#include <klocalizedstring.h>
#include <KNewStuff3/KNS3/DownloadDialog>
#include <KWindowSystem>
#include <Plasma/Applet>
#include <Plasma/Corona>
#include <Plasma/Containment>
#include <Plasma/PluginLoader>
#include <qstandardpaths.h>
#include <KActivities/Consumer>
#include <KPackage/Package>
#include <KPackage/PackageStructure>
#include <KPackage/PackageLoader>
#include "kcategorizeditemsviewmodels_p.h"
#include "plasmaappletitemmodel_p.h"
#include "openwidgetassistant_p.h"
#include "config-workspace.h"
using namespace KActivities;
using namespace KCategorizedItemsViewModels;
using namespace Plasma;
WidgetAction::WidgetAction(QObject *parent)
: QAction(parent)
{
}
WidgetAction::WidgetAction(const QIcon &icon, const QString &text, QObject *parent)
: QAction(icon, text, parent)
{
}
class WidgetExplorerPrivate
{
public:
WidgetExplorerPrivate(WidgetExplorer *w)
: q(w),
containment(0),
itemModel(w),
filterModel(w),
activitiesConsumer(new KActivities::Consumer())
{
QObject::connect(activitiesConsumer.data(), &Consumer::currentActivityChanged, q, [this] {
initRunningApplets();
});
}
void initFilters();
void initRunningApplets();
void containmentDestroyed();
void addContainment(Containment *containment);
/**
* Tracks a new running applet
*/
void appletAdded(Plasma::Applet *applet);
/**
* A running applet is no more
*/
void appletRemoved(Plasma::Applet *applet);
WidgetExplorer *q;
QString application;
Plasma::Containment *containment;
QHash<QString, int> runningApplets; // applet name => count
//extra hash so we can look up the names of deleted applets
QHash<Plasma::Applet *,QString> appletNames;
QPointer<Plasma::OpenWidgetAssistant> openAssistant;
KPackage::Package *package;
PlasmaAppletItemModel itemModel;
KCategorizedItemsViewModels::DefaultFilterModel filterModel;
bool showSpecialFilters = true;
DefaultItemFilterProxyModel filterItemModel;
QPointer<KNS3::DownloadDialog> newStuffDialog;
QScopedPointer<KActivities::Consumer> activitiesConsumer;
};
void WidgetExplorerPrivate::initFilters()
{
filterModel.clear();
filterModel.addFilter(i18n("All Widgets"),
KCategorizedItemsViewModels::Filter(), QIcon::fromTheme(QStringLiteral("plasma")));
if (showSpecialFilters) {
// Filters: Special
filterModel.addFilter(i18n("Running"),
KCategorizedItemsViewModels::Filter(QStringLiteral("running"), true),
QIcon::fromTheme(QStringLiteral("dialog-ok")));
filterModel.addFilter(i18n("Uninstallable"),
KCategorizedItemsViewModels::Filter(QStringLiteral("local"), true),
QIcon::fromTheme(QStringLiteral("list-remove")));
filterModel.addSeparator(i18n("Categories:"));
}
typedef QPair<QString, QString> catPair;
QMap<QString, catPair > categories;
QSet<QString> existingCategories = itemModel.categories();
//foreach (const QString &category, Plasma::Applet::listCategories(application)) {
QStringList cats;
const QList<KPluginInfo> list = PluginLoader::self()->listAppletInfo(QString());
for (auto& info : list) {
if (!info.isValid()) {
continue;
}
if (info.property(QStringLiteral("NoDisplay")).toBool() || info.category() == QLatin1String("Containments") ||
info.category().isEmpty()) {
// we don't want to show the hidden category
continue;
}
const QString c = info.category();
if (-1 == cats.indexOf(c)) {
cats << c;
}
}
qWarning() << "TODO: port listCategories()";
foreach (const QString &category, cats) {
const QString lowerCaseCat = category.toLower();
if (existingCategories.contains(lowerCaseCat)) {
const QString trans = i18nd("libplasma5", category.toLocal8Bit());
categories.insert(trans.toLower(), qMakePair(trans, lowerCaseCat));
}
}
foreach (const catPair &category, categories) {
filterModel.addFilter(category.first,
KCategorizedItemsViewModels::Filter(QStringLiteral("category"), category.second));
}
}
void WidgetExplorer::classBegin()
{
}
void WidgetExplorer::componentComplete()
{
d->itemModel.setStartupCompleted(true);
setApplication();
d->initRunningApplets();
}
QObject *WidgetExplorer::widgetsModel() const
{
return &d->filterItemModel;
}
QObject *WidgetExplorer::filterModel() const
{
return &d->filterModel;
}
bool WidgetExplorer::showSpecialFilters() const
{
return d->showSpecialFilters;
}
void WidgetExplorer::setShowSpecialFilters(bool show)
{
if (d->showSpecialFilters != show) {
d->showSpecialFilters = show;
d->initFilters();
emit showSpecialFiltersChanged();
}
}
QList <QObject *> WidgetExplorer::widgetsMenuActions()
{
QList <QObject *> actionList;
WidgetAction *action = nullptr;
if (KAuthorized::authorize(QStringLiteral("ghns"))) {
action = new WidgetAction(QIcon::fromTheme(QStringLiteral("applications-internet")),
i18n("Download New Plasma Widgets"), this);
connect(action, &QAction::triggered, this, &WidgetExplorer::downloadWidgets);
actionList << action;
}
action = new WidgetAction(this);
action->setSeparator(true);
actionList << action;
action = new WidgetAction(QIcon::fromTheme(QStringLiteral("package-x-generic")),
i18n("Install Widget From Local File..."), this);
QObject::connect(action, &QAction::triggered, this, &WidgetExplorer::openWidgetFile);
actionList << action;
return actionList;
}
QList<QObject *> WidgetExplorer::extraActions() const
{
QList<QObject *> actionList;
// foreach (QAction *action, actions()) { // FIXME: where did actions() come from?
// actionList << action;
// }
qWarning() << "extraactions needs reimplementation";
return actionList;
}
void WidgetExplorerPrivate::initRunningApplets()
{
//get applets from corona, count them, send results to model
if (!containment) {
return;
}
Plasma::Corona *c = containment->corona();
//we've tried our best to get a corona
//we don't want just one containment, we want them all
if (!c) {
qWarning() << "WidgetExplorer failed to find corona";
return;
}
appletNames.clear();
runningApplets.clear();
const QList<Containment*> containments = c->containments();
for (Containment *containment : containments) {
if (containment->containmentType() == Plasma::Types::DesktopContainment
&& containment->activity() != activitiesConsumer->currentActivity()) {
continue;
}
addContainment(containment);
}
//qDebug() << runningApplets;
itemModel.setRunningApplets(runningApplets);
}
void WidgetExplorerPrivate::addContainment(Containment *containment)
{
QObject::connect(containment, SIGNAL(appletAdded(Plasma::Applet*)), q, SLOT(appletAdded(Plasma::Applet*)));
QObject::connect(containment, SIGNAL(appletRemoved(Plasma::Applet*)), q, SLOT(appletRemoved(Plasma::Applet*)));
foreach (Applet *applet, containment->applets()) {
if (applet->pluginMetaData().isValid()) {
Containment *childContainment = applet->property("containment").value<Containment*>();
if (childContainment) {
addContainment(childContainment);
}
runningApplets[applet->pluginMetaData().pluginId()]++;
} else {
qDebug() << "Invalid plugin metadata. :(";
}
}
}
void WidgetExplorerPrivate::containmentDestroyed()
{
containment = 0;
}
void WidgetExplorerPrivate::appletAdded(Plasma::Applet *applet)
{
if (!applet->pluginMetaData().isValid()) {
return;
}
QString name = applet->pluginMetaData().pluginId();
runningApplets[name]++;
appletNames.insert(applet, name);
itemModel.setRunningApplets(name, runningApplets[name]);
}
void WidgetExplorerPrivate::appletRemoved(Plasma::Applet *applet)
{
QString name = appletNames.take(applet);
int count = 0;
if (runningApplets.contains(name)) {
count = runningApplets[name] - 1;
if (count < 1) {
runningApplets.remove(name);
} else {
runningApplets[name] = count;
}
}
itemModel.setRunningApplets(name, count);
}
//WidgetExplorer
WidgetExplorer::WidgetExplorer(QObject *parent)
: QObject(parent),
d(new WidgetExplorerPrivate(this))
{
d->filterItemModel.setSortCaseSensitivity(Qt::CaseInsensitive);
d->filterItemModel.setDynamicSortFilter(true);
d->filterItemModel.setSourceModel(&d->itemModel);
d->filterItemModel.sort(0);
}
WidgetExplorer::~WidgetExplorer()
{
delete d;
}
void WidgetExplorer::setApplication(const QString &app)
{
if (d->application == app && !app.isEmpty()) {
return;
}
d->application = app;
d->itemModel.setApplication(app);
d->initFilters();
d->itemModel.setRunningApplets(d->runningApplets);
emit applicationChanged();
}
QString WidgetExplorer::application()
{
return d->application;
}
QStringList WidgetExplorer::provides() const
{
return d->itemModel.provides();
}
void WidgetExplorer::setProvides(const QStringList &provides)
{
if (d->itemModel.provides() == provides) {
return;
}
d->itemModel.setProvides(provides);
emit providesChanged();
}
void WidgetExplorer::setContainment(Plasma::Containment *containment)
{
if (d->containment != containment) {
if (d->containment) {
d->containment->disconnect(this);
}
d->containment = containment;
if (d->containment) {
connect(d->containment, SIGNAL(destroyed(QObject*)), this, SLOT(containmentDestroyed()));
connect(d->containment, &Applet::immutabilityChanged, this, &WidgetExplorer::immutabilityChanged);
}
d->initRunningApplets();
emit containmentChanged();
}
}
Containment *WidgetExplorer::containment() const
{
return d->containment;
}
Plasma::Corona *WidgetExplorer::corona() const
{
if (d->containment) {
return d->containment->corona();
}
return 0;
}
void WidgetExplorer::addApplet(const QString &pluginName)
{
const QString p = PLASMA_RELATIVE_DATA_INSTALL_DIR "/plasmoids/"+pluginName;
qWarning() << "--------> load applet: " << pluginName << " relpath: " << p;
QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, p, QStandardPaths::LocateDirectory);
qDebug() << " .. pathes: " << dirs;
if (!dirs.count()) {
qWarning() << "Failed to find plasmoid path for " << pluginName;
return;
}
if (d->containment) {
d->containment->createApplet(dirs.first());
}
}
void WidgetExplorer::immutabilityChanged(Plasma::Types::ImmutabilityType type)
{
if (type != Plasma::Types::Mutable) {
emit shouldClose();
}
}
void WidgetExplorer::downloadWidgets()
{
if (!d->newStuffDialog) {
d->newStuffDialog = new KNS3::DownloadDialog(QLatin1String("plasmoids.knsrc"));
d->newStuffDialog.data()->setWindowTitle(i18n("Download New Plasma Widgets"));
d->newStuffDialog.data()->setAttribute(Qt::WA_DeleteOnClose);
}
d->newStuffDialog.data()->show();
emit shouldClose();
}
void WidgetExplorer::openWidgetFile()
{
Plasma::OpenWidgetAssistant *assistant = d->openAssistant.data();
if (!assistant) {
assistant = new Plasma::OpenWidgetAssistant(0);
d->openAssistant = assistant;
}
KWindowSystem::setOnDesktop(assistant->winId(), KWindowSystem::currentDesktop());
assistant->setAttribute(Qt::WA_DeleteOnClose, true);
assistant->show();
assistant->raise();
assistant->setFocus();
emit shouldClose();
}
void WidgetExplorer::uninstall(const QString &pluginName)
{
static const QString packageRoot = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + PLASMA_RELATIVE_DATA_INSTALL_DIR "/plasmoids/";
KPackage::PackageStructure *structure = KPackage::PackageLoader::self()->loadPackageStructure(QStringLiteral("Plasma/Applet"));
KPackage::Package pkg(structure);
pkg.uninstall(pluginName, packageRoot);
//FIXME: moreefficient way rather a linear scan?
for (int i = 0; i < d->itemModel.rowCount(); ++i) {
QStandardItem *item = d->itemModel.item(i);
if (item->data(PlasmaAppletItemModel::PluginNameRole).toString() == pluginName) {
d->itemModel.takeRow(i);
break;
}
}
// now remove all instances of that applet
if (corona()) {
const auto &containments = corona()->containments();
foreach (Containment *c, containments) {
const auto &applets = c->applets();
foreach (Applet *applet, applets) {
const auto &appletInfo = applet->pluginMetaData();
if (appletInfo.isValid() && appletInfo.pluginId() == pluginName) {
applet->destroy();
}
}
}
}
}
#include "moc_widgetexplorer.cpp"