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 }