Pārlūkot izejas kodu

Merge branch 'commandlineparser_enhanced'

* commandlineparser_enhanced:
  BUG: commandlineparser - Handle the case when (ShortPrefix + UnPrefixedArg) matches LongPrefix
  ENH: commandlineparser - Added more debug statement
  ENH: commandlineparser - If possible, use settings value as default value in helpText
  STYLE: commandlineparser - fix indent
  STYLE: commandlineparser - fix spelling in comment and add missing separator between functions
  ENH: commandlineparser - Added StrictMode property
  ENH: commandlinepaser - Added convenient method parseArguments(int argc, char** argv, bool* ok = 0)
  ENH: commandlineparser: API, documentation and test enhancements:
  DOC: Added method documentation
Jean-Christophe Fillion-Robin 15 gadi atpakaļ
vecāks
revīzija
02f4925781

+ 350 - 10
Libs/Core/Testing/Cpp/ctkCommandLineParserTest1.cpp

@@ -1,6 +1,7 @@
 
 // Qt includes
 #include <QDebug>
+#include <QSettings>
 
 // CTK includes
 #include "ctkCommandLineParser.h"
@@ -130,11 +131,10 @@ int ctkCommandLineParserTest1(int, char*[])
 
   QString expectedHelpString;
   QTextStream streamExpectedHelpString(&expectedHelpString);
