File indexing completed on 2024-05-12 16:39:39
0001 /* This file is part of the KDE project 0002 Copyright (C) 2003 Lucijan Busch <lucijan@kde.org> 0003 Copyright (C) 2003-2016 Jarosław Staniek <staniek@kde.org> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 * Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "kexipartmanager.h" 0022 #include "kexipart.h" 0023 #include "kexiinternalpart.h" 0024 #include "kexipartinfo.h" 0025 //! @todo KEXI3 #include "kexistaticpart.h" 0026 #include "KexiVersion.h" 0027 #include "KexiJsonTrader.h" 0028 0029 #include <KDbConnection> 0030 #include <KDbMessageHandler> 0031 0032 #include <KLocalizedString> 0033 #include <KPluginFactory> 0034 #include <KConfigGroup> 0035 #include <KSharedConfig> 0036 0037 #include <QApplication> 0038 #include <QDebug> 0039 #include <QGlobalStatic> 0040 0041 0042 using namespace KexiPart; 0043 0044 typedef QHash<QString, KexiInternalPart*> KexiInternalPartDict; 0045 0046 Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiPartTrader_instance, (KEXI_BASE_PATH)) 0047 0048 class Q_DECL_HIDDEN Manager::Private 0049 { 0050 public: 0051 explicit Private(Manager *manager_); 0052 ~Private(); 0053 0054 Manager *manager; 0055 PartDict parts; 0056 KexiInternalPartDict internalParts; 0057 PartInfoList partlist; 0058 PartInfoDict partsByPluginId; 0059 bool lookupDone; 0060 bool lookupResult; 0061 }; 0062 0063 Manager::Private::Private(Manager *manager_) 0064 : manager(manager_) 0065 , lookupDone(false) 0066 , lookupResult(false) 0067 { 0068 } 0069 0070 Manager::Private::~Private() 0071 { 0072 qDeleteAll(partlist); 0073 partlist.clear(); 0074 } 0075 0076 //--- 0077 0078 Manager::Manager(QObject *parent) 0079 : QObject(parent), KDbResultable(), d(new Private(this)) 0080 { 0081 } 0082 0083 Manager::~Manager() 0084 { 0085 delete d; 0086 } 0087 0088 template <typename PartClass> 0089 PartClass* Manager::part(Info *info, QHash<QString, PartClass*> *partDict) 0090 { 0091 if (!info) { 0092 return 0; 0093 } 0094 clearResult(); 0095 KDbMessageGuard mg(this); 0096 if (!lookup()) { 0097 return 0; 0098 } 0099 if (!info->isValid()) { 0100 m_result = KDbResult(info->errorMessage()); 0101 return 0; 0102 } 0103 PartClass *p = partDict->value(info->pluginId()); 0104 if (p) { 0105 return p; 0106 } 0107 0108 // actual loading 0109 KPluginFactory *factory = qobject_cast<KPluginFactory*>(info->instantiate()); 0110 if (!factory) { 0111 m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, 0112 xi18nc("@info", "Could not load Kexi plugin file <filename>%1</filename>.", 0113 info->fileName())); 0114 QPluginLoader loader(info->fileName()); // use this to get the message 0115 (void)loader.load(); 0116 m_result.setServerMessage(loader.errorString()); 0117 info->setErrorMessage(m_result.message()); 0118 qWarning() << m_result.message() << m_result.serverMessage(); 0119 return 0; 0120 } 0121 p = factory->create<PartClass>(this); 0122 if (!p) { 0123 m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, 0124 xi18nc("@info", 0125 "Could not open Kexi plugin <filename>%1</filename>.").arg(info->fileName())); 0126 qWarning() << m_result.message(); 0127 return 0; 0128 } 0129 p->setInfo(info); 0130 p->setObjectName(QString("%1 plugin").arg(info->id())); 0131 partDict->insert(info->pluginId(), p); 0132 return p; 0133 } 0134 0135 //! @return a string list @a list with removed whitespace from the beginning and end of each string. 0136 //! Empty strings are also removed. 0137 static QStringList cleanupStringList(const QStringList &list) 0138 { 0139 QStringList result; 0140 foreach(const QString &item, list) { 0141 QString cleanedItem = item.trimmed(); 0142 if (!cleanedItem.isEmpty()) { 0143 result.append(cleanedItem); 0144 } 0145 } 0146 return result; 0147 } 0148 0149 bool Manager::lookup() 0150 { 0151 if (d->lookupDone) { 0152 return d->lookupResult; 0153 } 0154 d->lookupDone = true; 0155 d->lookupResult = false; 0156 d->partlist.clear(); 0157 d->partsByPluginId.clear(); 0158 d->parts.clear(); 0159 0160 // load visual order of plugins 0161 KConfigGroup cg(KSharedConfig::openConfig()->group("Parts")); 0162 const QStringList orderedPluginIds = cleanupStringList( 0163 cg.readEntry("Order", "org.kexi-project.table," 0164 "org.kexi-project.query," 0165 "org.kexi-project.form," 0166 "org.kexi-project.report," 0167 "org.kexi-project.macro," 0168 "org.kexi-project.script").split(',')); 0169 QVector<Info*> orderedInfos(orderedPluginIds.count()); 0170 QStringList serviceTypes; 0171 serviceTypes << "Kexi/Viewer" << "Kexi/Designer" << "Kexi/Editor" 0172 << "Kexi/Internal"; 0173 QList<QPluginLoader*> offers = KexiPartTrader_instance->query(serviceTypes); 0174 foreach(const QPluginLoader *loader, offers) { 0175 QScopedPointer<Info> info(new Info(*loader)); 0176 if (info->id().isEmpty()) { 0177 qWarning() << "No plugin ID specified for Kexi Part" 0178 << info->fileName() << "-- skipping!"; 0179 continue; 0180 } 0181 // check version 0182 const QString expectedVersion = KexiPart::version(); 0183 if (info->version() != expectedVersion) { 0184 qWarning() << "Kexi plugin" << info->id() << "has version" 0185 << info->version() << "but version required by Kexi is" 0186 << expectedVersion 0187 << "-- skipping this plugin!"; 0188 continue; 0189 } 0190 // skip experimental types 0191 if ( (!Kexi::tempShowMacros() && info->id() == "org.kexi-project.macro") 0192 || (!Kexi::tempShowScripts() && info->id() == "org.kexi-project.script") 0193 ) 0194 { 0195 continue; 0196 } 0197 // skip duplicates 0198 if (d->partsByPluginId.contains(info->id())) { 0199 qWarning() << "More than one Kexi plugin with ID" 0200 << info->id() << info->fileName() << "-- skipping this one"; 0201 continue; 0202 } 0203 // find correct place for plugins visible in Navigator 0204 if (info->isVisibleInNavigator()) { 0205 const int index = orderedPluginIds.indexOf(info->id()); 0206 if (index != -1) { 0207 orderedInfos[index] = info.data(); 0208 } 0209 else { 0210 orderedInfos.append(info.data()); 0211 } 0212 // append later when we know order 0213 } 0214 else { 0215 // append now 0216 d->partlist.append(info.data()); 0217 } 0218 d->partsByPluginId.insert(info->pluginId(), info.data()); 0219 info.take(); 0220 } 0221 qDeleteAll(offers); 0222 offers.clear(); 0223 if (d->partsByPluginId.isEmpty()) { 0224 m_result = KDbResult( 0225 xi18nc("@info", "<para>Could not find any Kexi plugins, e.g. for tables or forms. " 0226 "Kexi would not be functional so it will exit.</para>" 0227 "<para><note>Please check if Kexi is properly installed.</note></para>")); 0228 return false; 0229 } 0230 0231 // fill the final list using computed order 0232 for (int i = 0; i < orderedInfos.size(); i++) { 0233 Info *info = orderedInfos[i]; 0234 if (!info) { 0235 continue; 0236 } 0237 //qDebug() << "adding Kexi part info" << info->pluginId(); 0238 d->partlist.insert(i, info); 0239 } 0240 // now the d->partlist is: [ordered plugins visible in Navigator] [other plugins in unspecified order] 0241 d->lookupResult = true; 0242 return true; 0243 } 0244 0245 Part* Manager::part(Info *info) 0246 { 0247 KDbMessageGuard mg(this); 0248 Part *p = part<Part>(info, &d->parts); 0249 if (p) { 0250 emit partLoaded(p); 0251 } 0252 return p; 0253 } 0254 0255 static QString realPluginId(const QString &pluginId) 0256 { 0257 if (pluginId.contains('.')) { 0258 return pluginId; 0259 } 0260 else { 0261 // not like "org.kexi-project.table" - construct 0262 return QString::fromLatin1("org.kexi-project.") 0263 + QString(pluginId).remove("kexi/"); 0264 } 0265 } 0266 0267 Part* Manager::partForPluginId(const QString &pluginId) 0268 { 0269 Info* info = infoForPluginId(pluginId); 0270 return part(info); 0271 } 0272 0273 Info* Manager::infoForPluginId(const QString &pluginId) 0274 { 0275 KDbMessageGuard mg(this); 0276 if (!lookup()) 0277 return 0; 0278 const QString realId = realPluginId(pluginId); 0279 Info *i = realId.isEmpty() ? 0 : d->partsByPluginId.value(realId); 0280 if (i) 0281 return i; 0282 m_result = KDbResult(kxi18nc("@info", "No plugin for ID <resource>%1</resource>") 0283 .subs(realId) 0284 .toString(Kuit::VisualFormat::PlainText)); 0285 return 0; 0286 } 0287 0288 /*! @todo KEXI3 0289 void Manager::insertStaticPart(StaticPart* part) 0290 { 0291 if (!part) 0292 return; 0293 KDbMessageGuard mg(this); 0294 if (!lookup()) 0295 return; 0296 d->partlist.append(part->info()); 0297 if (!part->info()->pluginId().isEmpty()) 0298 d->partsByPluginId.insert(part->info()->pluginId(), part->info()); 0299 d->parts.insert(part->info()->pluginId(), part); 0300 } 0301 */ 0302 0303 KexiInternalPart* Manager::internalPartForPluginId(const QString& pluginId) 0304 { 0305 Info* info = infoForPluginId(pluginId); 0306 if (!info || !info->serviceTypes().contains("Kexi/Internal")) { 0307 return nullptr; 0308 } 0309 return part<KexiInternalPart>(info, &d->internalParts); 0310 } 0311 0312 PartInfoList* Manager::infoList() 0313 { 0314 KDbMessageGuard mg(this); 0315 if (!lookup()) { 0316 return 0; 0317 } 0318 return &d->partlist; 0319 }