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 }