KCoreAddons

kpluginmetadata.h
1 /*
2  This file is part of the KDE project
3 
4  SPDX-FileCopyrightText: 2014 Alex Richardson <[email protected]>
5  SPDX-FileCopyrightText: 2021 Alexander Lohnau <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #ifndef KPLUGINMETADATA_H
11 #define KPLUGINMETADATA_H
12 
13 #include "kcoreaddons_export.h"
14 
15 #include <QExplicitlySharedDataPointer>
16 #include <QJsonObject>
17 #include <QMetaType>
18 #include <QString>
19 #include <QStringList>
20 
21 #include <functional>
22 
23 class KPluginLoader;
24 class KPluginFactory;
25 class QPluginLoader;
26 class KPluginMetaDataPrivate;
27 class KAboutPerson;
28 class QObject;
29 class QStaticPlugin;
30 /**
31  * @class KPluginMetaData kpluginmetadata.h KPluginMetaData
32  *
33  * This class allows easily accessing some standardized values from the JSON metadata that
34  * can be embedded into Qt plugins. Additional plugin-specific metadata can be retrieved by
35  * directly reading from the QJsonObject returned by KPluginMetaData::rawData().
36  *
37  * This class can be used instead of KPluginInfo from KService for applications that only load
38  * Qt C++ plugins.
39  *
40  * The following keys will be read from an object "KPlugin" inside the metadata JSON:
41  *
42  * Key | Accessor function | JSON Type
43  * -------------------| -------------------- | ---------------------
44  * Name | name() | string
45  * Description | description() | string
46  * ExtraInformation | extraInformation() | string
47  * Icon | iconName() | string
48  * Authors | authors() | object array (KAboutPerson)
49  * Category | category() | string
50  * License | license() | string
51  * Copyright | copyrightText() | string
52  * Id | pluginId() | string
53  * Version | version() | string
54  * Website | website() | string
55  * EnabledByDefault | isEnabledByDefault() | bool
56  * ServiceTypes | serviceTypes() | string array
57  * MimeTypes | mimeTypes() | string array
58  * FormFactors | formFactors() | string array
59  * Translators | translators() | object array (KAboutPerson)
60  * OtherContributors | otherContributors() | object array (KAboutPerson)
61  *
62  * The Authors, Translators and OtherContributors keys are expected to be
63  * list of objects that match the structure expected by KAboutPerson::fromJSON().
64  *
65  * An example metadata json file could look like this:
66  * @verbatim
67  {
68  "KPlugin": {
69  "Name": "Date and Time",
70  "Description": "Date and time by timezone",
71  "Icon": "preferences-system-time",
72  "Authors": [ { "Name": "Aaron Seigo", "Email": "[email protected]" } ],
73  "Category": "Date and Time",
74  "EnabledByDefault": "true",
75  "License": "LGPL",
76  "Id": "time",
77  "Version": "1.0",
78  "Website": "https://plasma.kde.org/"
79  }
80  }
81  @endverbatim
82  *
83  * @sa KAboutPerson::fromJSON()
84  * @since 5.1
85  */
86 class KCOREADDONS_EXPORT KPluginMetaData
87 {
88  Q_GADGET
89  Q_PROPERTY(bool isValid READ isValid CONSTANT)
90  Q_PROPERTY(bool isHidden READ isHidden CONSTANT)
91  Q_PROPERTY(QString fileName READ fileName CONSTANT)
92  Q_PROPERTY(QString metaDataFileName READ metaDataFileName CONSTANT)
93  Q_PROPERTY(QJsonObject rawData READ rawData CONSTANT)
94  Q_PROPERTY(QString name READ name CONSTANT)
95  Q_PROPERTY(QString description READ description CONSTANT)
96 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 87)
97  Q_PROPERTY(QString extraInformation READ extraInformation CONSTANT)
98 #endif
99  Q_PROPERTY(QVariantList authors READ authorsVariant CONSTANT)
100  Q_PROPERTY(QVariantList translators READ translatorsVariant CONSTANT)
101  Q_PROPERTY(QVariantList otherContributors READ otherContributorsVariant CONSTANT)
102  Q_PROPERTY(QString category READ category CONSTANT)
103  Q_PROPERTY(QString iconName READ iconName CONSTANT)
104  Q_PROPERTY(QString license READ license CONSTANT)
105  Q_PROPERTY(QString licenseText READ licenseText CONSTANT)
106  Q_PROPERTY(QString copyrightText READ copyrightText CONSTANT)
107  Q_PROPERTY(QString pluginId READ pluginId CONSTANT)
108  Q_PROPERTY(QString version READ version CONSTANT)
109  Q_PROPERTY(QString website READ website CONSTANT)
110 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 79)
111  Q_PROPERTY(QStringList dependencies READ dependencies CONSTANT)
112 #endif
113 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 89)
114  Q_PROPERTY(QStringList serviceTypes READ serviceTypes CONSTANT)
115 #endif
116  Q_PROPERTY(QStringList mimeTypes READ mimeTypes CONSTANT)
117  Q_PROPERTY(QStringList formFactors READ formFactors CONSTANT)
118  Q_PROPERTY(bool isEnabledByDefault READ isEnabledByDefault CONSTANT)
119  Q_PROPERTY(int initialPreference READ isEnabledByDefault CONSTANT)
120 
121 public:
122  /** Creates an invalid KPluginMetaData instance */
123  KPluginMetaData();
124 
125 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 86)
126  /**
127  * Reads the plugin metadata from a KPluginLoader instance. You must call KPluginLoader::setFileName()
128  * or use the appropriate constructor on @p loader before calling this.
129  */
130  KCOREADDONS_DEPRECATED_VERSION(5, 86, "Use KPluginMetaData(QPluginLoader) instead")
131  KPluginMetaData(const KPluginLoader &loader);
132 #endif
133 
134  /**
135  * Reads the plugin metadata from a QPluginLoader instance. You must call QPluginLoader::setFileName()
136  * or use the appropriate constructor on @p loader before calling this.
137  */
138  KPluginMetaData(const QPluginLoader &loader);
139 
140  /**
141  * Reads the plugin metadata from a plugin or .desktop which can be loaded from @p file.
142  *
143  * For plugins, platform-specific library suffixes may be omitted since @p file will be resolved
144  * using the same logic as QPluginLoader.
145  *
146  * If the file name ends with ".desktop", the .desktop file will be parsed instead of
147  * reading the metadata from the QPluginLoader. This is the same as calling
148  * KPluginMetaData::fromDesktopFile() without the serviceTypes parameter.
149  *
150  * If @p file ends with .json, the file will be loaded as the QJsonObject metadata.
151  *
152  * @see QPluginLoader::setFileName()
153  * @see KPluginMetaData::fromDesktopFile()
154  */
155  KPluginMetaData(const QString &file);
156 
157  /**
158  * Creates a KPluginMetaData from a QJsonObject holding the metadata and a file name
159  * This can be used if the data is not retrieved from a Qt C++ plugin library but from some
160  * other source.
161  * @see KPluginMetaData(const QJsonObject &, const QString &, const QString &)
162  */
163  KPluginMetaData(const QJsonObject &metaData, const QString &file);
164 
165  // TODO: KF6: merge with the above and make metaDataFile default to QString()
166  /**
167  * Creates a KPluginMetaData
168  * @param metaData the JSON metadata to use for this object
169  * @param pluginFile the file that the plugin can be loaded from
170  * @param metaDataFile the file that the JSON metadata was read from
171  *
172  * This can be used if the data is not retrieved from a Qt C++ plugin library but from some
173  * other source.
174  *
175  * @since 5.5
176  */
177  KPluginMetaData(const QJsonObject &metaData, const QString &pluginFile, const QString &metaDataFile);
178 
179  /*
180  * Constructs a KPluginMetaData from the static plugin.
181  * If it does not have any meta data the @p metaData value is used
182  *
183  * @see KPluginFactory::loadFactory
184  * @see KPluginFactory::instantiatePlugin
185  *
186  * @since 5.89
187  */
188  KPluginMetaData(QStaticPlugin plugin, const QJsonObject &metaData = {});
189 
190  /**
191  * Copy contructor
192  */
194  /**
195  * Copy assignment
196  */
197  KPluginMetaData &operator=(const KPluginMetaData &);
198  /**
199  * Destructor
200  */
201  ~KPluginMetaData();
202 
203  /**
204  * Load a KPluginMetaData instance from a .desktop file. Unlike the constructor which takes
205  * a single file parameter this method allows you to specify which service type files should
206  * be parsed to determine the correct type for a given .desktop property.
207  * This ensures that a e.g. comma-separated string list field in the .desktop file will correctly
208  * be converted to a JSON string array.
209  *
210  * @note This function mostly exists for backwards-compatibility. It is recommended
211  * that new applications load JSON files directly instead of using .desktop files for plugin metadata.
212  *
213  * @param file the .desktop file to load
214  * @param serviceTypes a list of files to parse If one of these paths is a relative path it
215  * will be resolved relative to the "kservicetypes5" subdirectory in QStandardPaths::GenericDataLocation.
216  * If the list is empty only the default set of properties will be treated specially and all other entries
217  * will be read as the JSON string type.
218  *
219  * @since 5.16
220  */
221  static KPluginMetaData fromDesktopFile(const QString &file, const QStringList &serviceTypes = QStringList());
222 
223  /**
224  * Load a KPluginMetaData instance from a .json file. Unlike the constructor with a single file argument,
225  * this ensure that only JSON format plugins are loaded and any other type is rejected.
226  *
227  * @param jsonFile the .json file to load
228  * @since 5.91
229  */
230  static KPluginMetaData fromJsonFile(const QString &jsonFile);
231 
232  /**
233  * @param directory The directory to search for plugins. If a relative path is given for @p directory,
234  * all entries of QCoreApplication::libraryPaths() will be checked with @p directory appended as a
235  * subdirectory. If an absolute path is given only that directory will be searched.
236  * @note Check if the returned KPluginMetaData is valid before continuing to use it.
237  *
238  * @param pluginId The Id of the plugin. The id should be the same as the filename, see KPluginMetaData::pluginId()
239  * @since 5.84
240  */
241  static KPluginMetaData findPluginById(const QString &directory, const QString &pluginId);
242 
243  /**
244  * Find all plugins inside @p directory. Only plugins which have JSON metadata will be considered.
245  *
246  * @param directory The directory to search for plugins. If a relative path is given for @p directory,
247  * all entries of QCoreApplication::libraryPaths() will be checked with @p directory appended as a
248  * subdirectory. If an absolute path is given only that directory will be searched.
249  *
250  * @param filter a callback function that returns @c true if the found plugin should be loaded
251  * and @c false if it should be skipped. If this argument is omitted all plugins will be loaded
252  *
253  * @return all plugins found in @p directory that fulfil the constraints of @p filter
254  * @since 5.86
255  */
256  static QVector<KPluginMetaData> findPlugins(const QString &directory, std::function<bool(const KPluginMetaData &)> filter = {});
257 
258  /**
259  * @return whether this object holds valid information about a plugin.
260  * If this is @c true pluginId() will return a non-empty string.
261  */
262  bool isValid() const;
263 
264  /**
265  * @return whether this object should be hidden, this is usually not used for binary
266  * plugins, when loading a KPluginMetaData from a .desktop file, this will reflect
267  * the value of the "Hidden" key.
268  *
269  * @since 5.8
270  */
271  bool isHidden() const;
272 
273  /**
274  * @return the path to the plugin. This string can be passed to the KPluginLoader
275  * or QPluginLoader constructors in order to attempt to load this plugin.
276  * @note It is not guaranteed that this is a valid path to a shared library (i.e. loadable
277  * by QPluginLoader) since the metadata could also refer to a non-C++ plugin.
278  */
279  QString fileName() const;
280 
281  /**
282  * @return the file that the metadata was read from. This is not necessarily the same as
283  * fileName(), since not all plugins have the metadata embedded. The metadata could also be
284  * stored in a separate .desktop file.
285  *
286  * @since 5.5
287  */
288  QString metaDataFileName() const;
289 
290  /**
291  * @return the full metadata stored inside the plugin file.
292  */
293  QJsonObject rawData() const;
294 
295 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 86)
296  /**
297  * Tries to instantiate this plugin using KPluginMetaData::fileName().
298  * @note The value of KPluginMetaData::dependencies() is not used here, dependencies must be
299  * resolved manually.
300  *
301  * @return The plugin root object or @c nullptr if it could not be loaded
302  * @see QPluginLoader::instance(), KPluginFactory::loadFactory, KPluginFactory::instantiatePlugin
303  * @deprecated Since 5.86, use @ref KPluginFactory::loadFactory or @ref KPluginFactory::instantiatePlugin when using
304  * KPluginFactory. Otherwise use QPluginLoader::instance() instead.
305  */
306  KCOREADDONS_DEPRECATED_VERSION(5, 86, "See API docs")
307  QObject *instantiate() const;
308 #endif
309 
310  /**
311  * @return the user visible name of the plugin.
312  */
313  QString name() const;
314 
315  /**
316  * @return a short description of the plugin.
317  */
318  QString description() const;
319 
320 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 87)
321  /**
322  * @return additional information about this plugin (e.g. for use in an "about plugin" dialog)
323  *
324  * @since 5.18
325  * @deprecated Since 5.87, deprecate for lack of usage. Use a meaningful custom key in the json metadata instead
326  */
327  KCOREADDONS_DEPRECATED_VERSION(5, 87, "Deprecate for lack of usage, use a meaningful custom key in the json metadata instead")
328  QString extraInformation() const;
329 #endif
330 
331  /**
332  * @return the author(s) of this plugin.
333  */
334  QList<KAboutPerson> authors() const;
335 
336  /**
337  * @return the translator(s) of this plugin.
338  *
339  * @since 5.18
340  */
341  QList<KAboutPerson> translators() const;
342 
343  /**
344  * @return a list of people that contributed to this plugin (other than the authors and translators).
345  *
346  * @since 5.18
347  */
348  QList<KAboutPerson> otherContributors() const;
349 
350  /**
351  * @return the categories of this plugin (e.g. "playlist/skin").
352  */
353  QString category() const;
354 
355  /**
356  * @return the icon name for this plugin
357  * @see QIcon::fromTheme()
358  */
359  QString iconName() const;
360 
361  /**
362  * @return the short license identifier (e.g. LGPL).
363  * @see KAboutLicense::byKeyword() for retrieving the full license information
364  */
365  QString license() const;
366 
367  /**
368  * @return the text of the license, equivalent to KAboutLicense::byKeyword(license()).text()
369  * @since 5.73
370  */
371  QString licenseText() const;
372 
373  /**
374  * @return a short copyright statement
375  *
376  * @since 5.18
377  */
378  QString copyrightText() const;
379 
380  /**
381  * @return the internal name of the plugin
382  * If the Id property is not set in the metadata, this will return the
383  * plugin file name without the file extension.
384  */
385  QString pluginId() const;
386 
387  /**
388  * @return the version of the plugin.
389  */
390  QString version() const;
391 
392  /**
393  * @return the website of the plugin.
394  */
395  QString website() const;
396 
397 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 79)
398  /**
399  * @return a list of plugins that this plugin depends on so that it can function properly
400  * @see KJsonPluginInfo::pluginId()
401  * @deprecated Since 5.79, plugin dependencies are deprecated and will be removed in KF6
402  */
403  KCOREADDONS_DEPRECATED_VERSION(5, 79, "Plugin dependencies are deprecated and will be removed in KF6")
404  QStringList dependencies() const;
405 #endif
406 
407 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 89)
408  /**
409  * Returns the service types that this plugin implements.
410  *
411  * This is mostly for historical / compatibility purposes.
412  * As a general rule, instead of opening many plugins to then filter by servicetype,
413  * put all plugins of the same type in a subdirectory, that you can pass to findPlugins directly.
414  * No point in opening 20 plugins to pick out only 3, when the filesystem can do that filtering for you.
415  *
416  * @note Unlike KService this does not contain the MIME types. To get the handled MIME types
417  * use the KPluginMetaData::mimeTypes() function.
418  * @return a list of service types this plugin implements (e.g. "Plasma/DataEngine")
419  * @dprecated Since 5.89, use dedicated plugin namespaces instead to filter plugins of a specific type
420  */
421  KCOREADDONS_DEPRECATED_VERSION(5, 89, "See API docs")
422  QStringList serviceTypes() const;
423 #endif
424 
425  /**
426  * @return a list of MIME types this plugin can handle (e.g. "application/pdf", "image/png", etc.)
427  * @since 5.16
428  */
429  QStringList mimeTypes() const;
430 
431  /**
432  * @return true if this plugin can handle the given mimetype
433  * This is more accurate than mimeTypes().contains(mimeType) because it also
434  * takes MIME type inheritance into account.
435  * @since 5.66
436  */
437  bool supportsMimeType(const QString &mimeType) const;
438 
439  /**
440  * @return A string list of formfactors this plugin is useful for, e.g. desktop, tablet,
441  * handset, mediacenter, etc.
442  * The keys for this are not formally defined.
443  *
444  * @since 5.12
445  */
446  QStringList formFactors() const;
447 
448  /**
449  * @return whether the plugin should be enabled by default.
450  * This is only a recommendation, applications can ignore this value if they want to.
451  */
452  bool isEnabledByDefault() const;
453 
454  /**
455  * @return the initial preference of the plugin.
456  * This is the preference to associate with this plugin initially (before
457  * the user has had any chance to define preferences for it).
458  * Higher values indicate stronger preference.
459  * @since 5.67
460  */
461  int initialPreference() const;
462 
463  /**
464  * Returns @c true if the plugin is enabled in @p config, otherwise returns isEnabledByDefault().
465  * This can be used in conjunctionwith KPluginWidget/KPluginSelector.
466  *
467  * The @p config param should be a KConfigGroup object, because KCoreAddons can not depend
468  * on KConfig directly, this parameter is a template.
469  * @param config KConfigGroup where the enabled state is stored
470  * @since 5.89
471  */
472  template<typename T>
473  bool isEnabled(const T &config) const
474  {
475  Q_ASSERT(config.isValid());
476  return config.readEntry(pluginId() + QLatin1String("Enabled"), isEnabledByDefault());
477  }
478 
479  /**
480  * @return the value for @p key from the metadata or @p defaultValue if the key does not exist
481  * or the value for @p key is not of type string
482  *
483  * @see KPluginMetaData::rawData() if QString is not the correct type for @p key
484  */
485  QString value(const QString &key, const QString &defaultValue = QString()) const;
486 
487 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 88)
488  /**
489  * Overload to make sure the bool overload is not taken by accident
490  * @overload
491  * @since 5.88
492  */
493  KCOREADDONS_DEPRECATED_VERSION(5, 88, "Construct a QString instead of using a char array, otherwise there the bool overload could be chosen by accident")
494  QString value(const QString &key, const char *ch) const
495  {
496  return value(key, QString::fromLatin1(ch));
497  }
498 #else
499  QString value(const QString &key, const char *ch) const = delete;
500 #endif
501 
502  /**
503  * @overload
504  * @since 5.88
505  */
506  bool value(const QString &key, bool defaultValue) const;
507 
508  /**
509  * @overload
510  * @since 5.88
511  */
512  int value(const QString &key, int defaultValue) const;
513 
514  /** @return the value for @p key inside @p jo as a string list. If the type of @p key is string, a list with containing
515  * just that string will be returned, if it is an array the list will contain one entry for each array member.
516  * If the key cannot be found an empty list will be returned.
517  * @overload
518  * @since 5.88
519  */
520  QStringList value(const QString &key, const QStringList &defaultValue) const;
521 
522 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 88)
523  /** @return the value for @p key inside @p jo as a string list. If the type of @p key is string, a list with containing
524  * just that string will be returned, if it is an array the list will contain one entry for each array member.
525  * If the key cannot be found an empty list will be returned.
526  * @deprecated Since 5.88, use @p value(QString, QStringList) on KPluginMetaData instance instead
527  */
528  KCOREADDONS_DEPRECATED_VERSION(5, 88, "Use value(QString, QStringList) on KPluginMetaData instance instead")
529  static QStringList readStringList(const QJsonObject &jo, const QString &key);
530 #endif
531 
532 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 88)
533  /**
534  * Reads a value from @p jo but unlike QJsonObject::value() it allows different entries for each locale
535  * This is done by appending the locale identifier in brackets to the key (e.g. "[de_DE]" or "[es]")
536  * When looking for a key "foo" with German (Germany) locale we will first attempt to read "foo[de_DE]",
537  * if that does not exist "foo[de]", finally falling back to "foo" if that also doesn't exist.
538  * @return the translated value for @p key from @p jo or @p defaultValue if @p key was not found
539  * @deprecated Since 5.88, use KJsonUtils::readTranslatedValue instead
540  */
541  KCOREADDONS_DEPRECATED_VERSION(5, 88, "Use KJsonUtils::readTranslatedValue instead")
542  static QJsonValue readTranslatedValue(const QJsonObject &jo, const QString &key, const QJsonValue &defaultValue = QJsonValue());
543 #endif
544 
545 #if KCOREADDONS_ENABLE_DEPRECATED_SINCE(5, 88)
546  /**
547  * @return the translated value of @p key from @p jo as a string or @p defaultValue if @p key was not found
548  * or the value for @p key is not of type string
549  * @see KPluginMetaData::readTranslatedValue(const QJsonObject &jo, const QString &key)
550  * @deprecated Since 5.88, use KJsonUtils::readTranslatedString instead
551  */
552  KCOREADDONS_DEPRECATED_VERSION(5, 88, "Use KJsonUtils::readTranslatedString instead")
553  static QString readTranslatedString(const QJsonObject &jo, const QString &key, const QString &defaultValue = QString());
554 #endif
555 
556  /**
557  * @return @c true if this object is equal to @p other, otherwise @c false
558  */
559  bool operator==(const KPluginMetaData &other) const;
560 
561  /**
562  * @return @c true if this object is not equal to @p other, otherwise @c false.
563  */
564  inline bool operator!=(const KPluginMetaData &other) const
565  {
566  return !(*this == other);
567  }
568 
569  /**
570  * @note for loading plugin the plugin independently of it being static/dynamic
571  * use @ref KPluginFactory::loadFactory or @ref KPluginFactory::instantiatePlugin.
572  * @return true if the instance represents a static plugin
573  * @since 5.89
574  */
575  bool isStaticPlugin() const;
576 
577 private:
578  QJsonObject rootObject() const;
579  void loadFromDesktopFile(const QString &file, const QStringList &serviceTypes);
580  void loadFromJsonFile(const QString &file);
581 
582 private:
583  QVariantList authorsVariant() const;
584  QVariantList translatorsVariant() const;
585  QVariantList otherContributorsVariant() const;
586  QStaticPlugin staticPlugin() const;
587  QString requestedFileName() const;
588 
589  QJsonObject m_metaData;
590  QString m_fileName;
591  QExplicitlySharedDataPointer<KPluginMetaDataPrivate> d; // for future binary compatible extensions
592  friend class KPluginFactory;
593 };
594 
595 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
596 inline size_t qHash(const KPluginMetaData &md, size_t seed)
597 #else
598 inline uint qHash(const KPluginMetaData &md, uint seed)
599 #endif
600 {
601  return qHash(md.pluginId(), seed);
602 }
603 
604 Q_DECLARE_METATYPE(KPluginMetaData)
605 
606 #endif // KPLUGINMETADATA_H
bool isEnabled(const T &config) const
Returns true if the plugin is enabled in config, otherwise returns isEnabledByDefault().
const QLatin1String name
This class behaves largely like QPluginLoader (and, indeed, uses it internally), but additionally rea...
Definition: kpluginloader.h:69
KCALENDARCORE_EXPORT uint qHash(const KCalendarCore::Period &key)
This class is used to store information about a person or developer.
Definition: kaboutdata.h:61
bool operator!=(const KPluginMetaData &other) const
This class allows easily accessing some standardized values from the JSON metadata that can be embedd...
QString fromLatin1(const char *str, int size)
KPluginFactory provides a convenient way to provide factory-style plugins.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Mon Jan 24 2022 22:54:37 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.