-  streamExpectedHelpString << "  --help-string..........This is an help string\n"
-                            << "  --help-string-med\n"
-                            << "  -hs2\n"
-                            << "  --help-string-long.....This is an help string too !\n"
-                            << "  -hs3...................This is an help string too for sure !?\n";
+  streamExpectedHelpString << "  --help-string..............This is an help string\n"
+                           << "  --help-string-med\n"
+                           << "  -hs2, --help-string-long...This is an help string too !\n"
+                           << "  -hs3.......................This is an help string too for sure !?\n";
 
   if (expectedHelpString != parser4.helpText('.'))
     {
@@ -145,11 +145,10 @@ int ctkCommandLineParserTest1(int, char*[])
 
   QString expectedHelpString2;
   QTextStream streamExpectedHelpString2(&expectedHelpString2);
-  streamExpectedHelpString2 << "  --help-string          This is an help string\n"
-                           << "  --help-string-med\n"
-                           << "  -hs2\n"
-                           << "  --help-string-long     This is an help string too !\n"
-                           << "  -hs3                   This is an help string too for sure !?\n";
+  streamExpectedHelpString2 << "  --help-string              This is an help string\n"
+                            << "  --help-string-med\n"
+                            << "  -hs2, --help-string-long   This is an help string too !\n"
+                            << "  -hs3                       This is an help string too for sure !?\n";
   if (expectedHelpString2 != parser4.helpText())
     {
     qCritical() << "Test4 - Problem with helpText() - helpText:\n" << parser4.helpText()
@@ -340,6 +339,39 @@ int ctkCommandLineParserTest1(int, char*[])
     return EXIT_FAILURE;
     }
 
+  // Test7b - Same as Test7 using
+  ctkCommandLineParser parser7b;
+  parser7b.setArgumentPrefix("--", "-");
+  parser7b.addArgument("--bool", "-", QVariant::Bool, "This is a boolean",
+                      false /*defaultValue*/, true /* ignoreRest*/);
+  QStringList arguments7b;
+  arguments7b << "ctkCommandLineParserTest1";
+  arguments7b << "--";
+  arguments7b << expectedUnparsedArguments;
+
+  ok = false;
+  parsedArgs = parser7b.parseArguments(arguments7b, &ok);
+  if (!ok)
+    {
+    qCritical() << "Test7b - Failed to parse arguments";
+    return EXIT_FAILURE;
+    }
+  bool expectedTest7bBool = true;
+  bool test7bBool = parsedArgs["--bool"].toBool();
+  if (test7bBool != expectedTest7bBool)
+    {
+    qCritical() << "Test7b - Failed - test7bBool" << test7bBool
+        << ", expectedTest7bBool" << expectedTest7bBool;
+    return EXIT_FAILURE;
+    }
+
+  if (parser7b.unparsedArguments() != expectedUnparsedArguments)
+    {
+    qCritical() << "Test7b - Failed - expectedUnparsedArguments " << expectedUnparsedArguments
+                << ", parser7b.unparsedArguments" << parser7b.unparsedArguments();
+    return EXIT_FAILURE;
+    }
+
   // Test8 - Check if addStringArgument and ignore_rest=true works as expected
   ctkCommandLineParser parser8;
   parser8.addArgument("--string", "", QVariant::String, "This is a string",
@@ -442,5 +474,313 @@ int ctkCommandLineParserTest1(int, char*[])
     return EXIT_FAILURE;
     }
 
+  // Test11 - Check if setArgumentPrefix works as expected
+  ctkCommandLineParser parser11;
+  parser11.setArgumentPrefix("--", "/");
+  parser11.addArgument("test-string", "", QVariant::String);
+  parser11.addArgument("", "i", QVariant::Int);
+
+  QStringList arguments11;
+  arguments11 << "ctkCommandLineParserTest1";
+  arguments11 << "--test-string" << "Unix-style";
+  arguments11 << "test-string"; // unknown argument
+  arguments11 << "/i" << "5";
+
+  ok = false;
+  parsedArgs = parser11.parseArguments(arguments11, &ok);
+  if (!ok)
+    {
+    qCritical() << "Test11 - Failed to parse arguments: " << parser11.errorString();
+    return EXIT_FAILURE;
+    }
+
+  if (!parser11.unparsedArguments().contains("test-string"))
+    {
+    qCritical() << "Test11 - argument test-string should be unknown.";
+    return EXIT_FAILURE;
+    }
+
+  if (!parser11.argumentParsed("test-string") || !parser11.argumentParsed("i"))
+    {
+    qCritical() << "Test11 - Problem with argumentParsed().";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs["test-string"].toString() != "Unix-style")
+    {
+    qCritical() << "Test11 - Failed argument: test-string, got: " << parsedArgs["test-string"].toString()
+        << ", expected: " << "Unix-style";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs["i"].toInt() != 5)
+    {
+    qCritical() << "Test11 - Failed argument: i, got: " << parsedArgs["i"].toInt()
+        << ", expected: " << 5;
+    return EXIT_FAILURE;
+    }
+
+  // Test12 - Check if the returned hash map contains the correct entries
+  ctkCommandLineParser parser12;
+  parser12.addArgument("--test-list", "-l", QVariant::StringList);
+
+  QStringList arguments12;
+  arguments12 << "ctkCommandLineParserTest1";
+
+  parsedArgs = parser12.parseArguments(arguments12);
+  if (!parsedArgs.isEmpty())
+    {
+    qCritical() << "Test12 - Returned hash map should be empty.";
+    return EXIT_FAILURE;
+    }
+
+  arguments12 << "--test-list" << "--test-list2";
+  parsedArgs = parser12.parseArguments(arguments12);
+  if (parsedArgs.size() != 1 || !parsedArgs.contains("--test-list"))
+    {
+    qCritical() << "Test12 - Returned hash map contains wrong elements.";
+    return EXIT_FAILURE;
+    }
+
+  // Test13 - Check that supplying a default value works
+  ctkCommandLineParser parser13;
+  parser13.addArgument("", "-d", QVariant::Int, "Argument with default value", 3);
+  parsedArgs = parser13.parseArguments(QStringList(), &ok);
+  if (!parsedArgs.contains("-d"))
+    {
+    qCritical() << "Test13 - Returned hash map does not contain argument with default value.";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs["-d"].toInt() != 3)
+    {
+    qCritical() << "Test13 - Returned hash map contains wrong argument parameter.";
+    return EXIT_FAILURE;
+    }
+
+  // ==================== QSettings tests ====================
+
+  QSettings settings;
+  settings.setValue("long-settings-argument", 5);
+  settings.setValue("s", "settings-short");
+  settings.setValue("invalid", QVariant());
+
+  // Test14 - Check that QSettings are used
+  ctkCommandLineParser parser14(&settings);
+  parser14.setArgumentPrefix("--", "-");
+  parser14.addArgument("long-settings-argument", "", QVariant::Int);
+  parser14.addArgument("", "s", QVariant::String, "A short argument", "my-short");
+
+  QStringList arguments14;
+  arguments14 << "ctkCommandLineParserTest1";
+  arguments14 << "-s" << "short";
+  arguments14 << "unknown";
+
+  parsedArgs = parser14.parseArguments(arguments14);
+
+  //  Check that QSettings are ignored
+  if (parsedArgs.size() != 1 || parsedArgs["s"] != "short")
+    {
+    qCritical() << "Test14 - Parsed arguments must only contain -s short.";
+    return EXIT_FAILURE;
+    }
+
+  // Now use QSettings
+  parser14.enableSettings("disable-settings");
+  parser14.addArgument("disable-settings", "", QVariant::Bool);
+  parsedArgs = parser14.parseArguments(arguments14);
+
+  if (!parser14.settingsEnabled())
+    {
+    qCritical() << "Test14 - Disabling settings failed.";
+    return EXIT_FAILURE;
+    }
+
+  if (parser14.unparsedArguments().size() != 1 ||
+      !parser14.unparsedArguments().contains("unknown"))
+    {
+    qCritical() << "Test14 - Parsing unknown arguments failed.";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs.contains("invalid"))
+    {
+    qCritical() << "Test14 - Invalid QSettings value found.";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs.size() != 2)
+    {
+    qCritical() << "Test14 - Wrong number of parsed arguments.";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs["s"] != "short")
+    {
+    qCritical() << "Test14 - QSettings values must not overwrite user values.";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs["long-settings-argument"].toInt() != 5)
+    {
+    qCritical() << "Test14 - Missing value from QSettings.";
+    return EXIT_FAILURE;
+    }
+
+  arguments14.clear();
+  arguments14 << "ctkCommandLineParserTest1";
+  parsedArgs = parser14.parseArguments(arguments14);
+
+  if (parsedArgs.size() != 2)
+    {
+    qCritical() << "Test14 - Only QSettings values corresponding to arguments must be included.";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs["s"] != "settings-short")
+    {
+    qCritical() << "Test14 - QSettings value should be used as default parameter.";
+    return EXIT_FAILURE;
+    }
+
+  // Disable QSettings via command line argument
+  arguments14.clear();
+  arguments14 << "ctkCommandLineParserTest1";
+  arguments14 << "--disable-settings";
+  arguments14 << "--long-settings-argument" << "12";
+
+  parsedArgs = parser14.parseArguments(arguments14);
+
+  if (parsedArgs["long-settings-argument"].toInt() != 12)
+    {
+    qCritical() << "Test14 - Wrong parameter for argument: long-settings-argument.";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs["s"] != "my-short")
+    {
+    qCritical() << parsedArgs;
+    qCritical() << "Test14 - Default value for argument -s not used.";
+    return EXIT_FAILURE;
+    }
+
+  // Test15 - Check that merging with QSettings works
+  settings.clear();
+  settings.setValue("--list-argument", "z");
+
+  ctkCommandLineParser parser15(&settings);
+  parser15.enableSettings();
+  parser15.addArgument("--list-argument", "", QVariant::StringList);
+
+  QStringList arguments15;
+  arguments15 << "ctkCommandLineParserTest1";
+  arguments15 << "--list-argument" << "a" << "b";
+
+  // Test with enabled merging
+  ok = false;
+  parsedArgs = parser15.parseArguments(arguments15, &ok);
+  if (!ok)
+    {
+    qCritical() << "Test15 - parsing arguments failed.";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs.contains("--list-argument"))
+    {
+    QStringList list = parsedArgs["--list-argument"].toStringList();
+    if (list.size() != 3)
+      {
+      qCritical() << "Test15 - Parameter merging failed.";
+      return EXIT_FAILURE;
+      }
+    if (!list.contains("a") || !list.contains("b") || !list.contains("z"))
+      {
+      qCritical() << "Test15 - Merged list contains wrong elements.";
+      return EXIT_FAILURE;
+      }
+    }
+  else
+    {
+    qCritical() << "Test15 - --list-argument was not parsed.";
+    return EXIT_FAILURE;
+    }
+
+  // Test with disabled merging
+  parser15.mergeSettings(false);
+  ok = false;
+  parsedArgs = parser15.parseArguments(arguments15, &ok);
+  if (!ok)
+    {
+    qCritical() << "Test15 - parsing arguments failed.";
+    return EXIT_FAILURE;
+    }
+
+  if (parsedArgs.contains("--list-argument"))
+    {
+    QStringList list = parsedArgs["--list-argument"].toStringList();
+    if (list.size() != 2)
+      {
+      qCritical() << "Test15 - Disabling merging failed.";
+      return EXIT_FAILURE;
+      }
+    if (!list.contains("a") || !list.contains("b"))
+      {
+      qCritical() << "Test15 - List contains wrong elements.";
+      return EXIT_FAILURE;
+      }
+    }
+  else
+    {
+    qCritical() << "Test15 - --list-argument was not parsed.";
+    return EXIT_FAILURE;
+    }
+
+  // Test16 - Check if strictMode works
+  settings.clear();
+
+  ctkCommandLineParser parser16(&settings);
+  parser16.enableSettings();
+  parser16.addArgument("--test-bool", "", QVariant::Bool);
+  parser16.setStrictModeEnabled(true);
+
+  QStringList arguments16;
+  arguments16 << "ctkCommandLineParserTest1";
+  arguments16 << "--test-bool";
+
+  // parseArguments should NOT fail
+  ok = false;
+  parsedArgs = parser16.parseArguments(arguments16, &ok);
+  if (!ok)
+    {
+    qCritical() << "Test16-1 - parsing arguments failed.";
+    return EXIT_FAILURE;
+    }
+
+  // Since two identical arguments are provided, parseArguments should fail
+  arguments16.clear();
+  arguments16 << "ctkCommandLineParserTest1";
+  arguments16 << "--test-bool";
+  arguments16 << "--test-bool";
+  ok = false;
+  parsedArgs = parser16.parseArguments(arguments16, &ok);
+  if (ok)
+    {
+    qCritical() << "Test16-2 - parsing arguments should fail.";
+    return EXIT_FAILURE;
+    }
+
+  // Since an unknown argument is provided, parseArguments should fail
+  arguments16.clear();
+  arguments16 << "ctkCommandLineParserTest1";
+  arguments16 << "--test-bool";
+  arguments16 << "test-bool";
+  ok = false;
+  parsedArgs = parser16.parseArguments(arguments16, &ok);
+  if (ok)
+    {
+    qCritical() << "Test16-3 - parsing arguments should fail.";
+    return EXIT_FAILURE;
+    }
+
   return EXIT_SUCCESS;
 }

