File indexing completed on 2024-04-21 04:58:14

0001 /*  This file is part of the KDE project
0002     SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
0003     SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
0004     SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "konqfactory.h"
0010 #include <konq_kpart_plugin.h>
0011 #include "konqdebug.h"
0012 #include "konqsettings.h"
0013 #include "konqmainwindow.h"
0014 #include "konqutils.h"
0015 
0016 // std
0017 #include <assert.h>
0018 
0019 // Qt
0020 #include <QWidget>
0021 #include <QFile>
0022 #include <QCoreApplication>
0023 
0024 // KDE
0025 #include <KLocalizedString>
0026 #include <KParts/ReadOnlyPart>
0027 #include <KMessageBox>
0028 #include <KApplicationTrader>
0029 #include <KParts/PartLoader>
0030 
0031 KonqViewFactory::KonqViewFactory(const KPluginMetaData &data, KPluginFactory *factory)
0032     : m_metaData(data), m_factory(factory), m_args()
0033 {
0034 }
0035 
0036 void KonqViewFactory::setArgs(const QVariantList &args)
0037 {
0038     m_args = args;
0039 }
0040 
0041 KParts::ReadOnlyPart *KonqViewFactory::create(QWidget *parentWidget, QObject *parent)
0042 {
0043     if (!m_factory) {
0044         return nullptr;
0045     }
0046     KParts::ReadOnlyPart *part = m_factory->create<KParts::ReadOnlyPart>(parentWidget, parent, m_args);
0047 
0048     if (!part) {
0049         qCWarning(KONQUEROR_LOG) << "No KParts::ReadOnlyPart created from" << m_metaData.name();
0050     } else {
0051         KonqParts::Plugin::loadPlugins(part, part, part->componentName());
0052         QFrame *frame = qobject_cast<QFrame *>(part->widget());
0053         if (frame) {
0054             frame->setFrameStyle(QFrame::NoFrame);
0055         }
0056     }
0057     return part;
0058 }
0059 
0060 static KonqViewFactory tryLoadingService(const KPluginMetaData &data)
0061 {
0062     if (auto factoryResult = KPluginFactory::loadFactory(data)) {
0063         return KonqViewFactory(data, factoryResult.plugin);
0064     } else {
0065         KMessageBox::error(nullptr,
0066                            i18n("There was an error loading the module %1.\nThe diagnostics is:\n%2",
0067                                 data.name(), factoryResult.errorString));
0068         return KonqViewFactory();
0069     }
0070 }
0071 
0072 //TODO port away from query: check whether this output type arguments can be replaced by something else
0073 KonqViewFactory KonqFactory::createView(const QString &serviceType,
0074                                         const QString &serviceName,
0075                                         KPluginMetaData *serviceImpl,
0076                                         QVector<KPluginMetaData> *partServiceOffers,
0077                                         KService::List *appServiceOffers,
0078                                         bool forceAutoEmbed)
0079 {
0080     //TODO port away from query: check whether service name can be empty
0081     qCDebug(KONQUEROR_LOG) << "Trying to create view for" << serviceType << serviceName;
0082 
0083     // We need to get those in any case
0084     QVector<KPluginMetaData> offers;
0085     KService::List appOffers;
0086 
0087     // Query the plugins
0088     getOffers(serviceType, &offers, &appOffers);
0089 
0090     if (partServiceOffers) {
0091         (*partServiceOffers) = offers;
0092     }
0093     if (appServiceOffers) {
0094         (*appServiceOffers) = appOffers;
0095     }
0096 
0097     // We ask ourselves whether to do it or not only if no service was specified.
0098     // If it was (from the View menu or from RMB + Embedding service), just do it.
0099     forceAutoEmbed = forceAutoEmbed || !serviceName.isEmpty();
0100     // Or if we have no associated app anyway, then embed.
0101     forceAutoEmbed = forceAutoEmbed || (appOffers.isEmpty() && !offers.isEmpty());
0102     // Or if the associated app is konqueror itself, then embed.
0103     if (!appOffers.isEmpty()) {
0104         forceAutoEmbed = forceAutoEmbed || KonqMainWindow::isMimeTypeAssociatedWithSelf(serviceType, appOffers.first());
0105     }
0106 
0107     if (! forceAutoEmbed) {
0108         if (! KonqFMSettings::settings()->shouldEmbed(serviceType)) {
0109             qCDebug(KONQUEROR_LOG) << "KonqFMSettings says: don't embed this servicetype";
0110             return KonqViewFactory();
0111         }
0112     }
0113 
0114     KPluginMetaData service;
0115 
0116     // Look for this service
0117     if (!serviceName.isEmpty()) {
0118         auto it = std::find_if(offers.constBegin(), offers.constEnd(), [serviceName](const KPluginMetaData &md){return md.pluginId() == serviceName;});
0119         if (it != offers.constEnd()) {
0120             service = *it;
0121         }
0122     }
0123 
0124     KonqViewFactory viewFactory;
0125 
0126     if (service.isValid()) {
0127         qCDebug(KONQUEROR_LOG) << "Trying to open lib for requested service " << service.name();
0128         viewFactory = tryLoadingService(service);
0129         // If this fails, then return an error.
0130         // When looking for konq_sidebartng or konq_aboutpage, we don't want to end up
0131         // with khtml or another Browser/View part in case of an error...
0132     } else {
0133         for (const KPluginMetaData &md : offers) {
0134             // Allowed as default? Assume it is, unless explicitly stated otherwise
0135             if (md.value(QStringLiteral("X-KDE-BrowserView-AllowAsDefault"), true)) {
0136                 viewFactory = tryLoadingService(md);
0137                 if (!viewFactory.isNull()) {
0138                     service = md;
0139                     break;
0140                 }
0141             } else {
0142                 qCDebug(KONQUEROR_LOG) << "Not allowed as default " << md.name();
0143             }
0144         }
0145     }
0146 
0147     if (serviceImpl) {
0148         (*serviceImpl) = service;
0149     }
0150 
0151     if (viewFactory.isNull()) {
0152         if (offers.isEmpty()) {
0153             qCWarning(KONQUEROR_LOG) << "no part was associated with" << serviceType;
0154         } else {
0155             qCWarning(KONQUEROR_LOG) << "no part could be loaded";    // full error was shown to user already
0156         }
0157         return viewFactory;
0158     }
0159 
0160     QVariantList args;
0161     for (const QString &str : service.value(QStringLiteral("X-KDE-BrowserView-Args"), QStringList())) {
0162         args << QVariant(str);
0163     }
0164 
0165     if (Konq::serviceTypes(service).contains(QStringLiteral("Browser/View"))) {
0166         args << QLatin1String("Browser/View");
0167     }
0168 
0169     viewFactory.setArgs(args);
0170     return viewFactory;
0171 }
0172 
0173 void KonqFactory::getOffers(const QString &serviceType, QVector<KPluginMetaData> *partServiceOffers, KService::List *appServiceOffers)
0174 {
0175 #ifdef __GNUC__
0176 #warning Temporary hack -- must separate mimetypes and servicetypes better
0177 #endif
0178     if (partServiceOffers && serviceType.length() > 0 && serviceType[0].isUpper()) {
0179         //TODO port away from query: check whether it's still necessary to exclude kfmclient* from this vector (they aren't parts, so I think they shouldn't be included here)
0180         auto filter = [serviceType](const KPluginMetaData &md){return Konq::serviceTypes(md).contains(serviceType);};
0181         *partServiceOffers = findParts(filter);
0182         //Some parts, in particular konsolepart (at least version 22.12.3), aren't installed if kf5/parts but in the plugins directory, so we need to look for them separately
0183         //TODO: remove this line when (if) all parts are installed in kf5/parts
0184         partServiceOffers->append(KPluginMetaData::findPlugins(QString(), filter));
0185         return;
0186     }
0187 
0188     if (partServiceOffers) {
0189         const QVector<KPluginMetaData> offers = KParts::PartLoader::partsForMimeType(serviceType);
0190 
0191         //If a part has both JSON metadata and a .desktop file, partsForMimeType return the plugin twice. To avoid this, we remove the duplicate entries
0192         //We can't use std::unique because it requires the vector to be sorted but we can't do that because the entries are sorted according to user
0193         //preferences (we only keep the first entry for each plugin).
0194         //TODO: remove when .desktop files for parts aren't supported anymore (KF6)
0195         QVector<KPluginMetaData> uniqueOffers;
0196         for (const KPluginMetaData &md : offers) {
0197             auto comparison = [md](const KPluginMetaData &md2){
0198                 return md.pluginId() == md2.pluginId();
0199             };
0200             if (!std::any_of(uniqueOffers.constBegin(), uniqueOffers.constEnd(), comparison)) {
0201                 uniqueOffers.append(md);
0202             }
0203         }
0204         *partServiceOffers = uniqueOffers;
0205     }
0206     if (appServiceOffers) {
0207         *appServiceOffers = KApplicationTrader::queryByMimeType(serviceType, [](const KService::Ptr &s){return !s->desktopEntryName().startsWith("kfmclient");});
0208     }
0209 }