diff --git a/Applications/PluginGenerator/CMakeLists.txt b/Applications/PluginGenerator/CMakeLists.txt index e47f4d36ea..1a082835e4 100644 --- a/Applications/PluginGenerator/CMakeLists.txt +++ b/Applications/PluginGenerator/CMakeLists.txt @@ -1,23 +1,69 @@ -find_package(CTK REQUIRED) +cmake_minimum_required(VERSION 2.8.4) + +project(MITKPluginGenerator) + +set(VERSION_MAJOR 1) +set(VERSION_MINOR 0) +set(VERSION_PATCH 0) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + set(standalone_build 1) +else() + set(standalone_build 0) +endif() + +#----------------------------------------------------------------------------- +# Prerequesites +#----------------------------------------------------------------------------- + +set(QT_DONT_USE_QTGUI 1) +find_package(Qt 4.6.2 REQUIRED) include(${QT_USE_FILE}) -include(${CTK_USE_FILE}) -set(src_files PluginGenerator.cpp) +#----------------------------------------------------------------------------- +# Executable +#----------------------------------------------------------------------------- + +set(src_files + PluginGenerator.cpp + ctkCommandLineParser.cpp +) +qt4_wrap_cpp(src_files ctkCommandLineParser.h) qt4_add_resources(src_files plugin_template.qrc project_template.qrc) add_executable(PluginGenerator ${src_files}) -target_link_libraries(PluginGenerator ${CTK_LIBRARIES} ${QT_LIBRARIES}) +target_link_libraries(PluginGenerator ${QT_LIBRARIES}) -# subproject support -add_dependencies(MITK-CoreUI PluginGenerator) +if(NOT standalone_build) + # subproject support + add_dependencies(MITK-CoreUI PluginGenerator) +endif() + +#----------------------------------------------------------------------------- +# Win32 Convenience +#----------------------------------------------------------------------------- -if(WIN32) +if(WIN32 AND NOT standalone_build) file(TO_NATIVE_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}" native_runtime_dir) add_custom_target(NewPlugin start "MITK PluginGenerator" /D "${native_runtime_dir}" cmd /K PluginGenerator.exe -h DEPENDS PluginGenerator) endif() -# Test the plugin generator -include(mitkTestPluginGenerator) +#----------------------------------------------------------------------------- +# Testing +#----------------------------------------------------------------------------- + +if(NOT standalone_build) + # Test the plugin generator + include(mitkTestPluginGenerator) +endif() + +#----------------------------------------------------------------------------- +# Packaging support +#----------------------------------------------------------------------------- + +if(standalone_build) + include(SetupPackaging.cmake) +endif() diff --git a/Applications/PluginGenerator/PluginGenerator.cpp b/Applications/PluginGenerator/PluginGenerator.cpp index a808901f9b..7663f9b7d1 100644 --- a/Applications/PluginGenerator/PluginGenerator.cpp +++ b/Applications/PluginGenerator/PluginGenerator.cpp @@ -1,402 +1,402 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/ for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ -#include +#include "ctkCommandLineParser.h" #include #include #include #include #include #include #include #include #include bool readAnswer(char defaultAnswer) { std::string line; std::cin >> std::noskipws >> line; // consume the new line character std::cin.clear(); std::cin.ignore(std::numeric_limits::max(), '\n'); char answer = defaultAnswer; if (!line.empty() && line[0] != '\n') { answer = std::tolower(line[0]); } if (answer == 'y') return true; if (answer == 'n') return false; if (defaultAnswer == 'y') return true; return false; } void createFilePathMapping(const QString& templateName, const QString& baseInDir, const QString& baseOutDir, QHash& fileNameMapping) { QFileInfo info(templateName); if (info.isDir()) { QStringList subEntries = QDir(templateName).entryList(); foreach(QString subEntry, subEntries) { createFilePathMapping(templateName + "/" + subEntry, baseInDir, baseOutDir, fileNameMapping); } return; } fileNameMapping[templateName] = QString(templateName).replace(baseInDir, baseOutDir); } QHash createTemplateFileMapping(const QString& qrcBase, const QString& baseOutDir, const QHash& fileNameMapping) { QHash filePathMapping; createFilePathMapping(qrcBase, qrcBase, baseOutDir, filePathMapping); QMutableHashIterator i(filePathMapping); while(i.hasNext()) { i.next(); QHashIterator j(fileNameMapping); while(j.hasNext()) { j.next(); i.setValue(i.value().replace(j.key(), j.value())); } } return filePathMapping; } bool generateFiles(const QHash& parameters, const QHash& filePathMapping) { QHashIterator paths(filePathMapping); while(paths.hasNext()) { paths.next(); QFile templ(paths.key()); templ.open(QIODevice::ReadOnly); QByteArray templContent = templ.readAll(); QHashIterator i(parameters); while (i.hasNext()) { i.next(); templContent.replace(i.key(), QByteArray(i.value().toLatin1())); } QFile outTempl(paths.value()); QDir dir(QFileInfo(outTempl).dir()); if (!dir.exists()) { if (!dir.mkpath(dir.absolutePath())) { qCritical() << "Could not create directory" << dir.absolutePath(); return EXIT_FAILURE; } } if (!outTempl.open(QIODevice::WriteOnly)) { qCritical() << outTempl.errorString(); return false; } outTempl.write(templContent); } return true; } int main(int argc, char** argv) { QCoreApplication app(argc, argv); app.setApplicationName("PluginGenerator"); app.setOrganizationName("DKFZ"); ctkCommandLineParser parser; // Use Unix-style argument names parser.setArgumentPrefix("--", "-"); parser.setStrictModeEnabled(true); // Add command line argument names parser.addArgument("help", "h", QVariant::Bool, "Show this help text"); parser.addArgument("out-dir", "o", QVariant::String, "Output directory", QDir::tempPath()); parser.addArgument("license", "l", QVariant::String, "Path to a file containing license information", ":/MITKLicense.txt"); parser.addArgument("vendor", "v", QVariant::String, "The vendor of the generated code", "DKFZ, Medical and Biological Informatics"); parser.addArgument("quiet", "q", QVariant::Bool, "Do not print additional information"); parser.addArgument("confirm-all", "y", QVariant::Bool, "Answer all questions with 'yes'"); parser.beginGroup("Plug-in options"); parser.addArgument("plugin-symbolic-name", "ps", QVariant::String, "The plugin's symbolic name"); parser.setExactMatchRegularExpression("-ps", "^[a-zA-Z]+\\.[a-zA-Z0-9._]+[^\\.]$", "Symbolic name invalid"); parser.addArgument("plugin-name", "pn", QVariant::String, "The plug-in's human readable name"); parser.beginGroup("Plug-in View options"); parser.addArgument("view-class", "vc", QVariant::String, "The View's' class name"); parser.addArgument("view-name", "vn", QVariant::String, "The View's human readable name"); parser.beginGroup("Project options"); parser.addArgument("project-copyright", "", QVariant::String, "Path to a file containing copyright information", ":/MITKCopyright.txt"); parser.addArgument("project-name", "", QVariant::String, "The project name"); parser.setExactMatchRegularExpression("--project-name", "^[a-zA-Z_\\-]+$", "Project name invalid"); parser.addArgument("project-app-name", "", QVariant::String, "The application name"); parser.setExactMatchRegularExpression("--project-app-name", "^[a-zA-Z_\\-]+$", "Project application name invalid"); parser.endGroup(); // Parse the command line arguments bool ok = false; QHash parsedArgs = parser.parseArguments(QCoreApplication::arguments(), &ok); if (!ok) { QTextStream(stderr, QIODevice::WriteOnly) << "Error parsing arguments: " << parser.errorString() << "\n"; return EXIT_FAILURE; } QTextStream out(stdout, QIODevice::WriteOnly); // Show a help message if (parsedArgs.contains("help")) { out << "A CTK plug-in generator for MITK\n\n" << parser.helpText(); return EXIT_SUCCESS; } // Check arguments // Project options QString projectName = parsedArgs["project-name"].toString(); QString projectAppName = parsedArgs["project-app-name"].toString(); QString copyrightPath = QDir::fromNativeSeparators(parsedArgs["project-copyright"].toString()); bool createProject = !projectName.isEmpty(); if (createProject && projectAppName.isEmpty()) { projectAppName = projectName; } QString pluginSymbolicName = parsedArgs["plugin-symbolic-name"].toString(); if (pluginSymbolicName.isEmpty()) { qCritical() << "Required argument 'plugin-symbolic-name' missing."; return EXIT_FAILURE; } QString pluginTarget(pluginSymbolicName); pluginTarget.replace('.', '_'); QString outDir = QDir::fromNativeSeparators(parsedArgs["out-dir"].toString()); QString licensePath = QDir::fromNativeSeparators(parsedArgs["license"].toString()); QString pluginExportDirective = pluginSymbolicName.split('.').last().toUpper() + "_EXPORT"; QString pluginName = parsedArgs["plugin-name"].toString(); if (pluginName.isEmpty()) { QStringList toks = pluginSymbolicName.split('.'); pluginName = toks.last(); pluginName[0] = pluginName[0].toUpper(); } QString vendor = parsedArgs["vendor"].toString(); QString viewName = parsedArgs["view-name"].toString(); if (viewName.isEmpty()) { qCritical() << "Required argument 'view-name' missing."; return EXIT_FAILURE; } QStringList toks = viewName.split(QRegExp("\\s"), QString::SkipEmptyParts); QString viewClass = parsedArgs["view-class"].toString(); if (viewClass.isEmpty()) { foreach(QString tok, toks) { QString tmp = tok; tmp[0] = tmp[0].toUpper(); viewClass += tmp; } } QString viewId; if (viewId.isEmpty()) { viewId = "org.mitk.views."; foreach(QString tok, toks) { viewId += tok.toLower(); } } bool quiet = parsedArgs.contains("quiet"); bool autoConfirm = parsedArgs.contains("confirm-all"); if (!outDir.endsWith('/')) outDir += '/'; if (createProject) outDir += projectName; else outDir += pluginSymbolicName; // Print the collected information if(!quiet) { if (createProject) { out << "Using the following information to create a project:\n\n" << " Project Name: " << projectName << '\n' << " Application Name: " << projectAppName << '\n' << " Copyright File: " << QDir::toNativeSeparators(copyrightPath) << '\n'; } else { out << "Using the following information to create a plug-in:\n\n"; } out << " License File: " << QDir::toNativeSeparators(licensePath) << '\n' << " Plugin-SymbolicName: " << pluginSymbolicName << '\n' << " Plugin-Name: " << pluginName << '\n' << " Plugin-Vendor: " << vendor << '\n' << " View Name: " << viewName << '\n' << " View Id: " << viewId << '\n' << " View Class: " << viewClass << '\n' << '\n' << "Create in: " << outDir << '\n' << '\n'; if (!autoConfirm) out << "Continue [Y/n]? "; out.flush(); if(!autoConfirm && !readAnswer('y')) { out << "Aborting.\n"; return EXIT_SUCCESS; } } // Check the output directory if (!QDir(outDir).exists()) { if (!autoConfirm) { out << "Directory '" << outDir << "' does not exist. Create it [Y/n]? "; out.flush(); } if (autoConfirm || readAnswer('y')) { if (!QDir().mkpath(outDir)) { qCritical() << "Could not create directory:" << outDir; return EXIT_FAILURE; } } else { out << "Aborting.\n"; return EXIT_SUCCESS; } } if (!QDir(outDir).entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty()) { if (!autoConfirm) { out << "Directory '" << outDir << "' is not empty. Continue [y/N]? "; out.flush(); } if (!autoConfirm && !readAnswer('n')) { out << "Aborting.\n"; return EXIT_SUCCESS; } } // Extract the license text QFile licenseFile(licensePath); if (!licenseFile.open(QIODevice::ReadOnly)) { qCritical() << "Cannot open file" << licenseFile.fileName(); return EXIT_FAILURE; } QString licenseText = licenseFile.readAll(); licenseFile.close(); QHash parameters; if (createProject) { // Extract the copyright QFile copyrightFile(copyrightPath); if (!copyrightFile.open(QIODevice::ReadOnly)) { qCritical() << "Cannot open file" << copyrightFile.fileName(); return EXIT_FAILURE; } QString copyrighText = copyrightFile.readAll(); copyrightFile.close(); parameters["$(copyright)"] = copyrighText; parameters["$(project-name)"] = projectName; parameters["$(project-app-name)"] = projectAppName; parameters["$(project-plugins)"] = QString("Plugins/") + pluginSymbolicName + ":ON"; QStringList toks = pluginTarget.split("_"); QString projectPluginBase = toks[0] + "_" + toks[1]; parameters["$(project-plugin-base)"] = projectPluginBase; } parameters["$(license)"] = licenseText; parameters["$(plugin-name)"] = pluginName; parameters["$(plugin-symbolic-name)"] = pluginSymbolicName; parameters["$(vendor)"] = vendor; parameters["$(plugin-target)"] = pluginTarget; parameters["$(plugin-export-directive)"] = pluginExportDirective; parameters["$(view-id)"] = viewId; parameters["$(view-name)"] = viewName; parameters["$(view-file-name)"] = viewClass; parameters["$(view-class-name)"] = viewClass; if (createProject) { QHash projectFileNameMapping; projectFileNameMapping["TemplateApp"] = projectAppName; QHash filePathMapping = createTemplateFileMapping(":/ProjectTemplate", outDir, projectFileNameMapping); generateFiles(parameters, filePathMapping); } QHash pluginFileNameMapping; pluginFileNameMapping["QmitkTemplateView"] = viewClass; if (createProject) { if (!outDir.endsWith('/')) outDir += '/'; outDir += "Plugins/" + pluginSymbolicName; } QHash filePathMapping = createTemplateFileMapping(":/PluginTemplate", outDir, pluginFileNameMapping); generateFiles(parameters, filePathMapping); return EXIT_SUCCESS; } diff --git a/Applications/PluginGenerator/SetupPackaging.cmake b/Applications/PluginGenerator/SetupPackaging.cmake new file mode 100644 index 0000000000..794d78f7b2 --- /dev/null +++ b/Applications/PluginGenerator/SetupPackaging.cmake @@ -0,0 +1,50 @@ +#----------------------------------------------------------------------------- +# Installation +#----------------------------------------------------------------------------- + +install(TARGETS PluginGenerator DESTINATION .) + +install(CODE " + set(DIRS + ${QT_LIBRARY_DIR} + ${QT_LIBRARY_DIR}/../bin + ${CTK_LIBRARY_DIRS} + ) + + include(BundleUtilities) + + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/PluginGenerator\" \"\" \"\${DIRS}\") +") + +#----------------------------------------------------------------------------- +# Packaging +#----------------------------------------------------------------------------- + +# +# First, set the generator variable +# +if(WIN32) + set(CPACK_GENERATOR ZIP) +elseif(APPLE) + set(CPACK_GENERATOR DragNDrop) +else() + set(CPACK_GENERATOR TGZ) +endif() + +# include required mfc libraries +include(InstallRequiredSystemLibraries) + +set(CPACK_PACKAGE_NAME "MITKPluginGenerator") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MITK PluginGenerator bootstraps MITK-based projects") +set(CPACK_PACKAGE_VENDOR "German Cancer Research Center (DKFZ)") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/MITKCopyright.txt") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/MITKCopyright.txt") +set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}") + +# tell cpack to strip all debug symbols from all files +set(CPACK_STRIP_FILES ON) + +include(CPack) + diff --git a/Applications/PluginGenerator/ctkCommandLineParser.cpp b/Applications/PluginGenerator/ctkCommandLineParser.cpp new file mode 100644 index 0000000000..39586d8812 --- /dev/null +++ b/Applications/PluginGenerator/ctkCommandLineParser.cpp @@ -0,0 +1,832 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// STL includes +#include + +// Qt includes +#include +#include +#include +#include +#include +#include + +// CTK includes +#include "ctkCommandLineParser.h" + +namespace +{ +// -------------------------------------------------------------------------- +class CommandLineParserArgumentDescription +{ +public: + CommandLineParserArgumentDescription( + const QString& longArg, const QString& longArgPrefix, + const QString& shortArg, const QString& shortArgPrefix, + QVariant::Type type, const QString& argHelp, + const QVariant& defaultValue, bool ignoreRest, + bool deprecated) + : LongArg(longArg), LongArgPrefix(longArgPrefix), + ShortArg(shortArg), ShortArgPrefix(shortArgPrefix), + ArgHelp(argHelp), IgnoreRest(ignoreRest), NumberOfParametersToProcess(0), + Deprecated(deprecated), DefaultValue(defaultValue), Value(type), ValueType(type) + { + if (defaultValue.isValid()) + { + Value = defaultValue; + } + + switch (type) + { + case QVariant::String: + { + NumberOfParametersToProcess = 1; + RegularExpression = ".*"; + } + break; + case QVariant::Bool: + { + NumberOfParametersToProcess = 0; + RegularExpression = ""; + } + break; + case QVariant::StringList: + { + NumberOfParametersToProcess = -1; + RegularExpression = ".*"; + } + break; + case QVariant::Int: + { + NumberOfParametersToProcess = 1; + RegularExpression = "-?[0-9]+"; + ExactMatchFailedMessage = "A negative or positive integer is expected."; + } + break; + default: + ExactMatchFailedMessage = QString("Type %1 not supported.").arg(static_cast(type)); + } + + } + + ~CommandLineParserArgumentDescription(){} + + bool addParameter(const QString& value); + + QString helpText(int fieldWidth, const char charPad, const QString& settingsValue = ""); + + QString LongArg; + QString LongArgPrefix; + QString ShortArg; + QString ShortArgPrefix; + QString ArgHelp; + bool IgnoreRest; + int NumberOfParametersToProcess; + QString RegularExpression; + QString ExactMatchFailedMessage; + bool Deprecated; + + QVariant DefaultValue; + QVariant Value; + QVariant::Type ValueType; +}; + +// -------------------------------------------------------------------------- +bool CommandLineParserArgumentDescription::addParameter(const QString& value) +{ + if (!RegularExpression.isEmpty()) + { + // Validate value + QRegExp regexp(this->RegularExpression); + if (!regexp.exactMatch(value)) + { + return false; + } + } + + switch (Value.type()) + { + case QVariant::String: + { + Value.setValue(value); + } + break; + case QVariant::Bool: + { + Value.setValue(!QString::compare(value, "true", Qt::CaseInsensitive)); + } + break; + case QVariant::StringList: + { + if (Value.isNull()) + { + QStringList list; + list << value; + Value.setValue(list); + } + else + { + QStringList list = Value.toStringList(); + list << value; + Value.setValue(list); + } + } + break; + case QVariant::Int: + { + Value.setValue(value.toInt()); + } + break; + default: + return false; + } + + return true; +} + +// -------------------------------------------------------------------------- +QString CommandLineParserArgumentDescription::helpText(int fieldWidth, const char charPad, + const QString& settingsValue) +{ + QString text; + QTextStream stream(&text); + stream.setFieldAlignment(QTextStream::AlignLeft); + stream.setPadChar(charPad); + + QString shortAndLongArg; + if (!this->ShortArg.isEmpty()) + { + shortAndLongArg += QString(" %1%2").arg(this->ShortArgPrefix).arg(this->ShortArg); + } + + if (!this->LongArg.isEmpty()) + { + if (this->ShortArg.isEmpty()) + { + shortAndLongArg.append(" "); + } + else + { + shortAndLongArg.append(", "); + } + + shortAndLongArg += QString("%1%2").arg(this->LongArgPrefix).arg(this->LongArg); + } + + if(!this->ArgHelp.isEmpty()) + { + stream.setFieldWidth(fieldWidth); + } + + stream << shortAndLongArg; + stream.setFieldWidth(0); + stream << this->ArgHelp; + if (!settingsValue.isNull()) + { + stream << " (default: " << settingsValue << ")"; + } + else if (!this->DefaultValue.isNull()) + { + stream << " (default: " << this->DefaultValue.toString() << ")"; + } + stream << "\n"; + return text; +} + +} + +// -------------------------------------------------------------------------- +// ctkCommandLineParser::ctkInternal class + +// -------------------------------------------------------------------------- +class ctkCommandLineParser::ctkInternal +{ +public: + ctkInternal(QSettings* settings) + : Debug(false), FieldWidth(0), UseQSettings(false), + Settings(settings), MergeSettings(true), StrictMode(false) + {} + + ~ctkInternal() { qDeleteAll(ArgumentDescriptionList); } + + CommandLineParserArgumentDescription* argumentDescription(const QString& argument); + + QList ArgumentDescriptionList; + QHash ArgNameToArgumentDescriptionMap; + QMap > GroupToArgumentDescriptionListMap; + + QStringList UnparsedArguments; + QStringList ProcessedArguments; + QString ErrorString; + bool Debug; + int FieldWidth; + QString LongPrefix; + QString ShortPrefix; + QString CurrentGroup; + bool UseQSettings; + QPointer Settings; + QString DisableQSettingsLongArg; + QString DisableQSettingsShortArg; + bool MergeSettings; + bool StrictMode; +}; + +// -------------------------------------------------------------------------- +// ctkCommandLineParser::ctkInternal methods + +// -------------------------------------------------------------------------- +CommandLineParserArgumentDescription* + ctkCommandLineParser::ctkInternal::argumentDescription(const QString& argument) +{ + QString unprefixedArg = argument; + if (!LongPrefix.isEmpty() && argument.startsWith(LongPrefix)) + { + // Case when (ShortPrefix + UnPrefixedArgument) matches LongPrefix + if (argument == LongPrefix && !ShortPrefix.isEmpty() && argument.startsWith(ShortPrefix)) + { + unprefixedArg = argument.mid(ShortPrefix.length()); + } + else + { + unprefixedArg = argument.mid(LongPrefix.length()); + } + } + else if (!ShortPrefix.isEmpty() && argument.startsWith(ShortPrefix)) + { + unprefixedArg = argument.mid(ShortPrefix.length()); + } + else if (!LongPrefix.isEmpty() && !ShortPrefix.isEmpty()) + { + return 0; + } + + if (this->ArgNameToArgumentDescriptionMap.contains(unprefixedArg)) + { + return this->ArgNameToArgumentDescriptionMap[unprefixedArg]; + } + return 0; +} + +// -------------------------------------------------------------------------- +// ctkCommandLineParser methods + +// -------------------------------------------------------------------------- +ctkCommandLineParser::ctkCommandLineParser(QObject* newParent) : Superclass(newParent) +{ + this->Internal = new ctkInternal(0); +} + +// -------------------------------------------------------------------------- +ctkCommandLineParser::ctkCommandLineParser(QSettings* settings, QObject* newParent) : + Superclass(newParent) +{ + this->Internal = new ctkInternal(settings); +} + +// -------------------------------------------------------------------------- +ctkCommandLineParser::~ctkCommandLineParser() +{ + delete this->Internal; +} + +// -------------------------------------------------------------------------- +QHash ctkCommandLineParser::parseArguments(const QStringList& arguments, + bool* ok) +{ + // Reset + this->Internal->UnparsedArguments.clear(); + this->Internal->ProcessedArguments.clear(); + this->Internal->ErrorString.clear(); + foreach (CommandLineParserArgumentDescription* desc, + this->Internal->ArgumentDescriptionList) + { + desc->Value = QVariant(desc->ValueType); + if (desc->DefaultValue.isValid()) + { + desc->Value = desc->DefaultValue; + } + } + + bool error = false; + bool ignoreRest = false; + bool useSettings = this->Internal->UseQSettings; + CommandLineParserArgumentDescription * currentArgDesc = 0; + QList parsedArgDescriptions; + for(int i = 1; i < arguments.size(); ++i) + { + QString argument = arguments.at(i); + if (this->Internal->Debug) { qDebug() << "Processing" << argument; } + + // should argument be ignored ? + if (ignoreRest) + { + if (this->Internal->Debug) + { + qDebug() << " Skipping: IgnoreRest flag was been set"; + } + this->Internal->UnparsedArguments << argument; + continue; + } + + // Skip if the argument does not start with the defined prefix + if (!(argument.startsWith(this->Internal->LongPrefix) + || argument.startsWith(this->Internal->ShortPrefix))) + { + if (this->Internal->StrictMode) + { + this->Internal->ErrorString = QString("Unknown argument %1").arg(argument); + error = true; + break; + } + if (this->Internal->Debug) + { + qDebug() << " Skipping: It does not start with the defined prefix"; + } + this->Internal->UnparsedArguments << argument; + continue; + } + + // Skip if argument has already been parsed ... + if (this->Internal->ProcessedArguments.contains(argument)) + { + if (this->Internal->StrictMode) + { + this->Internal->ErrorString = QString("Argument %1 already processed !").arg(argument); + error = true; + break; + } + if (this->Internal->Debug) + { + qDebug() << " Skipping: Already processed !"; + } + continue; + } + + // Retrieve corresponding argument description + currentArgDesc = this->Internal->argumentDescription(argument); + + // Is there a corresponding argument description ? + if (currentArgDesc) + { + // If the argument is deprecated, print the help text but continue processing + if (currentArgDesc->Deprecated) + { + qWarning().nospace() << "Deprecated argument " << argument << ": " << currentArgDesc->ArgHelp; + } + else + { + parsedArgDescriptions.push_back(currentArgDesc); + } + + // Is the argument the special "disable QSettings" argument? + if ((!currentArgDesc->LongArg.isEmpty() && currentArgDesc->LongArg == this->Internal->DisableQSettingsLongArg) + || (!currentArgDesc->ShortArg.isEmpty() && currentArgDesc->ShortArg == this->Internal->DisableQSettingsShortArg)) + { + useSettings = false; + } + + this->Internal->ProcessedArguments << currentArgDesc->ShortArg << currentArgDesc->LongArg; + int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess; + ignoreRest = currentArgDesc->IgnoreRest; + if (this->Internal->Debug && ignoreRest) + { + qDebug() << " IgnoreRest flag is True"; + } + + // Is the number of parameters associated with the argument being processed known ? + if (numberOfParametersToProcess == 0) + { + currentArgDesc->addParameter("true"); + } + else if (numberOfParametersToProcess > 0) + { + QString missingParameterError = + "Argument %1 has %2 value(s) associated whereas exacly %3 are expected."; + for(int j=1; j <= numberOfParametersToProcess; ++j) + { + if (i + j >= arguments.size()) + { + this->Internal->ErrorString = + missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess); + if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; } + if (ok) { *ok = false; } + return QHash(); + } + QString parameter = arguments.at(i + j); + if (this->Internal->Debug) + { + qDebug() << " Processing parameter" << j << ", value:" << parameter; + } + if (this->argumentAdded(parameter)) + { + this->Internal->ErrorString = + missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess); + if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; } + if (ok) { *ok = false; } + return QHash(); + } + if (!currentArgDesc->addParameter(parameter)) + { + this->Internal->ErrorString = QString( + "Value(s) associated with argument %1 are incorrect. %2"). + arg(argument).arg(currentArgDesc->ExactMatchFailedMessage); + + if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; } + if (ok) { *ok = false; } + return QHash(); + } + } + // Update main loop increment + i = i + numberOfParametersToProcess; + } + else if (numberOfParametersToProcess == -1) + { + if (this->Internal->Debug) + { + qDebug() << " Proccessing StringList ..."; + } + int j = 1; + while(j + i < arguments.size()) + { + if (this->argumentAdded(arguments.at(j + i))) + { + if (this->Internal->Debug) + { + qDebug() << " No more parameter for" << argument; + } + break; + } + QString parameter = arguments.at(j + i); + if (this->Internal->Debug) + { + qDebug() << " Processing parameter" << j << ", value:" << parameter; + } + if (!currentArgDesc->addParameter(parameter)) + { + this->Internal->ErrorString = QString( + "Value(s) associated with argument %1 are incorrect. %2"). + arg(argument).arg(currentArgDesc->ExactMatchFailedMessage); + + if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; } + if (ok) { *ok = false; } + return QHash(); + } + j++; + } + // Update main loop increment + i = i + j; + } + } + else + { + if (this->Internal->StrictMode) + { + this->Internal->ErrorString = QString("Unknown argument %1").arg(argument); + error = true; + break; + } + if (this->Internal->Debug) + { + qDebug() << " Skipping: Unknown argument"; + } + this->Internal->UnparsedArguments << argument; + } + } + + if (ok) + { + *ok = !error; + } + + QSettings* settings = 0; + if (this->Internal->UseQSettings && useSettings) + { + if (this->Internal->Settings) + { + settings = this->Internal->Settings; + } + else + { + // Use a default constructed QSettings instance + settings = new QSettings(); + } + } + + QHash parsedArguments; + QListIterator it(this->Internal->ArgumentDescriptionList); + while (it.hasNext()) + { + QString key; + CommandLineParserArgumentDescription* desc = it.next(); + if (!desc->LongArg.isEmpty()) + { + key = desc->LongArg; + } + else + { + key = desc->ShortArg; + } + + if (parsedArgDescriptions.contains(desc)) + { + // The argument was supplied on the command line, so use the given value + + if (this->Internal->MergeSettings && settings) + { + // Merge with QSettings + QVariant settingsVal = settings->value(key); + + if (desc->ValueType == QVariant::StringList && + settingsVal.canConvert(QVariant::StringList)) + { + QStringList stringList = desc->Value.toStringList(); + stringList.append(settingsVal.toStringList()); + parsedArguments.insert(key, stringList); + } + else + { + // do a normal insert + parsedArguments.insert(key, desc->Value); + } + } + else + { + // No merging, just insert all user values + parsedArguments.insert(key, desc->Value); + } + } + else + { + if (settings) + { + // If there is a valid QSettings entry for the argument, use the value + QVariant settingsVal = settings->value(key, desc->Value); + if (!settingsVal.isNull()) + { + parsedArguments.insert(key, settingsVal); + } + } + else + { + // Just insert the arguments with valid default values + if (!desc->Value.isNull()) + { + parsedArguments.insert(key, desc->Value); + } + } + } + } + + // If we created a default QSettings instance, delete it + if (settings && !this->Internal->Settings) + { + delete settings; + } + + return parsedArguments; +} + +// ------------------------------------------------------------------------- +QHash ctkCommandLineParser::parseArguments(int argc, char** argv, bool* ok) +{ + QStringList arguments; + + // Create a QStringList of arguments + for(int i = 0; i < argc; ++i) + { + arguments << argv[i]; + } + + return this->parseArguments(arguments, ok); +} + +// ------------------------------------------------------------------------- +QString ctkCommandLineParser::errorString() const +{ + return this->Internal->ErrorString; +} + +// ------------------------------------------------------------------------- +const QStringList& ctkCommandLineParser::unparsedArguments() const +{ + return this->Internal->UnparsedArguments; +} + +// -------------------------------------------------------------------------- +void ctkCommandLineParser::addArgument(const QString& longarg, const QString& shortarg, + QVariant::Type type, const QString& argHelp, + const QVariant& defaultValue, bool ignoreRest, + bool deprecated) +{ + Q_ASSERT_X(!(longarg.isEmpty() && shortarg.isEmpty()), "addArgument", + "both long and short argument names are empty"); + if (longarg.isEmpty() && shortarg.isEmpty()) { return; } + + Q_ASSERT_X(!defaultValue.isValid() || defaultValue.type() == type, "addArgument", + "defaultValue type does not match"); + if (defaultValue.isValid() && defaultValue.type() != type) + throw std::logic_error("The QVariant type of defaultValue does not match the specified type"); + + /* Make sure it's not already added */ + bool added = this->Internal->ArgNameToArgumentDescriptionMap.contains(longarg); + Q_ASSERT_X(!added, "addArgument", "long argument already added"); + if (added) { return; } + + added = this->Internal->ArgNameToArgumentDescriptionMap.contains(shortarg); + Q_ASSERT_X(!added, "addArgument", "short argument already added"); + if (added) { return; } + + CommandLineParserArgumentDescription* argDesc = + new CommandLineParserArgumentDescription(longarg, this->Internal->LongPrefix, + shortarg, this->Internal->ShortPrefix, type, + argHelp, defaultValue, ignoreRest, deprecated); + + int argWidth = 0; + if (!longarg.isEmpty()) + { + this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc; + argWidth += longarg.length() + this->Internal->LongPrefix.length(); + } + if (!shortarg.isEmpty()) + { + this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc; + argWidth += shortarg.length() + this->Internal->ShortPrefix.length() + 2; + } + argWidth += 5; + + // Set the field width for the arguments + if (argWidth > this->Internal->FieldWidth) + { + this->Internal->FieldWidth = argWidth; + } + + this->Internal->ArgumentDescriptionList << argDesc; + this->Internal->GroupToArgumentDescriptionListMap[this->Internal->CurrentGroup] << argDesc; +} + +// -------------------------------------------------------------------------- +void ctkCommandLineParser::addDeprecatedArgument( + const QString& longarg, const QString& shortarg, const QString& argHelp) +{ + addArgument(longarg, shortarg, QVariant::StringList, argHelp, QVariant(), false, true); +} + +// -------------------------------------------------------------------------- +bool ctkCommandLineParser::setExactMatchRegularExpression( + const QString& argument, const QString& expression, const QString& exactMatchFailedMessage) +{ + CommandLineParserArgumentDescription * argDesc = + this->Internal->argumentDescription(argument); + if (!argDesc) + { + return false; + } + + if (argDesc->Value.type() == QVariant::Bool) + { + return false; + } + argDesc->RegularExpression = expression; + argDesc->ExactMatchFailedMessage = exactMatchFailedMessage; + return true; +} + +// -------------------------------------------------------------------------- +int ctkCommandLineParser::fieldWidth() const +{ + return this->Internal->FieldWidth; +} + +// -------------------------------------------------------------------------- +void ctkCommandLineParser::beginGroup(const QString& description) +{ + this->Internal->CurrentGroup = description; +} + +// -------------------------------------------------------------------------- +void ctkCommandLineParser::endGroup() +{ + this->Internal->CurrentGroup.clear(); +} + +// -------------------------------------------------------------------------- +void ctkCommandLineParser::enableSettings(const QString& disableLongArg, const QString& disableShortArg) +{ + this->Internal->UseQSettings = true; + this->Internal->DisableQSettingsLongArg = disableLongArg; + this->Internal->DisableQSettingsShortArg = disableShortArg; +} + +// -------------------------------------------------------------------------- +void ctkCommandLineParser::mergeSettings(bool merge) +{ + this->Internal->MergeSettings = merge; +} + +// -------------------------------------------------------------------------- +bool ctkCommandLineParser::settingsEnabled() const +{ + return this->Internal->UseQSettings; +} + +// -------------------------------------------------------------------------- +QString ctkCommandLineParser::helpText(const char charPad) const +{ + QString text; + QTextStream stream(&text); + + QList deprecatedArgs; + + // Loop over grouped argument descriptions + QMapIterator > it( + this->Internal->GroupToArgumentDescriptionListMap); + while(it.hasNext()) + { + it.next(); + if (!it.key().isEmpty()) + { + stream << "\n" << it.key() << "\n"; + } + foreach(CommandLineParserArgumentDescription* argDesc, it.value()) + { + if (argDesc->Deprecated) + { + deprecatedArgs << argDesc; + } + else + { + // Extract associated value from settings if any + QString settingsValue; + if (this->Internal->Settings) + { + QString key; + if (!argDesc->LongArg.isEmpty()) + { + key = argDesc->LongArg; + } + else + { + key = argDesc->ShortArg; + } + settingsValue = this->Internal->Settings->value(key).toString(); + } + stream << argDesc->helpText(this->Internal->FieldWidth, charPad, settingsValue); + } + } + } + + if (!deprecatedArgs.empty()) + { + stream << "\nDeprecated arguments:\n"; + foreach(CommandLineParserArgumentDescription* argDesc, deprecatedArgs) + { + stream << argDesc->helpText(this->Internal->FieldWidth, charPad); + } + } + + return text; +} + +// -------------------------------------------------------------------------- +bool ctkCommandLineParser::argumentAdded(const QString& argument) const +{ + return this->Internal->ArgNameToArgumentDescriptionMap.contains(argument); +} + +// -------------------------------------------------------------------------- +bool ctkCommandLineParser::argumentParsed(const QString& argument) const +{ + return this->Internal->ProcessedArguments.contains(argument); +} + +// -------------------------------------------------------------------------- +void ctkCommandLineParser::setArgumentPrefix(const QString& longPrefix, const QString& shortPrefix) +{ + this->Internal->LongPrefix = longPrefix; + this->Internal->ShortPrefix = shortPrefix; +} + +// -------------------------------------------------------------------------- +void ctkCommandLineParser::setStrictModeEnabled(bool strictMode) +{ + this->Internal->StrictMode = strictMode; +} + diff --git a/Applications/PluginGenerator/ctkCommandLineParser.h b/Applications/PluginGenerator/ctkCommandLineParser.h new file mode 100644 index 0000000000..edc7616eec --- /dev/null +++ b/Applications/PluginGenerator/ctkCommandLineParser.h @@ -0,0 +1,419 @@ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#ifndef __ctkCommandLineParser_h +#define __ctkCommandLineParser_h + +// Qt includes +#include +#include +#include + +class QSettings; + + +/** + * \ingroup Core + * + * The CTK command line parser. + * + * Use this class to add information about the command line arguments + * your program understands and to easily parse them from a given list + * of strings. + * + * This parser provides the following features: + * + *
    + *
  • Add arguments by supplying a long name and/or a short name. + * Arguments are validated using a regular expression. They can have + * a default value and a help string.
  • + *
  • Deprecated arguments.
  • + *
  • Custom regular expressions for argument validation.
  • + *
  • Set different argument name prefixes for native platform look and feel.
  • + *
  • QSettings support. Default values for arguments can be read from + * a QSettings object.
  • + *
  • Create a help text for the command line arguments with support for + * grouping arguments.
  • + *