+ 447 - 119
Libs/Core/ctkCommandLineParser.cpp

@@ -1,61 +1,69 @@
+// STL includes
+#include <stdexcept>
 
 // Qt includes 
 #include <QHash>
 #include <QStringList>
 #include <QTextStream>
 #include <QDebug>
+#include <QSettings>
+#include <QPointer>
 
 // CTK includes
 #include "ctkCommandLineParser.h"
 
 namespace
 {
-
+// --------------------------------------------------------------------------
 class CommandLineParserArgumentDescription
 {
 public:
   CommandLineParserArgumentDescription(
-    const QString& longArg, const QString& shortArg, QVariant::Type type,
-    const QString& argHelp, const QVariant& defaultValue, bool ignoreRest)
-      : LongArg(longArg), ShortArg(shortArg), ArgHelp(argHelp),
-  IgnoreRest(ignoreRest), NumberOfParametersToProcess(0),
-  Value(type)
+    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:
       {
+      case QVariant::String:
+        {
         NumberOfParametersToProcess = 1;
         RegularExpression = ".*";
-      }
-      break;
-    case QVariant::Bool:
-      {
+        }
+        break;
+      case QVariant::Bool:
+        {
         NumberOfParametersToProcess = 0;
         RegularExpression = "";
-      }
-      break;
-    case QVariant::StringList:
-      {
+        }
+        break;
+      case QVariant::StringList:
+        {
         NumberOfParametersToProcess = -1;
         RegularExpression = ".*";
-      }
-      break;
-    case QVariant::Int:
-      {
+        }
+        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<int>(type));
       }
-      break;
-    default:
-      ExactMatchFailedMessage = QString("Type %1 not supported.").arg(static_cast<int>(type));
-    }
 
   }
 
