From c5b598f2dddd5ff94b0c94e82b07fd5ca0ae63d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20K=C3=BCgler?= Date: Thu, 21 Mar 2013 03:17:57 +0100 Subject: [PATCH] pristine copies of widgetexplorer.{h,cpp} from kde-workspace/libs/plasmagenericshell/widgetsexplorer --- widgetexplorer/widgetexplorer.cpp | 646 ++++++++++++++++++++++++++++++ widgetexplorer/widgetexplorer.h | 192 +++++++++ 2 files changed, 838 insertions(+) create mode 100644 widgetexplorer/widgetexplorer.cpp create mode 100644 widgetexplorer/widgetexplorer.h diff --git a/widgetexplorer/widgetexplorer.cpp b/widgetexplorer/widgetexplorer.cpp new file mode 100644 index 000000000..9ab008215 --- /dev/null +++ b/widgetexplorer/widgetexplorer.cpp @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2007 by Ivan Cukic + * Copyright (C) 2009 by Ana Cecília Martins + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "kcategorizeditemsviewmodels_p.h" +#include "plasmaappletitemmodel_p.h" +#include "openwidgetassistant_p.h" + +//getting the user local +//KGlobal::dirs()->localkdedir(); +//Compare it to the entryPath of the KPluginInfo +//and see if it can be uninstalled + +using namespace KCategorizedItemsViewModels; + +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) + { + } + + void initFilters(); + void init(Plasma::Location loc); + void initRunningApplets(); + void containmentDestroyed(); + void setLocation(Plasma::Location loc); + void finished(); + + /** + * Tracks a new running applet + */ + void appletAdded(Plasma::Applet *applet); + + /** + * A running applet is no more + */ + void appletRemoved(Plasma::Applet *applet); + + //this orientation is just for convenience, is the location that is important + Qt::Orientation orientation; + Plasma::Location location; + WidgetExplorer *q; + QString application; + Plasma::Containment *containment; + + QHash runningApplets; // applet name => count + //extra hash so we can look up the names of deleted applets + QHash appletNames; + QWeakPointer openAssistant; + Plasma::Package *package; + + PlasmaAppletItemModel itemModel; + KCategorizedItemsViewModels::DefaultFilterModel filterModel; + DefaultItemFilterProxyModel filterItemModel; + + Plasma::DeclarativeWidget *declarativeWidget; + + QGraphicsLinearLayout *mainLayout; +}; + +void WidgetExplorerPrivate::initFilters() +{ + filterModel.addFilter(i18n("All Widgets"), + KCategorizedItemsViewModels::Filter(), KIcon("plasma")); + + // Filters: Special + filterModel.addFilter(i18n("Running"), + KCategorizedItemsViewModels::Filter("running", true), + KIcon("dialog-ok")); + + filterModel.addSeparator(i18n("Categories:")); + + typedef QPair catPair; + QMap categories; + QSet existingCategories = itemModel.categories(); + foreach (const QString &category, Plasma::Applet::listCategories(application)) { + const QString lowerCaseCat = category.toLower(); + if (existingCategories.contains(lowerCaseCat)) { + const QString trans = i18n(category.toLocal8Bit()); + categories.insert(trans.toLower(), qMakePair(trans, lowerCaseCat)); + } + } + + foreach (const catPair &category, categories) { + filterModel.addFilter(category.first, + KCategorizedItemsViewModels::Filter("category", category.second)); + } + +} + +void WidgetExplorerPrivate::init(Plasma::Location loc) +{ + q->setFocusPolicy(Qt::StrongFocus); + + //init widgets + location = loc; + orientation = ((location == Plasma::LeftEdge || location == Plasma::RightEdge)?Qt::Vertical:Qt::Horizontal); + mainLayout = new QGraphicsLinearLayout(Qt::Vertical); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + + //connect + //QObject::connect(filteringWidget, SIGNAL(closeClicked()), q, SIGNAL(closeClicked())); + + initRunningApplets(); + + Plasma::PackageStructure::Ptr structure = Plasma::PackageStructure::load("Plasma/Generic"); + package = new Plasma::Package(QString(), "org.kde.desktop.widgetexplorer", structure); + + declarativeWidget = new Plasma::DeclarativeWidget(q); + declarativeWidget->setInitializationDelayed(true); + declarativeWidget->setQmlPath(package->filePath("mainscript")); + mainLayout->addItem(declarativeWidget); + + if (declarativeWidget->engine()) { + QDeclarativeContext *ctxt = declarativeWidget->engine()->rootContext(); + if (ctxt) { + filterItemModel.setSortCaseSensitivity(Qt::CaseInsensitive); + filterItemModel.setDynamicSortFilter(true); + filterItemModel.setSourceModel(&itemModel); + filterItemModel.sort(0); + ctxt->setContextProperty("widgetExplorer", q); + } + } + + q->setLayout(mainLayout); +} + +void WidgetExplorerPrivate::finished() +{ + if (declarativeWidget->mainComponent()->isError()) { + return; + } + + emit q->widgetsMenuActionsChanged(); + emit q->extraActionsChanged(); + + return; + QObject::connect(declarativeWidget->rootObject(), SIGNAL(addAppletRequested(const QString &)), + q, SLOT(addApplet(const QString &))); + QObject::connect(declarativeWidget->rootObject(), SIGNAL(closeRequested()), + q, SIGNAL(closeClicked())); + + + QList actionList; + foreach (QAction *action, q->actions()) { + actionList << action; + } + declarativeWidget->rootObject()->setProperty("extraActions", QVariant::fromValue(actionList)); +} + +void WidgetExplorerPrivate::setLocation(const Plasma::Location loc) +{ + Qt::Orientation orient; + if (loc == Plasma::LeftEdge || loc == Plasma::RightEdge) { + orient = Qt::Vertical; + } else { + orient = Qt::Horizontal; + } + + if (location == loc) { + return; + } + + location = loc; + + if (orientation == orient) { + return; + } + + emit q->orientationChanged(); +} + +QObject *WidgetExplorer::widgetsModel() const +{ + return &d->filterItemModel; +} + +QObject *WidgetExplorer::filterModel() const +{ + return &d->filterModel; +} + +QList WidgetExplorer::widgetsMenuActions() +{ + QList actionList; + + QSignalMapper *mapper = new QSignalMapper(this); + QObject::connect(mapper, SIGNAL(mapped(QString)), this, SLOT(downloadWidgets(QString))); + + WidgetAction *action = new WidgetAction(KIcon("applications-internet"), + i18n("Download New Plasma Widgets"), this); + QObject::connect(action, SIGNAL(triggered(bool)), mapper, SLOT(map())); + mapper->setMapping(action, QString()); + actionList << action; + + KService::List offers = KServiceTypeTrader::self()->query("Plasma/PackageStructure"); + foreach (const KService::Ptr &service, offers) { + //kDebug() << service->property("X-Plasma-ProvidesWidgetBrowser"); + if (service->property("X-Plasma-ProvidesWidgetBrowser").toBool()) { + WidgetAction *action = new WidgetAction(KIcon("applications-internet"), + i18nc("%1 is a type of widgets, as defined by " + "e.g. some plasma-packagestructure-*.desktop files", + "Download New %1", service->name()), this); + QObject::connect(action, SIGNAL(triggered(bool)), mapper, SLOT(map())); + mapper->setMapping(action, service->property("X-KDE-PluginInfo-Name").toString()); + actionList << action; + } + } + + action = new WidgetAction(this); + action->setSeparator(true); + actionList << action; + + action = new WidgetAction(KIcon("package-x-generic"), + i18n("Install Widget From Local File..."), this); + QObject::connect(action, SIGNAL(triggered(bool)), this, SLOT(openWidgetFile())); + actionList << action; + + return actionList; +} + +QList WidgetExplorer::extraActions() const +{ + QList actionList; + foreach (QAction *action, actions()) { + actionList << action; + } + 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) { + kDebug() << "can't happen"; + return; + } + + appletNames.clear(); + runningApplets.clear(); + QList containments = c->containments(); + foreach (Containment *containment, containments) { + QObject::connect(containment, SIGNAL(appletAdded(Plasma::Applet*,QPointF)), q, SLOT(appletAdded(Plasma::Applet*))); + QObject::connect(containment, SIGNAL(appletRemoved(Plasma::Applet*)), q, SLOT(appletRemoved(Plasma::Applet*))); + + foreach (Applet *applet, containment->applets()) { + runningApplets[applet->pluginName()]++; + } + } + + //kDebug() << runningApplets; + itemModel.setRunningApplets(runningApplets); +} + +void WidgetExplorerPrivate::containmentDestroyed() +{ + containment = 0; +} + +void WidgetExplorerPrivate::appletAdded(Plasma::Applet *applet) +{ + QString name = applet->pluginName(); + + runningApplets[name]++; + appletNames.insert(applet, name); + itemModel.setRunningApplets(name, runningApplets[name]); +} + +void WidgetExplorerPrivate::appletRemoved(Plasma::Applet *applet) +{ + Plasma::Applet *a = (Plasma::Applet *)applet; //don't care if it's valid, just need the address + + QString name = appletNames.take(a); + + 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(Plasma::Location loc, QGraphicsItem *parent) + :QGraphicsWidget(parent), + d(new WidgetExplorerPrivate(this)) +{ + d->init(loc); +} + +WidgetExplorer::WidgetExplorer(QGraphicsItem *parent) + :QGraphicsWidget(parent), + d(new WidgetExplorerPrivate(this)) +{ + d->init(Plasma::BottomEdge); +} + +WidgetExplorer::~WidgetExplorer() +{ + delete d; +} + +void WidgetExplorer::setLocation(Plasma::Location loc) +{ + d->setLocation(loc); + emit(locationChanged(loc)); +} + +WidgetExplorer::Location WidgetExplorer::location() +{ + return (WidgetExplorer::Location)d->location; +} + +Qt::Orientation WidgetExplorer::orientation() const +{ + return d->orientation; +} + +void WidgetExplorer::populateWidgetList(const QString &app) +{ + d->application = app; + d->itemModel.setApplication(app); + d->initFilters(); + + d->itemModel.setRunningApplets(d->runningApplets); + +} + +QString WidgetExplorer::application() +{ + return d->application; +} + +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, SIGNAL(immutabilityChanged(Plasma::ImmutabilityType)), this, SLOT(immutabilityChanged(Plasma::ImmutabilityType))); + + setLocation(containment->location()); + } + + d->initRunningApplets(); + } +} + +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) +{ + d->containment->addApplet(pluginName); +} + +void WidgetExplorer::immutabilityChanged(Plasma::ImmutabilityType type) +{ + if (type != Plasma::Mutable) { + emit closeClicked(); + } +} + +void WidgetExplorer::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) { + // have to treat escape specially, as it makes text() return " " + QGraphicsWidget::keyPressEvent(event); + return; + } + +} + +bool WidgetExplorer::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ActionAdded: + case QEvent::ActionChanged: + case QEvent::ActionRemoved: + if (d->declarativeWidget->rootObject()) { + emit widgetsMenuActionsChanged(); + } + break; + default: + break; + } + + return QGraphicsWidget::event(event); +} + +void WidgetExplorer::focusInEvent(QFocusEvent* event) +{ + Q_UNUSED(event); +} + +void WidgetExplorer::downloadWidgets(const QString &type) +{ + Plasma::PackageStructure *installer = 0; + + if (!type.isEmpty()) { + QString constraint = QString("'%1' == [X-KDE-PluginInfo-Name]").arg(type); + KService::List offers = KServiceTypeTrader::self()->query("Plasma/PackageStructure", + constraint); + if (offers.isEmpty()) { + kDebug() << "could not find requested PackageStructure plugin" << type; + } else { + KService::Ptr service = offers.first(); + QString error; + installer = service->createInstance(topLevelWidget(), + QVariantList(), &error); + if (installer) { + connect(installer, SIGNAL(newWidgetBrowserFinished()), + installer, SLOT(deleteLater())); + } else { + kDebug() << "found, but could not load requested PackageStructure plugin" << type + << "; reported error was" << error; + } + } + } + + emit closeClicked(); + if (installer) { + installer->createNewWidgetBrowser(); + } else { + // we don't need to delete the default Applet::packageStructure as that + // belongs to the applet + Plasma::Applet::packageStructure()->createNewWidgetBrowser(); + /** + for reference in a libplasma2 world, the above line equates to this: + + KNS3::DownloadDialog *knsDialog = m_knsDialog.data(); + if (!knsDialog) { + m_knsDialog = knsDialog = new KNS3::DownloadDialog("plasmoids.knsrc", parent); + connect(knsDialog, SIGNAL(accepted()), this, SIGNAL(newWidgetBrowserFinished())); + } + + knsDialog->show(); + knsDialog->raise(); + */ + } +} + +void WidgetExplorer::openWidgetFile() +{ + emit closeClicked(); + + 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(); +} + +void WidgetExplorer::uninstall(const QString &pluginName) +{ + Plasma::PackageStructure installer; + installer.uninstallPackage(pluginName, + KStandardDirs::locateLocal("data", "plasma/plasmoids/")); + + //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; + } + } +} + +QPoint WidgetExplorer::tooltipPosition(QGraphicsObject *item, int tipWidth, int tipHeight) +{ + if (!item) { + return QPoint(); + } + + // Find view + if (!item->scene()) { + return QPoint(); + } + + QList views = item->scene()->views(); + if (views.isEmpty()) { + return QPoint(); + } + + QGraphicsView *view = 0; + if (views.size() == 1) { + view = views[0]; + } else { + QGraphicsView *found = 0; + QGraphicsView *possibleFind = 0; + + foreach (QGraphicsView *v, views) { + if (v->sceneRect().intersects(item->sceneBoundingRect()) || + v->sceneRect().contains(item->scenePos())) { + if (v->isActiveWindow()) { + found = v; + } else { + possibleFind = v; + } + } + } + view = found ? found : possibleFind; + } + + if (!view) { + return QPoint(); + } + + // Compute tip pos + QRect itemRect( + view->mapToGlobal(view->mapFromScene(item->scenePos())), + item->boundingRect().size().toSize()); + QPoint pos; + switch (d->location) { + case Plasma::LeftEdge: + pos.setX(itemRect.right()); + pos.setY(itemRect.top() + (itemRect.height() - tipHeight) / 2); + break; + case Plasma::TopEdge: + pos.setX(itemRect.left() + (itemRect.width() - tipWidth) / 2); + pos.setY(itemRect.bottom()); + break; + case Plasma::RightEdge: + pos.setX(itemRect.left() - tipWidth); + pos.setY(itemRect.top() + (itemRect.height() - tipHeight) / 2); + break; + case Plasma::BottomEdge: + default: + pos.setX(itemRect.left() + (itemRect.width() - tipWidth) / 2); + pos.setY(itemRect.top() - tipHeight); + break; + } + + // Ensure tip stays within screen boundaries + const QRect avail = QApplication::desktop()->availableGeometry(view); + pos.setX(qBound(avail.left(), pos.x(), avail.right() - tipWidth)); + pos.setY(qBound(avail.top(), pos.y(), avail.bottom() - tipHeight)); + return pos; +} + +} // namespace Plasma + +#include "widgetexplorer.moc" diff --git a/widgetexplorer/widgetexplorer.h b/widgetexplorer/widgetexplorer.h new file mode 100644 index 000000000..cee2c578a --- /dev/null +++ b/widgetexplorer/widgetexplorer.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2007 by Ivan Cukic + * Copyright (C) 2009 by Ana Cecília Martins + * + * 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. + */ + + +#ifndef WIDGETEXPLORER_H +#define WIDGETEXPLORER_H + +#include + +#include + +#include + +#include "plasmaappletitemmodel_p.h" + +#include "plasmagenericshell_export.h" + +namespace Plasma +{ + +class Corona; +class Containment; +class Applet; +class WidgetExplorerPrivate; +class WidgetExplorerPrivate; + +//We need to access the separator property that is not exported by QAction +class WidgetAction : public QAction +{ + Q_OBJECT + Q_PROPERTY(bool separator READ isSeparator WRITE setSeparator) + +public: + WidgetAction(QObject *parent = 0); + WidgetAction(const QIcon &icon, const QString &text, QObject *parent); +}; + +class PLASMAGENERICSHELL_EXPORT WidgetExplorer : public QGraphicsWidget +{ + + Q_OBJECT + + /** + * Model that lists all applets + */ + Q_PROPERTY(QObject * widgetsModel READ widgetsModel CONSTANT) + + /** + * Model that lists all applets filters and categories + */ + Q_PROPERTY(QObject * filterModel READ filterModel CONSTANT) + + /** + * Actions for adding widgets, like download plasma widgets, download google gadgets, install from local file + */ + Q_PROPERTY(QList widgetsMenuActions READ widgetsMenuActions NOTIFY widgetsMenuActionsChanged) + + /** + * Extra actions assigned by the shell, like switch to activity manager + */ + Q_PROPERTY(QList extraActions READ extraActions NOTIFY extraActionsChanged) + + /** + * Plasma location of the panel containment the controller is associated to + */ + Q_PROPERTY(Location location READ location NOTIFY locationChanged) + Q_ENUMS(Location) + + /** + * Orientation the controller will be disaplayed, depends from location + */ + Q_PROPERTY(Qt::Orientation orientation READ orientation NOTIFY orientationChanged) + +public: + /** + * The Location enumeration describes where on screen an element, such as an + * Applet or its managing container, is positioned on the screen. + **/ + enum Location { + Floating = 0, /**< Free floating. Neither geometry or z-ordering + is described precisely by this value. */ + Desktop, /**< On the planar desktop layer, extending across + the full screen from edge to edge */ + FullScreen, /**< Full screen */ + TopEdge, /**< Along the top of the screen*/ + BottomEdge, /**< Along the bottom of the screen*/ + LeftEdge, /**< Along the left side of the screen */ + RightEdge /**< Along the right side of the screen */ + }; + + explicit WidgetExplorer(Plasma::Location loc, QGraphicsItem *parent = 0); + explicit WidgetExplorer(QGraphicsItem *parent = 0); + ~WidgetExplorer(); + + QString application(); + + /** + * Populates the widget list for the given application. This must be called + * before the widget explorer will be usable as the widget list will remain + * empty up to that point. + * + * @arg application the application which the widgets should be loaded for. + */ + void populateWidgetList(const QString &application = QString()); + + /** + * Changes the current default containment to add applets to + * + * @arg containment the new default + */ + void setContainment(Plasma::Containment *containment); + + /** + * @return the current default containment to add applets to + */ + Containment *containment() const; + /** + * @return the current corona this widget is added to + */ + Plasma::Corona *corona() const; + + + void setLocation(const Plasma::Location loc); + //FIXME: it's asymmetric due to the problems of QML of exporting enums + WidgetExplorer::Location location(); + + Qt::Orientation orientation() const; + + + QObject *widgetsModel() const; + QObject *filterModel() const; + + QList widgetsMenuActions(); + QList extraActions() const; + + Q_INVOKABLE void uninstall(const QString &pluginName); + + Q_INVOKABLE QPoint tooltipPosition(QGraphicsObject *item, int tipWidth, int tipHeight); + +Q_SIGNALS: + void locationChanged(Plasma::Location loc); + void orientationChanged(); + void closeClicked(); + void widgetsMenuActionsChanged(); + void extraActionsChanged(); + +public Q_SLOTS: + /** + * Adds currently selected applets + */ + void addApplet(const QString &pluginName); + void openWidgetFile(); + void downloadWidgets(const QString &type); + +protected Q_SLOTS: + void immutabilityChanged(Plasma::ImmutabilityType); + +protected: + void keyPressEvent(QKeyEvent *e); + bool event(QEvent *e); + void focusInEvent(QFocusEvent * event); + +private: + Q_PRIVATE_SLOT(d, void appletAdded(Plasma::Applet*)) + Q_PRIVATE_SLOT(d, void appletRemoved(Plasma::Applet*)) + Q_PRIVATE_SLOT(d, void containmentDestroyed()) + Q_PRIVATE_SLOT(d, void finished()) + + WidgetExplorerPrivate * const d; + friend class WidgetExplorerPrivate; +}; + +} // namespace Plasma + +#endif // WIDGETEXPLORER_H