File indexing completed on 2024-05-05 04:47:44

0001 /****************************************************************************************
0002  * Copyright (c) 2017 Malte Veerman <malte.veerman@gmail.com>                             *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #define DEBUG_PREFIX "AppletModel"
0018 
0019 #include "AppletModel.h"
0020 
0021 #include "AmarokContextPackageStructure.h"
0022 #include "AppletLoader.h"
0023 #include "core/support/Amarok.h"
0024 #include "core/support/Debug.h"
0025 
0026 #include <QUrl>
0027 
0028 #include <KPackage/Package>
0029 #include <KPackage/PackageLoader>
0030 #include <KPluginInfo>
0031 
0032 #include <algorithm>
0033 
0034 
0035 using namespace Context;
0036 
0037 
0038 class Context::AppletPackage : public KPackage::Package
0039 {
0040 public:
0041     AppletPackage(const KPackage::Package &package) : KPackage::Package(package) {}
0042 
0043     bool operator==(const AppletPackage &p)
0044     {
0045         return metadata() == p.metadata();
0046     }
0047 };
0048 
0049 
0050 AppletModel::AppletModel(AppletLoader *loader, QObject* parent)
0051     : QAbstractListModel(parent)
0052     , m_loader(loader)
0053 {
0054     newApplets(loader->applets());
0055     connect(loader, &AppletLoader::finished, this, &AppletModel::newApplets);
0056 }
0057 
0058 AppletModel::~AppletModel()
0059 {
0060 }
0061 
0062 int AppletModel::rowCount(const QModelIndex& parent) const
0063 {
0064     Q_UNUSED(parent)
0065 
0066     return m_packages.size();
0067 }
0068 
0069 void AppletModel::newApplets(const QList<KPluginMetaData>& applets)
0070 {
0071     DEBUG_BLOCK
0072 
0073     beginResetModel();
0074 
0075     m_packages.clear();
0076 
0077     for (const auto &applet : applets)
0078     {
0079         auto loader = KPackage::PackageLoader::self();
0080         auto structure = new AmarokContextPackageStructure;
0081         loader->addKnownPackageStructure(QStringLiteral("Amarok/Context"), structure);
0082         auto package = loader->loadPackage(QStringLiteral("Amarok/Context"), applet.pluginId());
0083 
0084         if (package.isValid())
0085         {
0086             m_packages << package;
0087         }
0088         else
0089             error() << "Error loading package:" << applet.pluginId();
0090     }
0091 
0092     //Sort applets by name
0093     std::sort(m_packages.begin(), m_packages.end(), [] (const AppletPackage &p1, const AppletPackage &p2) {
0094         return p1.metadata().name() < p2.metadata().name();
0095     });
0096 
0097     endResetModel();
0098 }
0099 
0100 QVariant AppletModel::data(const QModelIndex& index, int role) const
0101 {
0102     int row = index.row();
0103 
0104     if (row >= m_packages.size())
0105         return QVariant();
0106 
0107     const auto &package = m_packages.at(row);
0108 
0109     switch (role)
0110     {
0111         case Name:
0112             return package.metadata().name();
0113 
0114         case Id:
0115             return package.metadata().pluginId();
0116 
0117         case Icon:
0118             return package.fileUrl("icon");
0119 
0120         case Mainscript:
0121             return package.fileUrl("mainscript");
0122 
0123         case Collapsed:
0124             return Amarok::config(QStringLiteral("Context")).readEntry(package.metadata().pluginId() + "_collapsed", false);
0125 
0126         case ContentHeight:
0127             return Amarok::config(QStringLiteral("Context")).readEntry(package.metadata().pluginId() + "_contentHeight", 300);
0128     }
0129 
0130     return QVariant();
0131 }
0132 
0133 bool Context::AppletModel::setData(const QModelIndex& index, const QVariant& value, int role)
0134 {
0135     int row = index.row();
0136 
0137     if (row >= m_packages.size())
0138         return false;
0139 
0140     const auto &package = m_packages.at(row);
0141 
0142     switch (role)
0143     {
0144         case Collapsed:
0145         {
0146             Amarok::config(QStringLiteral("Context")).writeEntry(package.metadata().pluginId() + "_collapsed", value.toBool());
0147             Q_EMIT dataChanged(index, index, QVector<int>{role});
0148             return true;
0149         }
0150         case ContentHeight:
0151         {
0152             Amarok::config(QStringLiteral("Context")).writeEntry(package.metadata().pluginId() + "_contentHeight", value.toReal());
0153             Q_EMIT dataChanged(index, index, QVector<int>{role});
0154             return true;
0155         }
0156 
0157         default:
0158             warning() << (Role) role << "is read-only.";
0159     }
0160 
0161     return false;
0162 }
0163 
0164 QHash< int, QByteArray > AppletModel::roleNames() const
0165 {
0166     QHash<int, QByteArray> roles;
0167     roles.insert(Name, "name");
0168     roles.insert(Id, "appletId");
0169     roles.insert(Icon, "icon");
0170     roles.insert(Mainscript, "mainscript");
0171     roles.insert(Collapsed, "collapsed");
0172     roles.insert(ContentHeight, "contentHeight");
0173 
0174     return roles;
0175 }
0176 
0177 void AppletModel::setAppletCollapsed(const QString& id, bool collapsed)
0178 {
0179     DEBUG_BLOCK
0180 
0181     debug() << "Set collapsed for applet:" << id << "to:" << collapsed;
0182 
0183     auto package = findPackage(id);
0184     if (package.isValid())
0185     {
0186         Amarok::config(QStringLiteral("Context")).writeEntry(id + "_collapsed", collapsed);
0187         int row = m_packages.indexOf(package);
0188         auto index = createIndex(row, 0);
0189         Q_EMIT dataChanged(index, index, QVector<int>{Collapsed});
0190     }
0191 }
0192 
0193 void Context::AppletModel::setAppletContentHeight(const QString& id, qreal height)
0194 {
0195     DEBUG_BLOCK
0196 
0197     debug() << "Set content height for applet:" << id << "to:" << height;
0198 
0199     auto package = findPackage(id);
0200     if (package.isValid())
0201     {
0202         Amarok::config(QStringLiteral("Context")).writeEntry(id + "_contentHeight", height);
0203         int row = m_packages.indexOf(package);
0204         auto index = createIndex(row, 0);
0205         Q_EMIT dataChanged(index, index, QVector<int>{ContentHeight});
0206     }
0207 }
0208 
0209 QUrl Context::AppletModel::imageUrl(const QString& id, const QString& imageName)
0210 {
0211     auto package = findPackage(id);
0212     if (package.isValid())
0213         return package.fileUrl("images", imageName);
0214     return QUrl();
0215 }
0216 
0217 AppletPackage AppletModel::findPackage(const QString& id)
0218 {
0219     for (const auto &package : m_packages)
0220     {
0221         auto metadata = package.metadata();
0222 
0223         if (metadata.pluginId() == id)
0224             return package;
0225     }
0226 
0227     warning() << "Applet with id:" << id << "not found.";
0228     return AppletPackage(KPackage::Package());
0229 }
0230 
0231 AppletProxyModel::AppletProxyModel(AppletModel* appletModel, QObject *parent)
0232     : QSortFilterProxyModel(parent)
0233     , m_appletModel(appletModel)
0234 {
0235     setSourceModel(appletModel);
0236     setDynamicSortFilter(true);
0237     sort(0);
0238 
0239     connect(m_appletModel->loader(), &AppletLoader::finished, this, &AppletProxyModel::enabledAppletsChanged);
0240 }
0241 
0242 AppletProxyModel::~AppletProxyModel()
0243 {
0244 }
0245 
0246 QStringList AppletProxyModel::enabledApplets() const
0247 {
0248     QStringList list;
0249     for (const auto &applet : m_appletModel->loader()->enabledApplets())
0250     {
0251         list << applet.pluginId();
0252     }
0253 
0254     std::sort(list.begin(), list.end(),
0255               [] (const QString &a, const QString &b)  {
0256                   QStringList ae = Amarok::config(QStringLiteral("Context")).readEntry("enabledApplets", QStringList());
0257                   return ae.indexOf(a) < ae.indexOf(b);
0258               }
0259     );
0260 
0261     return list;
0262 }
0263 
0264 void AppletProxyModel::setAppletEnabled(const QString& id, bool enabled, int place)
0265 {
0266     DEBUG_BLOCK
0267 
0268     debug() << "Set enabled for applet:" << id << "to:" << enabled;
0269 
0270     if (enabled == appletEnabled(id))
0271         return;
0272 
0273     QStringList ea = enabledApplets();
0274 
0275     if (enabled)
0276     {
0277         if (place <= -1)
0278             place = ea.size();
0279 
0280         debug() << "Applet's new place is" << place;
0281         ea.insert(place, id);
0282     }
0283     else
0284     {
0285         ea.removeAll(id);
0286     }
0287     Amarok::config(QStringLiteral("Context")).writeEntry("enabledApplets", ea);
0288 
0289     debug() << "New enabled applets:" << ea;
0290 
0291     invalidateFilter();
0292 
0293     Q_EMIT enabledAppletsChanged();
0294 }
0295 
0296 void AppletProxyModel::setAppletPlace(const QString& id, int place)
0297 {
0298     DEBUG_BLOCK
0299 
0300     debug() << "Set place for applet:" << id << "to:" << place;
0301 
0302     int currentPlace = appletPlace(id);
0303     debug() << "Current place is" << currentPlace;
0304 
0305     if (currentPlace == place)
0306         return;
0307 
0308     if (place <= -1)
0309     {
0310         setAppletEnabled(id, false);
0311         return;
0312     }
0313 
0314     if (currentPlace <= -1)
0315         setAppletEnabled(id, true, place);
0316 
0317     QStringList ea = enabledApplets();
0318 
0319     place = qMin(place, ea.size() - 1);
0320     bool forward = place > currentPlace;
0321 
0322     beginMoveRows(QModelIndex(), currentPlace, currentPlace, QModelIndex(), forward ? place + 1 : place);
0323     ea.move(currentPlace, place);
0324     Amarok::config(QStringLiteral("Context")).writeEntry("enabledApplets", ea);
0325     endMoveRows();
0326 
0327     debug() << "New enabled applets:" << ea;
0328 }
0329 
0330 int AppletProxyModel::appletPlace(const QString& id) const
0331 {
0332     return enabledApplets().indexOf(id);
0333 }
0334 
0335 bool AppletProxyModel::appletEnabled(const QString& id) const
0336 {
0337     return enabledApplets().contains(id);
0338 }
0339 
0340 void Context::AppletProxyModel::clear()
0341 {
0342     for( const auto &applet : enabledApplets() )
0343     {
0344         setAppletEnabled( applet, false );
0345     }
0346 }
0347 
0348 bool AppletProxyModel::lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const
0349 {
0350     int placeLeft = appletPlace(source_left.data(AppletModel::Id).toString());
0351     int placeRight = appletPlace(source_right.data(AppletModel::Id).toString());
0352 
0353     return placeLeft < placeRight;
0354 }
0355 
0356 bool AppletProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
0357 {
0358     QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
0359     return appletEnabled(index.data(AppletModel::Id).toString());
0360 }
0361 
0362