@@ -63,104 +71,123 @@ public:
 
   bool addParameter(const QString& value);
 
-  QString helpText(int fieldWidth, const char charPad);
+  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 Value;
+  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:
     {
+    case QVariant::String:
+      {
       Value.setValue(value);
-    }
-    break;
-  case QVariant::Bool:
-    {
+      }
+      break;
+    case QVariant::Bool:
+      {
       Value.setValue(!QString::compare(value, "true", Qt::CaseInsensitive));
-    }
-    break;
-  case QVariant::StringList:
-    {
-      if (Value.isNull())
+      }
+      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:
-    {
+      break;
+    case QVariant::Int:
+      {
       Value.setValue(value.toInt());
+      }
+      break;
+    default:
+      return false;
     }
-    break;
-  default:
-    return false;
-  }
 
   return true;
 }
 
 // --------------------------------------------------------------------------
-QString CommandLineParserArgumentDescription::helpText(int fieldWidth, const char charPad)
+QString CommandLineParserArgumentDescription::helpText(int fieldWidth, const char charPad,
+                                                       const QString& settingsValue)
 {
   QString text;
   QTextStream stream(&text);
   stream.setFieldAlignment(QTextStream::AlignLeft);
   stream.setPadChar(charPad);
 
-  if (this->LongArg.isEmpty() && !this->ArgHelp.isEmpty())
+  QString shortAndLongArg;
+  if (!this->ShortArg.isEmpty())
     {
-    stream.setFieldWidth(fieldWidth);
+    shortAndLongArg += QString("  %1%2").arg(this->ShortArgPrefix).arg(this->ShortArg);
     }
 
-  if (!this->ShortArg.isEmpty())
+  if (!this->LongArg.isEmpty())
     {
-    stream  << QString("  %1").arg(this->ShortArg);
-    if(!this->LongArg.isEmpty())
+    if (this->ShortArg.isEmpty())
       {
-      stream << "\n";
+      shortAndLongArg.append("  ");
       }
+    else
+      {
+      shortAndLongArg.append(", ");
+      }
+
+    shortAndLongArg += QString("%1%2").arg(this->LongArgPrefix).arg(this->LongArg);
     }
 
-  if (!this->LongArg.isEmpty())
+  if(!this->ArgHelp.isEmpty())
     {
-    if(!this->ArgHelp.isEmpty())
-      {
-      stream.setFieldWidth(fieldWidth);
-      }
-    stream  << QString("  %1").arg(this->LongArg);
+    stream.setFieldWidth(fieldWidth);
     }
+
+  stream  << shortAndLongArg;
   stream.setFieldWidth(0);
-  stream << this->ArgHelp << "\n";
+  stream << this->ArgHelp;
+  if (!settingsValue.isNull())
+    {
+    stream << " (default: " << settingsValue << ")";
+    }
+  else if (!this->DefaultValue.isNull())
+    {
+    stream << " (default: " << this->DefaultValue.toString() << ")";
+    }
+  stream << "\n";
   return text;
 }
 
