Orb/Doxygen/addon/doxywizard/expert.cpp
changeset 0 42188c7ea2d9
child 4 468f4c8d3d5b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orb/Doxygen/addon/doxywizard/expert.cpp	Thu Jan 21 17:29:01 2010 +0000
@@ -0,0 +1,554 @@
+#include "expert.h"
+#include "inputbool.h"
+#include "inputstring.h"
+#include "inputint.h"
+#include "inputstring.h"
+#include "inputstrlist.h"
+#include <QtGui>
+#include <QtXml>
+#include "config.h"
+#include "version.h"
+
+#undef  SA
+#define SA(x) QString::fromAscii(x)
+
+static QString convertToComment(const QString &s)
+{
+  if (s.isEmpty()) 
+  {
+    return QString();
+  }
+  else
+  {
+    return SA("# ")+
+           s.trimmed().replace(SA("\n"),SA("\n# "))+
+           SA("\n");
+  }
+}
+
+//------------------------------------------------------------------------------------
+
+Expert::Expert()
+{
+  m_treeWidget = new QTreeWidget;
+  m_treeWidget->setColumnCount(1);
+  m_topicStack = new QStackedWidget;
+
+  QFile file(SA(":/config.xml"));
+  QString err;
+  int errLine,errCol;
+  QDomDocument configXml;
+  if (file.open(QIODevice::ReadOnly))
+  {
+    if (!configXml.setContent(&file,false,&err,&errLine,&errCol))
+    {
+      QString msg = tr("Error parsing internal config.xml at line %1 column %2.\n%3").
+                  arg(errLine).arg(errCol).arg(err);
+      QMessageBox::warning(this, tr("Error"), msg);
+      exit(1);
+    }
+  }
+  m_rootElement = configXml.documentElement();
+
+  createTopics(m_rootElement);
+  m_helper = new QTextEdit;
+  m_helper->setReadOnly(true);
+  m_splitter = new QSplitter(Qt::Vertical);
+  m_splitter->addWidget(m_treeWidget);
+  m_splitter->addWidget(m_helper);
+
+  QWidget *rightSide = new QWidget;
+  QGridLayout *grid = new QGridLayout(rightSide);
+  m_prev = new QPushButton(tr("Previous"));
+  m_prev->setEnabled(false);
+  m_next = new QPushButton(tr("Next"));
+  grid->addWidget(m_topicStack,0,0,1,2);
+  grid->addWidget(m_prev,1,0,Qt::AlignLeft);
+  grid->addWidget(m_next,1,1,Qt::AlignRight);
+  grid->setColumnStretch(0,1);
+  grid->setRowStretch(0,1);
+
+  addWidget(m_splitter);
+  addWidget(rightSide);
+  connect(m_next,SIGNAL(clicked()),SLOT(nextTopic()));
+
+  connect(m_prev,SIGNAL(clicked()),SLOT(prevTopic()));
+}
+
+Expert::~Expert()
+{
+  QHashIterator<QString,Input*> i(m_options);
+  while (i.hasNext()) 
+  {
+    i.next();
+    delete i.value();
+  }
+}
+
+void Expert::createTopics(const QDomElement &rootElem)
+{
+  QList<QTreeWidgetItem*> items;
+  QDomElement childElem = rootElem.firstChildElement();
+  while (!childElem.isNull())
+  {
+    if (childElem.tagName()==SA("group"))
+    {
+      QString name = childElem.attribute(SA("name"));
+      items.append(new QTreeWidgetItem((QTreeWidget*)0,QStringList(name)));
+      QWidget *widget = createTopicWidget(childElem);
+      m_topics[name] = widget;
+      m_topicStack->addWidget(widget);
+    }
+    childElem = childElem.nextSiblingElement();
+  }
+  m_treeWidget->setHeaderLabels(QStringList() << SA("Topics"));
+  m_treeWidget->insertTopLevelItems(0,items);
+  connect(m_treeWidget,
+          SIGNAL(currentItemChanged(QTreeWidgetItem *,QTreeWidgetItem *)),
+          this,
+          SLOT(activateTopic(QTreeWidgetItem *,QTreeWidgetItem *)));
+}
+
+
+QWidget *Expert::createTopicWidget(QDomElement &elem)
+{
+  QScrollArea *area   = new QScrollArea;
+  QWidget     *topic  = new QWidget;
+  QGridLayout *layout = new QGridLayout(topic);
+  QDomElement child   = elem.firstChildElement();
+  int row=0;
+  while (!child.isNull())
+  {
+    QString type = child.attribute(SA("type"));
+    if (type==SA("bool"))
+    {
+      InputBool *boolOption = 
+          new InputBool(
+            layout,row,
+            child.attribute(SA("id")),
+            child.attribute(SA("defval"))==SA("1"),
+            child.attribute(SA("docs"))
+           );
+      m_options.insert(
+          child.attribute(SA("id")),
+          boolOption
+         );
+      connect(boolOption,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
+      connect(boolOption,SIGNAL(changed()),SIGNAL(changed()));
+    }
+    else if (type==SA("string"))
+    {
+      InputString::StringMode mode;
+      QString format = child.attribute(SA("format"));
+      if (format==SA("dir"))
+      {
+        mode = InputString::StringDir;
+      }
+      else if (format==SA("file"))
+      {
+        mode = InputString::StringFile;
+      }
+      else // format=="string"
+      {
+        mode = InputString::StringFree;
+      }
+      InputString *stringOption = 
+          new InputString(
+            layout,row,
+            child.attribute(SA("id")),
+            child.attribute(SA("defval")),
+            mode,
+            child.attribute(SA("docs")),
+            child.attribute(SA("abspath"))
+           );
+      m_options.insert(
+          child.attribute(SA("id")),
+          stringOption
+         );
+      connect(stringOption,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
+      connect(stringOption,SIGNAL(changed()),SIGNAL(changed()));
+    }
+    else if (type==SA("enum"))
+    {
+      InputString *enumList = new InputString(
+            layout,row,
+            child.attribute(SA("id")),
+            child.attribute(SA("defval")),
+            InputString::StringFixed,
+            child.attribute(SA("docs"))
+           );
+      QDomElement enumVal = child.firstChildElement();
+      while (!enumVal.isNull())
+      {
+        enumList->addValue(enumVal.attribute(SA("name")));
+        enumVal = enumVal.nextSiblingElement();
+      }
+      enumList->setDefault();
+
+      m_options.insert(child.attribute(SA("id")),enumList);
+      connect(enumList,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
+      connect(enumList,SIGNAL(changed()),SIGNAL(changed()));
+    }
+    else if (type==SA("int"))
+    {
+      InputInt *intOption = 
+          new InputInt(
+            layout,row,
+            child.attribute(SA("id")),
+            child.attribute(SA("defval")).toInt(),
+            child.attribute(SA("minval")).toInt(),
+            child.attribute(SA("maxval")).toInt(),
+            child.attribute(SA("docs"))
+          );
+      m_options.insert(
+          child.attribute(SA("id")),
+          intOption
+        );
+      connect(intOption,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
+      connect(intOption,SIGNAL(changed()),SIGNAL(changed()));
+    }
+    else if (type==SA("list"))
+    {
+      InputStrList::ListMode mode;
+      QString format = child.attribute(SA("format"));
+      if (format==SA("dir"))
+      {
+        mode = InputStrList::ListDir;
+      }
+      else if (format==SA("file"))
+      {
+        mode = InputStrList::ListFile;
+      }
+      else if (format==SA("filedir"))
+      {
+        mode = InputStrList::ListFileDir;
+      }
+      else // format=="string"
+      {
+        mode = InputStrList::ListString;
+      }
+      QStringList sl;
+      QDomElement listVal = child.firstChildElement();
+      while (!listVal.isNull())
+      {
+        sl.append(listVal.attribute(SA("name")));
+        listVal = listVal.nextSiblingElement();
+      }
+      InputStrList *listOption = 
+          new InputStrList(
+            layout,row,
+            child.attribute(SA("id")),
+            sl,
+            mode,
+            child.attribute(SA("docs"))
+          );
+      m_options.insert(
+          child.attribute(SA("id")),
+          listOption
+        );
+      connect(listOption,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
+      connect(listOption,SIGNAL(changed()),SIGNAL(changed()));
+    }
+    else if (type==SA("obsolete"))
+    {
+      // ignore
+    }
+    else // should not happen
+    {
+      printf("Unsupported type %s\n",qPrintable(child.attribute(SA("type"))));
+    }
+    child = child.nextSiblingElement();
+  }
+
+  // compute dependencies between options
+  child = elem.firstChildElement();
+  while (!child.isNull())
+  {
+    QString dependsOn = child.attribute(SA("depends"));
+    QString id        = child.attribute(SA("id"));
+    if (!dependsOn.isEmpty())
+    {
+       Input *parentOption = m_options[dependsOn];
+       Input *thisOption   = m_options[id];
+       Q_ASSERT(parentOption);
+       Q_ASSERT(thisOption);
+       if (parentOption && thisOption)
+       {
+         //printf("Adding dependency '%s' (%p)->'%s' (%p)\n",
+         //  qPrintable(dependsOn),parentOption,
+         //  qPrintable(id),thisOption);
+         parentOption->addDependency(thisOption);
+       }
+    }
+    child = child.nextSiblingElement();
+  }
+
+  // set initial dependencies
+  QHashIterator<QString,Input*> i(m_options);
+  while (i.hasNext()) 
+  {
+    i.next();
+    if (i.value())
+    {
+      i.value()->updateDependencies();
+    }
+  }
+
+  layout->setRowStretch(row,1);
+  layout->setColumnStretch(1,2);
+  layout->setSpacing(5);
+  topic->setLayout(layout);
+  area->setWidget(topic);
+  area->setWidgetResizable(true);
+  return area;
+}
+
+void Expert::activateTopic(QTreeWidgetItem *item,QTreeWidgetItem *)
+{
+  if (item)
+  {
+    QWidget *w = m_topics[item->text(0)];
+    m_topicStack->setCurrentWidget(w);
+    m_prev->setEnabled(m_topicStack->currentIndex()!=0); 
+    m_next->setEnabled(m_topicStack->currentIndex()!=m_topicStack->count()-1); 
+  }
+}
+
+void Expert::loadSettings(QSettings *s)
+{
+  QHashIterator<QString,Input*> i(m_options);
+  while (i.hasNext()) 
+  {
+    i.next();
+    QVariant var = s->value(SA("config/")+i.key());
+    //printf("Loading key %s: type=%d\n",qPrintable(i.key()),var.type());
+    if (i.value())
+    {
+      i.value()->value() = var;
+      i.value()->update();
+    }
+  }
+}
+
+void Expert::saveSettings(QSettings *s)
+{
+  QHashIterator<QString,Input*> i(m_options);
+  while (i.hasNext()) 
+  {
+    i.next();
+    if (i.value())
+    {
+      s->value(SA("config/")+i.key(),i.value()->value());
+    }
+  }
+}
+
+void Expert::loadConfig(const QString &fileName)
+{
+  //printf("Expert::loadConfig(%s)\n",qPrintable(fileName));
+  parseConfig(fileName,m_options);
+}
+
+void Expert::saveTopic(QTextStream &t,QDomElement &elem,QTextCodec *codec,
+                       bool brief)
+{
+  // write group header
+  t << endl;
+  t << "#---------------------------------------------------------------------------" << endl;
+  t << "# " << elem.attribute(SA("docs")) << endl;
+  t << "#---------------------------------------------------------------------------" << endl;
+
+  // write options...
+  QDomElement childElem = elem.firstChildElement();
+  while (!childElem.isNull())
+  {
+    QString type = childElem.attribute(SA("type"));
+    QString name = childElem.attribute(SA("id"));
+    QHash<QString,Input*>::const_iterator i = m_options.find(name);
+    if (i!=m_options.end())
+    {
+      Input *option = i.value();
+      if (!brief)
+      {
+        t << endl;
+        t << convertToComment(childElem.attribute(SA("docs")));
+        t << endl;
+      }
+      t << name.leftJustified(23) << "= ";
+      if (option)
+      {
+        option->writeValue(t,codec);
+      }
+      t << endl;
+    }
+    childElem = childElem.nextSiblingElement();
+  }
+
+}
+
+bool Expert::writeConfig(QTextStream &t,bool brief)
+{
+  if (!brief)
+  {
+    // write global header
+    t << "# Doxyfile " << versionString << endl << endl; // TODO: add version
+    t << "# This file describes the settings to be used by the documentation system\n";
+    t << "# doxygen (www.doxygen.org) for a project\n";
+    t << "#\n";
+    t << "# All text after a hash (#) is considered a comment and will be ignored\n";
+    t << "# The format is:\n";
+    t << "#       TAG = value [value, ...]\n";
+    t << "# For lists items can also be appended using:\n";
+    t << "#       TAG += value [value, ...]\n";
+    t << "# Values that contain spaces should be placed between quotes (\" \")\n";
+  }
+
+  QTextCodec *codec = 0;
+  Input *option = m_options[QString::fromAscii("DOXYFILE_ENCODING")];
+  if (option)
+  {
+    codec = QTextCodec::codecForName(option->value().toString().toAscii());
+    if (codec==0) // fallback: use UTF-8
+    {
+      codec = QTextCodec::codecForName("UTF-8");
+    }
+  }
+  QDomElement childElem = m_rootElement.firstChildElement();
+  while (!childElem.isNull())
+  {
+    saveTopic(t,childElem,codec,brief);
+    childElem = childElem.nextSiblingElement();
+  }
+  return true;
+}
+
+QByteArray Expert::saveInnerState () const
+{
+  return m_splitter->saveState();
+}
+
+bool Expert::restoreInnerState ( const QByteArray & state )
+{
+  return m_splitter->restoreState(state);
+}
+
+void Expert::showHelp(Input *option)
+{
+  m_helper->setText(
+           QString::fromAscii("<qt><b>")+option->id()+
+           QString::fromAscii("</b><br>")+
+           option->docs().
+           replace(QChar::fromAscii('\n'),QChar::fromAscii(' '))+
+           QString::fromAscii("<qt>")
+          );
+}
+
+void Expert::nextTopic()
+{
+  m_topicStack->setCurrentIndex(m_topicStack->currentIndex()+1);
+  m_next->setEnabled(m_topicStack->count()!=m_topicStack->currentIndex()+1);
+  m_prev->setEnabled(m_topicStack->currentIndex()!=0);
+  m_treeWidget->setCurrentItem(m_treeWidget->invisibleRootItem()->child(m_topicStack->currentIndex()));
+}
+
+void Expert::prevTopic()
+{
+  m_topicStack->setCurrentIndex(m_topicStack->currentIndex()-1);
+  m_next->setEnabled(m_topicStack->count()!=m_topicStack->currentIndex()+1);
+  m_prev->setEnabled(m_topicStack->currentIndex()!=0);
+  m_treeWidget->setCurrentItem(m_treeWidget->invisibleRootItem()->child(m_topicStack->currentIndex()));
+}
+
+void Expert::resetToDefaults()
+{
+  //printf("Expert::makeDefaults()\n");
+  QHashIterator<QString,Input*> i(m_options);
+  while (i.hasNext()) 
+  {
+    i.next();
+    if (i.value())
+    {
+      i.value()->reset();
+    }
+  }
+}
+
+static bool stringVariantToBool(const QVariant &v)
+{
+  QString s = v.toString().toLower();
+  return s==QString::fromAscii("yes") || s==QString::fromAscii("true") || s==QString::fromAscii("1");
+} 
+
+static bool getBoolOption(
+    const QHash<QString,Input*>&model,const QString &name)
+{
+  Input *option = model[name];
+  Q_ASSERT(option!=0);
+  return stringVariantToBool(option->value());
+} 
+
+static QString getStringOption(
+    const QHash<QString,Input*>&model,const QString &name)
+{
+  Input *option = model[name];
+  Q_ASSERT(option!=0);
+  return option->value().toString();
+}
+
+
+bool Expert::htmlOutputPresent(const QString &workingDir) const
+{
+  bool generateHtml = getBoolOption(m_options,QString::fromAscii("GENERATE_HTML"));
+  if (!generateHtml || workingDir.isEmpty()) return false;
+  QString indexFile = getHtmlOutputIndex(workingDir);
+  QFileInfo fi(indexFile);
+  return fi.exists() && fi.isFile();
+}
+
+QString Expert::getHtmlOutputIndex(const QString &workingDir) const
+{
+  QString outputDir = getStringOption(m_options,QString::fromAscii("OUTPUT_DIRECTORY"));
+  QString htmlOutputDir = getStringOption(m_options,QString::fromAscii("HTML_OUTPUT"));
+  //printf("outputDir=%s\n",qPrintable(outputDir));
+  //printf("htmlOutputDir=%s\n",qPrintable(htmlOutputDir));
+  QString indexFile = workingDir;
+  if (QFileInfo(outputDir).isAbsolute()) // override
+  {
+    indexFile = outputDir;
+  }
+  else // append
+  { 
+    indexFile += QString::fromAscii("/")+outputDir;
+  }
+  if (QFileInfo(htmlOutputDir).isAbsolute()) // override
+  {
+    indexFile = htmlOutputDir;
+  }
+  else // append
+  {
+    indexFile += QString::fromAscii("/")+htmlOutputDir;
+  }
+  indexFile+=QString::fromAscii("/index.html");
+  return indexFile;
+}
+
+bool Expert::pdfOutputPresent(const QString &workingDir) const
+{
+  bool generateLatex = getBoolOption(m_options,QString::fromAscii("GENERATE_LATEX"));
+  bool pdfLatex = getBoolOption(m_options,QString::fromAscii("USE_PDFLATEX"));
+  if (!generateLatex || !pdfLatex) return false;
+  QString latexOutput = getStringOption(m_options,QString::fromAscii("LATEX_OUTPUT"));
+  QString indexFile;
+  if (QFileInfo(latexOutput).isAbsolute())
+  {
+    indexFile = latexOutput+QString::fromAscii("/refman.pdf");
+  }
+  else
+  {
+    indexFile = workingDir+QString::fromAscii("/")+
+                latexOutput+QString::fromAscii("/refman.pdf");
+  }
+  QFileInfo fi(indexFile);
+  return fi.exists() && fi.isFile();
+}
+