File indexing completed on 2024-05-12 04:33:19

0001 /*
0002     SPDX-FileCopyrightText: 2009-2010 Michael G. Hansen <mike at mghansen dot de>
0003     SPDX-FileCopyrightText: 2011-2018 Gilles Caulier <caulier dot gilles at gmail dot com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 // C++ include
0009 
0010 #include <memory>
0011 
0012 // Qt includes
0013 
0014 #include <QTextStream>
0015 #include <QMenu>
0016 #include <QDebug>
0017 #include <QUrl>
0018 #include <QStandardPaths>
0019 #include <QApplication>
0020 #include <QCommandLineParser>
0021 #include <QCommandLineOption>
0022 
0023 // Libkipi includes
0024 
0025 #include "libkipi_version.h"
0026 #include "plugin.h"
0027 #include "pluginloader.h"
0028 #include "kipiinterface.h"
0029 
0030 #ifdef HAVE_KEXIV2
0031 #   include <kexiv2/kexiv2.h>
0032 #endif
0033 
0034 using namespace KXMLKipiCmd;
0035 using namespace KIPI;
0036 
0037 /**
0038 * \brief Returns the name of a KIPI::Category
0039 * \param category Category which should be returned as a string
0040 * \returns String version of the category
0041 */
0042 QString PluginCategoriesToString(const Category& category)
0043 {
0044     QString categorystring;
0045 
0046     switch(category)
0047     {
0048         case ImagesPlugin:
0049             categorystring = QLatin1String("Images");
0050             break;
0051 
0052         case ToolsPlugin:
0053             categorystring = QLatin1String("Tool");
0054             break;
0055 
0056         case ImportPlugin:
0057             categorystring = QLatin1String("Import");
0058             break;
0059 
0060         case ExportPlugin:
0061             categorystring = QLatin1String("Export");
0062             break;
0063 
0064         case BatchPlugin:
0065             categorystring = QLatin1String("Batch");
0066             break;
0067 
0068         case CollectionsPlugin:
0069             categorystring = QLatin1String("Collections");
0070             break;
0071 
0072         default:
0073             categorystring = QLatin1String("Unknown");
0074             break;
0075     };
0076 
0077     return categorystring;
0078 }
0079 
0080 /**
0081 * \brief Takes a list of QActions with submenus and converts them to a flat list
0082 * \param actions List of QActions with submenus
0083 * \param level Initial level of indentation (used internally for recursion)
0084 * \returns A list of QPairs containing the indentation level and the QAction*
0085 */
0086 QList<QPair<int, QAction*> > FlattenActionList(const QList<QAction*>& actions, const int level = 0)
0087 {
0088     QList<QPair<int, QAction*> > results;
0089 
0090     for (QList<QAction*>::ConstIterator it = actions.constBegin(); it!=actions.constEnd(); ++it)
0091     {
0092         const QString text = (*it)->text();
0093 
0094         if (!text.isEmpty())
0095         {
0096             results.append( QPair<int, QAction*>(level, *it) );
0097         }
0098 
0099         QMenu* const menu = (*it)->menu();
0100 
0101         if (menu)
0102         {
0103             results.append( FlattenActionList(menu->actions(), level+1) );
0104         }
0105     }
0106 
0107     return results;
0108 }
0109 
0110 /**
0111 * \brief Loads the kipi-plugins
0112 * \param libraryName If not empty, only the plugin in library libraryName is loaded and setup
0113 * \returns False if plugin library libraryName could not be loaded
0114 */
0115 bool LoadPlugins(const QString& libraryName = QString::fromLatin1(""))
0116 {
0117     if (!libraryName.isEmpty())
0118     {
0119         qDebug() << QString::fromLatin1("Will only load library \"%1\"").arg(libraryName);
0120     }
0121     else
0122     {
0123         qDebug() << "Will load all libraries, if possible.";
0124     }
0125 
0126     PluginLoader* const kipiPluginLoader = PluginLoader::instance();
0127 
0128     if (libraryName.isEmpty())
0129     {
0130         kipiPluginLoader->loadPlugins();
0131         return true;
0132     }
0133 
0134     const PluginLoader::PluginList pluginList = kipiPluginLoader->pluginList();
0135 
0136     for (PluginLoader::PluginList::ConstIterator it = pluginList.constBegin();
0137          it!=pluginList.constEnd(); ++it)
0138     {
0139         if ( (*it)->library() == libraryName )
0140         {
0141             if ( !(*it)->shouldLoad() )
0142             {
0143                 qDebug() << QString::fromLatin1("Can not load plugin \"%1\": Loader says it should not load.").arg(libraryName);
0144                 return false;
0145             }
0146 
0147             if ( !(*it)->plugin() )
0148             {
0149                 qDebug() << QString::fromLatin1("Plugin \"%1\" failed to load.").arg(libraryName);
0150                 return false;
0151             }
0152 
0153             qDebug() << QString::fromLatin1("Plugin \"%1\" loaded.").arg(libraryName);
0154             return true;
0155         }
0156     }
0157 
0158     qDebug() << QString::fromLatin1("Plugin \"%1\" not found.").arg(libraryName);
0159 
0160     return false;
0161 }
0162 
0163 /**
0164 * \brief Lists the available plugins and their actions
0165 * \param libraryName Load only plugin found in library libraryName
0166 * \returns False if plugin in library \a libraryName could not be loaded
0167 */
0168 bool ListPlugins(const QString& libraryName = QString::fromLatin1(""))
0169 {
0170     PluginLoader* const kipiPluginLoader = PluginLoader::instance();
0171 
0172     if ( !LoadPlugins( libraryName ) )
0173         return false;
0174 
0175     const PluginLoader::PluginList pluginList = kipiPluginLoader->pluginList();
0176     int pluginNumber                          = 1;
0177     const int nPlugins                        = pluginList.size();
0178     const int nDigits                         = QString::number(nPlugins).size();
0179     const QString preSpace                    = QString(nDigits+1+1, QChar::fromLatin1(' '));
0180     QWidget* const dummyWidget                = new QWidget();
0181 
0182     qDebug() << QString::fromLatin1("Found %1 plugin(s):").arg(nPlugins);
0183 
0184     for (PluginLoader::PluginList::ConstIterator it = pluginList.constBegin();
0185          it!= pluginList.constEnd(); ++ it)
0186     {
0187         const QString pluginNumberString = QString::fromLatin1("%1").arg(pluginNumber, nDigits);
0188         ++pluginNumber;
0189 
0190         qDebug() << QString::fromLatin1("%1: %2 - %3").arg(pluginNumberString).arg((*it)->name()).arg((*it)->comment());
0191         qDebug() << preSpace << QString::fromLatin1("Library: ") << (*it)->library();
0192 
0193         Plugin* const plugin = (*it)->plugin();
0194 
0195         if (plugin == nullptr)
0196         {
0197             qDebug() << preSpace << QString::fromLatin1( "Plugin not loaded.");
0198             continue;
0199         }
0200 
0201         plugin->setup(dummyWidget);
0202         const QList<QPair<int, QAction*> > actionsList = FlattenActionList(plugin->actions());
0203 
0204         qDebug() << preSpace << QString::fromLatin1("Actions:");
0205 
0206         const QString preSpaceActions = preSpace + QString::fromLatin1("  ");
0207 
0208         for (QList<QPair<int, QAction*> >::ConstIterator it = actionsList.constBegin();
0209              it!=actionsList.constEnd(); ++it)
0210         {
0211             const int level             = (*it).first;
0212             const QAction* const action = (*it).second;
0213             qDebug() << preSpaceActions << QString(level*2, QChar::fromLatin1(' ')) << '"' << action->text() << '"';
0214         }
0215     }
0216 
0217     return true;
0218 }
0219 
0220 /**
0221 * \brief Calls an action of a plugin
0222 * \param actionText Text of the action to call
0223 * \param libraryName Load only the plugin in this library
0224 * \returns False if the action could not be called
0225 */
0226 bool CallAction(const QString& actionText, const QString& libraryName = QString::fromLatin1(""))
0227 {
0228     qDebug() << QString::fromLatin1("Looking for action \"%1\"...").arg(actionText);
0229 
0230     PluginLoader* const kipiPluginLoader = PluginLoader::instance();
0231 
0232     if ( !LoadPlugins( libraryName ) )
0233         return false;
0234 
0235     PluginLoader::PluginList pluginList = kipiPluginLoader->pluginList();
0236 
0237     QWidget* const dummyWidget = new QWidget();
0238 
0239     bool foundAction = false;
0240 
0241     for (PluginLoader::PluginList::ConstIterator info = pluginList.constBegin();
0242          (info!=pluginList.constEnd()) && !foundAction; ++info)
0243     {
0244         if ( !libraryName.isEmpty() && ( (*info)->library() != libraryName ) )
0245             continue;
0246 
0247         // scan for the desired action:
0248         Plugin* const plugin = (*info)->plugin();
0249 
0250         if (plugin == nullptr)
0251         {
0252             qDebug() << QString::fromLatin1("Plugin \"%1\" failed to load.").arg((*info)->library());
0253             continue;
0254         }
0255 
0256         plugin->setup(dummyWidget/*.get()*/);
0257         const QList<QPair<int, QAction*> > actionsList = FlattenActionList(plugin->actions());
0258 
0259         for (QList<QPair<int, QAction*> >::ConstIterator it = actionsList.constBegin();
0260              it!=actionsList.constEnd(); ++it)
0261         {
0262             QAction* const pluginAction = (*it).second;
0263             qDebug()<<pluginAction->text();
0264 
0265             if ( pluginAction->text() != actionText )
0266                 continue;
0267 
0268             qDebug() << QString::fromLatin1("Found action \"%1\" in library \"%2\", will now call it.").arg(actionText).arg((*info)->library());
0269 
0270             // call the action:
0271             pluginAction->trigger();
0272             qDebug() << QString::fromLatin1("Plugin is done.");
0273             foundAction = true;
0274 
0275             break;
0276         }
0277     }
0278 
0279     if (!foundAction)
0280         qDebug() << QString::fromLatin1("Could not find action \"%1\".").arg(actionText);
0281 
0282     return foundAction;
0283 }
0284 
0285 int main(int argc, char* argv[])
0286 {
0287 #ifdef HAVE_KEXIV2
0288     KExiv2Iface::KExiv2::initializeExiv2();
0289 #endif
0290 
0291     QApplication app(argc, argv);
0292     app.setApplicationName(QLatin1String("kipicmd"));
0293     app.setApplicationVersion(QLatin1String(KIPI_VERSION_STRING));
0294     app.setOrganizationName(QLatin1String("http://www.kde.org"));
0295     app.setWindowIcon(QIcon(QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0296                                                    QLatin1String(":/images/kipi-icon.svg"))));
0297 
0298     QCommandLineParser parser;
0299     parser.addVersionOption();
0300     parser.addHelpOption();
0301     parser.setApplicationDescription(QLatin1String("kipi CLI host test application to run kipi tool as stand alone"));
0302 
0303     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("list"),           QLatin1String("List the available plugins")));
0304     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("w"),              QLatin1String("Wait until non-modal dialogs are closed")));
0305     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("l"),              QLatin1String("Library name of plugin to use"),             QLatin1String("library")));
0306     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("a"),              QLatin1String("Action to call"),                            QLatin1String("action")));
0307     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("i"),              QLatin1String("Selected images"),                           QLatin1String("selectedimages")));
0308     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("c"),              QLatin1String("Selected collections"),                       QLatin1String("selectedcollections")));
0309     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("allc"),           QLatin1String("All collections"),                           QLatin1String("allcollections")));
0310     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("+[images]"),      QLatin1String("List of images")));
0311     parser.addOption(QCommandLineOption(QStringList() << QLatin1String("+[collections]"), QLatin1String("List of collections")));
0312     parser.process(app);
0313 
0314     KipiInterface* const kipiInterface = new KipiInterface(&app);
0315 
0316     PluginLoader* const loader = new PluginLoader(nullptr);
0317     loader->setInterface(kipiInterface);
0318     loader->init();
0319 
0320     QList<QUrl> listSelectedImages;
0321     QList<QUrl> listSelectedAlbums;
0322     QList<QUrl> listAllAlbums;
0323 
0324     // Determine which with list we start
0325 
0326     QList<QUrl>* startList = nullptr;
0327 
0328     if (parser.isSet(QString::fromLatin1("i")))
0329     {
0330         startList = &listSelectedImages;
0331         startList->append(QUrl::fromLocalFile(parser.value(QString::fromLatin1("i"))));
0332     }
0333     else if (parser.isSet(QString::fromLatin1("c")))
0334     {
0335         startList = &listSelectedAlbums;
0336         startList->append(QUrl::fromLocalFile(parser.value(QString::fromLatin1("c"))));
0337     }
0338     else if (parser.isSet(QString::fromLatin1("allc")))
0339     {
0340         startList = &listAllAlbums;
0341         startList->append(QUrl::fromLocalFile(parser.value(QString::fromLatin1("allc"))));
0342     }
0343 
0344     if (startList)
0345     {
0346         qDebug() << "startList: " << *startList;
0347     }
0348     else
0349     {
0350         qDebug() << "startList: is null";
0351     }
0352 
0353     qDebug() << "parser: "    << parser.optionNames();
0354 
0355     // Append the remaining arguments to the lists
0356 
0357     const QStringList args = parser.positionalArguments();
0358 
0359     for (int i = 0; i < args.count(); ++i)
0360     {
0361         const QString argValue = args.value(i);
0362 
0363         if (argValue == QString::fromLatin1("-i"))
0364         {
0365             startList = &listSelectedImages;
0366         }
0367         else if (argValue == QString::fromLatin1("-c"))
0368         {
0369             startList = &listSelectedAlbums;
0370         }
0371         else if (argValue == QString::fromLatin1("-allc"))
0372         {
0373             startList = &listAllAlbums;
0374         }
0375         else
0376         {
0377             if (startList == nullptr)
0378             {
0379                 qCritical() << "StartList is null.\n"
0380                                "Please specify how the filenames you provided should be used.";
0381                 return 0;
0382             }
0383             else
0384             {
0385                 startList->append(QUrl::fromLocalFile(args.value(i)));
0386             }
0387         }
0388     }
0389 
0390     qDebug() << "listSelectedImages:" << listSelectedImages;
0391     qDebug() << "listSelectedAlbums:" << listSelectedAlbums;
0392     qDebug() << "listAllAlbums:"      << listAllAlbums;
0393 
0394     kipiInterface->addSelectedImages(listSelectedImages);
0395     kipiInterface->addSelectedAlbums(listSelectedAlbums);
0396     kipiInterface->addAlbums(listAllAlbums);
0397 
0398     // determine whether only one plugin should be loaded
0399 
0400     const QString nameOfOnlyOnePluginToLoad = parser.value(QString::fromLatin1("l"));
0401 
0402     // determine what to do
0403 
0404     int returnValue    = 0;
0405     bool startedPlugin = false;
0406 
0407     if ( parser.isSet(QString::fromLatin1("list")) )
0408     {
0409         if (!ListPlugins( nameOfOnlyOnePluginToLoad ))
0410         {
0411             returnValue = 1;
0412         }
0413     }
0414     else if ( parser.isSet(QString::fromLatin1("a")) )
0415     {
0416         const QString action = parser.value(QString::fromLatin1("a"));
0417 
0418         qDebug() << action;
0419 
0420         if ( !CallAction( action, nameOfOnlyOnePluginToLoad ) )
0421         {
0422             returnValue = 1;
0423         }
0424         else
0425         {
0426             startedPlugin = true;
0427         }
0428     }
0429     else
0430     {
0431         qCritical() << "No argument specified: either use --list,\n"
0432                        "or specify an action to be called.\n"
0433                        "Example : ./kipicmd -w -lkipiplugin_kxmlhelloworld -a\"KXML Hello World Image...\" -i ~/Images/*";
0434         return 0;
0435     }
0436 
0437     int ret = returnValue;
0438 
0439     if (startedPlugin && parser.isSet(QString::fromLatin1("w")))
0440     {
0441         ret = app.exec();
0442     }
0443 
0444 #ifdef HAVE_KEXIV2
0445     KExiv2Iface::KExiv2::cleanupExiv2();
0446 #endif
0447 
0448     return ret;
0449 }