@@ -173,20 +200,33 @@ QString CommandLineParserArgumentDescription::helpText(int fieldWidth, const cha
 class ctkCommandLineParser::ctkInternal
 {
 public:
-  ctkInternal():Debug(false),FieldWidth(0){}
+  ctkInternal(QSettings* settings)
+    : Debug(false), FieldWidth(0), UseQSettings(false),
+      Settings(settings), MergeSettings(true), StrictMode(false)
+  {}
 
   ~ctkInternal() { qDeleteAll(ArgumentDescriptionList); }
   
   CommandLineParserArgumentDescription* argumentDescription(const QString& argument);
   
-  QList<CommandLineParserArgumentDescription*>          ArgumentDescriptionList;
-  QHash<QString, CommandLineParserArgumentDescription*> ArgNameToArgumentDescriptionMap;
+  QList<CommandLineParserArgumentDescription*>                 ArgumentDescriptionList;
+  QHash<QString, CommandLineParserArgumentDescription*>        ArgNameToArgumentDescriptionMap;
+  QMap<QString, QList<CommandLineParserArgumentDescription*> > GroupToArgumentDescriptionListMap;
   
   QStringList UnparsedArguments; 
   QStringList ProcessedArguments;
   QString     ErrorString;
   bool        Debug;
   int         FieldWidth;
+  QString     LongPrefix;
+  QString     ShortPrefix;
+  QString     CurrentGroup;
+  bool        UseQSettings;
+  QPointer<QSettings> Settings;
+  QString     DisableQSettingsLongArg;
+  QString     DisableQSettingsShortArg;
+  bool        MergeSettings;
+  bool        StrictMode;
 };
 
 // --------------------------------------------------------------------------
@@ -196,9 +236,31 @@ public:
 CommandLineParserArgumentDescription*
   ctkCommandLineParser::ctkInternal::argumentDescription(const QString& argument)
 {
-  if (this->ArgNameToArgumentDescriptionMap.contains(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[argument];
+    return this->ArgNameToArgumentDescriptionMap[unprefixedArg];
     }
   return 0;
 }
@@ -207,9 +269,9 @@ CommandLineParserArgumentDescription*
 // ctkCommandLineParser methods
 
 // --------------------------------------------------------------------------
-ctkCommandLineParser::ctkCommandLineParser()
+ctkCommandLineParser::ctkCommandLineParser(QSettings* settings)
 {
-  this->Internal = new ctkInternal();
+  this->Internal = new ctkInternal(settings);
 }
 
 // --------------------------------------------------------------------------
@@ -226,18 +288,51 @@ QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList&
   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<CommandLineParserArgumentDescription*> 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;
       }
@@ -245,7 +340,16 @@ QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList&
     // Skip if argument has already been parsed ...
     if (this->Internal->ProcessedArguments.contains(argument))
       {
-      qDebug() << "Skipping argument" << argument << " - Already processed !";
+      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;
       }
 
@@ -255,9 +359,30 @@ QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList&
     // 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)
@@ -275,20 +400,20 @@ QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList&
             this->Internal->ErrorString = 
                 missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess);
             if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