+ * + * Here is an example how to use this class inside a main function: + * + * \code + * #include + * #include + * #include + * + * int main(int argc, char** argv) + * { + * QCoreApplication app(argc, argv); + * // This is used by QSettings + * QCoreApplication::setOrganizationName("MyOrg"); + * QCoreApplication::setApplicationName("MyApp"); + * + * ctkCommandLineParser parser; + * // Use Unix-style argument names + * parser.setArgumentPrefix("--", "-"); + * // Enable QSettings support + * parser.enableSettings("disable-settings"); + * + * // Add command line argument names + * parser.addArgument("disable-settings", "", QVariant::Bool, "Do not use QSettings"); + * parser.addArgument("help", "h", QVariant::Bool, "Show this help text"); + * parser.addArgument("search-paths", "s", QVariant::StringList, "A list of paths to search"); + * + * // Parse the command line arguments + * bool ok = false; + * QHash parsedArgs = parser.parseArguments(QCoreApplication::arguments(), &ok); + * if (!ok) + * { + * QTextStream(stderr, QIODevice::WriteOnly) << "Error parsing arguments: " + * << parser.errorString() << "\n"; + * return EXIT_FAILURE; + * } + * + * // Show a help message + * if (parsedArgs.contains("help") || parsedArgs.contains("h")) + * { + * QTextStream(stdout, QIODevice::WriteOnly) << parser.helpText(); + * return EXIT_SUCCESS; + * } + * + * // Do something + * + * return EXIT_SUCCESS; + * } + * \endcode + */ +class ctkCommandLineParser : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString errorString READ errorString) + Q_PROPERTY(QStringList unparsedArguments READ unparsedArguments) + Q_PROPERTY(bool settingsEnabled READ settingsEnabled) + +public: + + typedef QObject Superclass; + + /** + * Constructs a parser instance. + * + * If QSettings support is enabled by a call to enableSettings() + * a default constructed QSettings instance will be used when parsing + * the command line arguments. Make sure to call QCoreApplication::setOrganizationName() + * and QCoreApplication::setApplicationName() before using default + * constructed QSettings objects. + * + * @param newParent The QObject parent. + */ + ctkCommandLineParser(QObject* newParent = 0); + + /** + * Constructs a parser instance. + * + * If QSettings support is enabled by a call to enableSettings() + * the provided QSettings instance will be used. If the QSettings instance is + * zero, a default constructed QSettings instance will be used when parsing + * the command line arguments. Using a default constructed instance is usually + * what you want, if you have called QCoreApplication::setOrganizationName() + * and QCoreApplication::setApplicationName(). + * + * @param settings A QSettings instance which should be used. + * @param newParent The QObject parent. + * + * + */ + ctkCommandLineParser(QSettings* settings, QObject* newParent = 0); + + ~ctkCommandLineParser(); + + /** + * Parse a given list of command line arguments. + * + * This method parses a list of QString elements considering the known arguments + * added by calls to addArgument(). If any one of the argument + * values does not match the corresponding regular expression, + * ok is set to false and an empty QHash object is returned. + * + * The keys in the returned QHash object correspond to the long argument string, + * if it is not empty. Otherwise, the short argument string is used as key. The + * QVariant values can safely be converted to the type specified in the + * addArgument() method call. + * + * @param arguments A QStringList containing command line arguments. Usually + * given by QCoreApplication::arguments(). + * @param ok A pointer to a boolean variable. Will be set to true + * if all regular expressions matched, false otherwise. + * @return A QHash object mapping the long argument (if empty, the short one) + * to a QVariant containing the value. + */ + QHash parseArguments(const QStringList &arguments, bool* ok = 0); + + /** + * Convenient method allowing to parse a given list of command line arguments. + * @see parseArguments(const QStringList &, bool*) + */ + QHash parseArguments(int argc, char** argv, bool* ok = 0); + + /** + * Returns a detailed error description if a call to parseArguments() + * failed. + * + * @return The error description, empty if no error occured. + * @see parseArguments(const QStringList&, bool*) + */ + QString errorString() const; + + /** + * This method returns all unparsed arguments, i.e. all arguments + * for which no long or short name has been registered via a call + * to addArgument(). + * + * @see addArgument() + * + * @return A list containing unparsed arguments. + */ + const QStringList& unparsedArguments() const; + + /** + * Checks if the given argument has been added via a call + * to addArgument(). + * + * @see addArgument() + * + * @param argument The argument to be checked. + * @return true if the argument was added, false + * otherwise. + */ + Q_INVOKABLE bool argumentAdded(const QString& argument) const; + + /** + * Checks if the given argument has been parsed successfully by a previous + * call to parseArguments(). + * + * @param argument The argument to be checked. + * @return true if the argument was parsed, false + * otherwise. + */ + Q_INVOKABLE bool argumentParsed(const QString& argument) const; + + /** + * Adds a command line argument. An argument can have a long name + * (like --long-argument-name), a short name (like -l), or both. The type + * of the argument can be specified by using the type parameter. + * The following types are supported: + * + * + * + * + * + * + * + * + *
Type# of parametersDefault regular exprExample
QVariant::String1.*--test-string StringParameter
QVariant::Bool0does not apply--enable-something
QVariant::StringList-1.*--test-list string1 string2
QVariant::Int1-?[0-9]+--test-int -5
+ * + * The regular expressions are used to validate the parameters of command line + * arguments. You can restrict the valid set of parameters by calling + * setExactMatchRegularExpression() for your argument. + * + * Optionally, a help string and a default value can be provided for the argument. If + * the QVariant type of the default value does not match type, an + * exception is thrown. Arguments with default values are always returned by + * parseArguments(). + * + * You can also declare an argument deprecated, by setting deprecated + * to true. Alternatively you can add a deprecated argument by calling + * addDeprecatedArgument(). + * + * If the long or short argument has already been added, or if both are empty strings, + * the method call has no effect. + * + * @param longarg The long argument name. + * @param shortarg The short argument name. + * @param type The argument type (see the list above for supported types). + * @param argHelp A help string describing the argument. + * @param defaultValue A default value for the argument. + * @param ignoreRest All arguments after the current one will be ignored. + * @param deprecated Declares the argument deprecated. + * + * @see setExactMatchRegularExpression() + * @see addDeprecatedArgument() + * @throws std::logic_error If the QVariant type of defaultValue + * does not match type, a std::logic_error is thrown. + */ + void addArgument(const QString& longarg, const QString& shortarg, + QVariant::Type type, const QString& argHelp = QString(), + const QVariant& defaultValue = QVariant(), + bool ignoreRest = false, bool deprecated = false); + + /** + * Adds a deprecated command line argument. If a deprecated argument is provided + * on the command line, argHelp is displayed in the console and + * processing continues with the next argument. + * + * Deprecated arguments are grouped separately at the end of the help text + * returned by helpText(). + * + * @param longarg The long argument name. + * @param shortarg The short argument name. + * @param argHelp A help string describing alternatives to the deprecated argument. + */ + void addDeprecatedArgument(const QString& longarg, const QString& shortarg, + const QString& argHelp); + + /** + * Sets a custom regular expression for validating argument parameters. The method + * errorString() can be used the get the last error description. + * + * @param argument The previously added long or short argument name. + * @param expression A regular expression which the arugment parameters must match. + * @param exactMatchFailedMessage An error message explaining why the parameter did + * not match. + * + * @return true if the argument was found and the regular expression was set, + * false otherwise. + * + * @see errorString() + */ + bool setExactMatchRegularExpression(const QString& argument, const QString& expression, + const QString& exactMatchFailedMessage); + + /** + * The field width for the argument names without the help text. + * + * @return The argument names field width in the help text. + */ + int fieldWidth() const; + + /** + * Creates a help text containing properly formatted argument names and help strings + * provided by calls to addArgument(). The arguments can be grouped by + * using beginGroup() and endGroup(). + * + * @param charPad The padding character. + * @return The formatted help text. + */ + QString helpText(const char charPad = ' ') const; + + /** + * Sets the argument prefix for long and short argument names. This can be used + * to create native command line arguments without changing the calls to + * addArgument(). For example on Unix-based systems, long argument + * names start with "--" and short names with "-", while on Windows argument names + * always start with "/". + * + * Note that all methods in ctkCommandLineParser which take an argument name + * expect the name as it was supplied to addArgument. + * + * Example usage: + * + * \code + * ctkCommandLineParser parser; + * parser.setArgumentPrefix("--", "-"); + * parser.addArgument("long-argument", "l", QVariant::String); + * QStringList args; + * args << "program name" << "--long-argument Hi"; + * parser.parseArguments(args); + * \endcode + * + * @param longPrefix The prefix for long argument names. + * @param shortPrefix The prefix for short argument names. + */ + void setArgumentPrefix(const QString& longPrefix, const QString& shortPrefix); + + /** + * Begins a new group for documenting arguments. All newly added arguments via + * addArgument() will be put in the new group. You can close the + * current group by calling endGroup() or be opening a new group. + * + * Note that groups cannot be nested and all arguments which do not belong to + * a group will be listed at the top of the text created by helpText(). + * + * @param description The description of the group + */ + void beginGroup(const QString& description); + + /** + * Ends the current group. + * + * @see beginGroup(const QString&) + */ + void endGroup(); + + /** + * Enables QSettings support in ctkCommandLineParser. If an argument name is found + * in the QSettings instance with a valid QVariant, the value is considered as + * a default value and overwrites default values registered with + * addArgument(). User supplied values on the command line overwrite + * values in the QSettings instance, except for arguments with multiple parameters + * which are merged with QSettings values. Call mergeSettings(false) + * to disable merging. + * + * See ctkCommandLineParser(QSettings*) for information about how to + * supply a QSettings instance. + * + * Additionally, a long and short argument name can be specified which will disable + * QSettings support if supplied on the command line. The argument name must be + * registered as a regular argument via addArgument(). + * + * @param disableLongArg Long argument name. + * @param disableShortArg Short argument name. + * + * @see ctkCommandLineParser(QSettings*) + */ + void enableSettings(const QString& disableLongArg = "", + const QString& disableShortArg = ""); + + /** + * Controlls the merging behavior of user values and QSettings values. + * + * If merging is on (the default), user supplied values for an argument + * which can take more than one parameter are merged with values stored + * in the QSettings instance. If merging is off, the user values overwrite + * the QSettings values. + * + * @param merge true enables QSettings merging, false + * disables it. + */ + void mergeSettings(bool merge); + + /** + * Can be used to check if QSettings support has been enabled by a call to + * enableSettings(). + * + * @return true if QSettings support is enabled, false + * otherwise. + */ + bool settingsEnabled() const; + + + /** + * Can be used to teach the parser to stop parsing the arguments and return False when + * an unknown argument is encountered. By default StrictMode is disabled. + * + * @see parseArguments(const QStringList &, bool*) + */ + void setStrictModeEnabled(bool strictMode); + +private: + class ctkInternal; + ctkInternal * Internal; +}; +#endif