-            if (ok) *ok = false;
+            if (ok) { *ok = false; }
             return QHash<QString, QVariant>();
             }
           QString parameter = arguments.at(i + j);
           if (this->Internal->Debug)
             {
-            qDebug() << "Processing parameter" << j << ", value:" << parameter;
+            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;
+            if (ok) { *ok = false; }
             return QHash<QString, QVariant>();
             }
           if (!currentArgDesc->addParameter(parameter))
@@ -298,7 +423,7 @@ QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList&
                 arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
 
             if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
-            if (ok) *ok = false;
+            if (ok) { *ok = false; }
             return QHash<QString, QVariant>();
             }
           }
@@ -309,7 +434,7 @@ QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList&
         {
         if (this->Internal->Debug)
           {
-          qDebug() << "Proccessing StringList ...";
+          qDebug() << "  Proccessing StringList ...";
           }
         int j = 1;
         while(j + i < arguments.size())
@@ -318,14 +443,14 @@ QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList&
             {
             if (this->Internal->Debug)
               {
-              qDebug() << "No more parameter for" << argument;
+              qDebug() << "  No more parameter for" << argument;
               }
             break;
             }
           QString parameter = arguments.at(j + i);
           if (this->Internal->Debug)
             {
-            qDebug() << "Processing parameter" << j << ", value:" << parameter;
+            qDebug() << "  Processing parameter" << j << ", value:" << parameter;
             }
           if (!currentArgDesc->addParameter(parameter))
             {
@@ -334,7 +459,7 @@ QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList&
                 arg(argument).arg(currentArgDesc->ExactMatchFailedMessage);
 
             if (this->Internal->Debug) { qDebug() << this->Internal->ErrorString; }
-            if (ok) *ok = false;
+            if (ok) { *ok = false; }
             return QHash<QString, QVariant>();
             }
           j++;
@@ -345,40 +470,135 @@ QHash<QString, QVariant> ctkCommandLineParser::parseArguments(const QStringList&
       }
     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 = true;
+  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<QString, QVariant> parsedArguments;
   QListIterator<CommandLineParserArgumentDescription*> 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;
     }
 
-    parsedArguments.insert(key, desc->Value);
-  }
   return parsedArguments;
 }
 
 // -------------------------------------------------------------------------
-QString ctkCommandLineParser::errorString()
+QHash<QString, QVariant> 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 QStringList& ctkCommandLineParser::unparsedArguments() const
 {
   return this->Internal->UnparsedArguments;
 }
@@ -386,46 +606,60 @@ const QStringList& ctkCommandLineParser::unparsedArguments()
 // --------------------------------------------------------------------------
 void ctkCommandLineParser::addArgument(const QString& longarg, const QString& shortarg,
                                        QVariant::Type type, const QString& argHelp,
-                                       const QVariant& defaultValue, bool ignoreRest)
+                                       const QVariant& defaultValue, bool ignoreRest,
+                                       bool deprecated)
 {
-  Q_ASSERT(!defaultValue.isValid() || defaultValue.type() == type);
+  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(!added);
+  Q_ASSERT_X(!added, "addArgument", "long argument already added");
   if (added) { return; }
 
   added = this->Internal->ArgNameToArgumentDescriptionMap.contains(shortarg);
-  Q_ASSERT(!added);
+  Q_ASSERT_X(!added, "addArgument", "short argument already added");
   if (added) { return; }
 
   CommandLineParserArgumentDescription* argDesc =
-    new CommandLineParserArgumentDescription(longarg, shortarg, type,
-                                             argHelp, defaultValue, ignoreRest);
-
-  Q_ASSERT(!(longarg.isEmpty() && shortarg.isEmpty()));
-  if (longarg.isEmpty() && shortarg.isEmpty()) { return; }
+    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;
-    int argWidth = longarg.length() + 7;
-    if (argWidth > this->Internal->FieldWidth)
     {
-      this->Internal->FieldWidth = argWidth;
+    this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc;
+    argWidth += longarg.length() + this->Internal->LongPrefix.length();
     }
-  }
   if (!shortarg.isEmpty())
-  {
+    {
     this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc;
-    int argWidth = shortarg.length() + 7;
-    if (argWidth > this->Internal->FieldWidth)
+    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->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);
 }
 
 // --------------------------------------------------------------------------
@@ -449,29 +683,123 @@ bool ctkCommandLineParser::setExactMatchRegularExpression(
 }
 
 // --------------------------------------------------------------------------
-QString ctkCommandLineParser::helpText(const char charPad)
+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);
 
-  // Loop over argument descriptions
-  foreach(CommandLineParserArgumentDescription* argDesc,
-          this->Internal->ArgumentDescriptionList)
+  QList<CommandLineParserArgumentDescription*> deprecatedArgs;
+
+  // Loop over grouped argument descriptions
+  QMapIterator<QString, QList<CommandLineParserArgumentDescription*> > 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 << argDesc->helpText(this->Internal->FieldWidth, charPad);
+    stream << "\nDeprecated arguments:\n";
+    foreach(CommandLineParserArgumentDescription* argDesc, deprecatedArgs)
+      {
+      stream << argDesc->helpText(this->Internal->FieldWidth, charPad);
+      }
     }
+
   return text;
 }
 
 // --------------------------------------------------------------------------
-bool ctkCommandLineParser::argumentAdded(const QString& argument)
+bool ctkCommandLineParser::argumentAdded(const QString& argument) const
 {
   return this->Internal->ArgNameToArgumentDescriptionMap.contains(argument);
 }
 
 // --------------------------------------------------------------------------
-bool ctkCommandLineParser::argumentParsed(const QString& 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;
+}
+

+ 343 - 12
Libs/Core/ctkCommandLineParser.h

@@ -6,38 +6,369 @@
 #include <QStringList>
 #include <QVariant>
 
+class QSettings;
+
 // CTK includes
 #include "CTKCoreExport.h"
 
-// --------------------------------------------------------------------------
+/**
+ * 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:
+ *
+ * <ul>
+ * <li>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.</li>
+ * <li>Deprecated arguments.</li>
+ * <li>Custom regular expressions for argument validation.</li>
+ * <li>Set different argument name prefixes for native platform look and feel.</li>
+ * <li>QSettings support. Default values for arguments can be read from
+ *     a QSettings object.</li>
+ * <li>Create a help text for the command line arguments with support for
+ *     grouping arguments.</li>
+ * </ul>
+ *
+ * Here is an example how to use this class inside a main function:
+ *
+ * \code
+ * #include <ctkCommandLineParser.h>
+ * #include <QCoreApplication>
+ * #include <QTextStream>
+ *
+ * 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<QString, QVariant> 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 CTK_CORE_EXPORT ctkCommandLineParser
 {
 public:
 
-  ctkCommandLineParser();
+  /**
+   * Constructs a parser instance.
+   *
+   * If QSettings support is enabled by a call to <code>enableSettings()</code>
+   * 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 <code>QCoreApplication::setOrganizationName()</code>
+   * and <code>QCoreApplication::setApplicationName()</code>.
+   *
+   * @param settings A QSettings instance which should be used.
+   */
+  ctkCommandLineParser(QSettings* settings = 0);
+
   ~ctkCommandLineParser();
   
-  QHash<QString /*longarg*/, QVariant> parseArguments(const QStringList &arguments, bool* ok = 0);
+  /**
+   * Parse a given list of command line arguments.
+   *
+   * This method parses a list of QString elements considering the known arguments
+   * added by calls to <code>addArgument()</code>. If any one of the argument
+   * values does not match the corresponding regular expression,
+   * <code>ok</code> 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
+   * <code>addArgument()</code> method call.
+   *
+   * @param arguments A QStringList containing command line arguments. Usually
+   *        given by <code>QCoreApplication::arguments()</code>.
+   * @param ok A pointer to a boolean variable. Will be set to <code>true</code>
+   *        if all regular expressions matched, <code>false</code> otherwise.
+   * @return A QHash object mapping the long argument (if empty, the short one)
+   *         to a QVariant containing the value.
+   */
+  QHash<QString, QVariant> parseArguments(const QStringList &arguments, bool* ok = 0);
 
-  QString errorString();
+  /**
+    * Convenient method allowing to parse a given list of command line arguments.
+    * @see parseArguments(const QStringList &, bool*)
+    */
+  QHash<QString, QVariant> parseArguments(int argc, char** argv, bool* ok = 0);
+
+  /**
+   * Returns a detailed error description if a call to <code>parseArguments()</code>
+   * failed.
+   *
+   * @return The error description, empty if no error occured.
+   * @see parseArguments(const QStringList&, bool*)
+   */
+  QString errorString() const;
   
-  const QStringList& unparsedArguments();
+  /**
+   * This method returns all unparsed arguments, i.e. all arguments
+   * for which no long or short name has been registered via a call
+   * to <code>addArgument()</code>.
+   *
+   * @see addArgument()
+   *
+   * @return A list containing unparsed arguments.
+   */
+  const QStringList& unparsedArguments() const;
   
-  bool argumentAdded(const QString& argument);
+  /**
+   * Checks if the given argument has been added via a call
+   * to <code>addArgument()</code>.
+   *
+   * @see addArgument()
+   *
+   * @param argument The argument to be checked.
+   * @return <code>true</code> if the argument was added, <code>false</code>
+   *         otherwise.
+   */
+  bool argumentAdded(const QString& argument) const;
 
-  bool argumentParsed(const QString& argument);
+  /**
+   * Checks if the given argument has been parsed successfully by a previous
+   * call to <code>parseArguments()</code>.
+   *
+   * @param argument The argument to be checked.
+   * @return <code>true</code> if the argument was parsed, <code>false</code>
+   *         otherwise.
+   */
+  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 <code>type</code> parameter.
+   * The following types are supported:
+   *
+   * <table>
+   * <tr><td><b>Type</b></td><td><b># of parameters</b></td><td><b>Default regular expr</b></td>
+   *        <td><b>Example</b></td></tr>
+   * <tr><td>QVariant::String</td><td>1</td><td>.*</td><td>--test-string StringParameter</td></tr>
+   * <tr><td>QVariant::Bool</td><td>0</td><td>does not apply</td><td>--enable-something</td></tr>
+   * <tr><td>QVariant::StringList</td><td>-1</td><td>.*</td><td>--test-list string1 string2</td></tr>
+   * <tr><td>QVariant::Int</td><td>1</td><td>-?[0-9]+</td><td>--test-int -5</td></tr>
+   * </table>
+   *
+   * The regular expressions are used to validate the parameters of command line
+   * arguments. You can restrict the valid set of parameters by calling
+   * <code>setExactMatchRegularExpression()</code> 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 <code>type</code>, an
+   * exception is thrown. Arguments with default values are always returned by
+   * <code>parseArguments()</code>.
+   *
+   * You can also declare an argument deprecated, by setting <code>deprecated</code>
+   * to <code>true</code>. Alternatively you can add a deprecated argument by calling
+   * <code>addDeprecatedArgument()</code>.
+   *
+   * 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 <code>defaultValue</code>
+   *         does not match <code>type</code>, a <code>std::logic_error</code> is thrown.
+   */
   void addArgument(const QString& longarg, const QString& shortarg,
                    QVariant::Type type, const QString& argHelp = QString(),
-                   const QVariant& defaultValue = QVariant(), bool ignoreRest = false);
+                   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, <code>argHelp</code> 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 <code>helpText()</code>.
+   *
+   * @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
+   * <code>errorString()</code> 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 <code>true</code> if the argument was found and the regular expression was set,
+   *         <code>false</code> otherwise.
+   *
+   * @see errorString()
+   */
   bool setExactMatchRegularExpression(const QString& argument, const QString& expression,
-                                      const QString& ExactMatchFailedMessage);
+                                      const QString& exactMatchFailedMessage);
 
-  int fieldWidth();
+  /**
+   * 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 <code>addArgument()</code>. The arguments can be grouped by
+   * using <code>beginGroup()</code> and <code>endGroup()</code>.
+   *
+   * @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
+   * <code>addArgument()</code>. 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 <code>addArgument</code>.
+   *
+   * 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
+   * <code>addArgument()</code> will be put in the new group. You can close the
+   * current group by calling <code>endGroup()</code> 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 <code>helpText()</code>.
+   *
+   * @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
+   * <code>addArgument()</code>. 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 <code>mergeSettings(false)</code>
+   * to disable merging.
+   *
+   * See <code>ctkCommandLineParser(QSettings*)</code> 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 <code>addArgument()</code>.
+   *
+   * @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 <code>true</code> enables QSettings merging, <code>false</code>
+   *        disables it.
+   */
+  void mergeSettings(bool merge);
+
+  /**
+   * Can be used to check if QSettings support has been enabled by a call to
+   * <code>enableSettings()</code>.
+   *
+   * @return <code>true</code> if QSettings support is enabled, <code>false</code>
+   *         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 <code>StrictMode</code> is disabled.
+    *
+    * @see parseArguments(const QStringList &, bool*)
+    */
+  void setStrictModeEnabled(bool strictMode);
 
-  QString helpText(const char charPad = ' ');
-  
 private:
   class ctkInternal;
   ctkInternal * Internal;