expert.cpp 25.5 KB
Newer Older
1 2
#include <QtGui>
#include <QtXml>
3 4 5 6
#include "expert.h"
#include "inputbool.h"
#include "inputstring.h"
#include "inputint.h"
7 8
#include "inputstring.h"
#include "inputstrlist.h"
9 10
#include "config.h"
#include "version.h"
11
#include "configdoc.h"
12
#include "../../src/settings.h"
13

14 15 16 17 18 19 20 21 22 23 24
#define SA(x) QString::fromAscii(x)

static QString convertToComment(const QString &s)
{
  if (s.isEmpty()) 
  {
    return QString();
  }
  else
  {
    return SA("# ")+
25
           s.trimmed().replace(SA("\n"),SA("\n# ")).replace(SA("# \n"), SA("#\n"))+
26 27 28 29
           SA("\n");
  }
}

30 31 32 33 34 35 36 37 38 39 40 41 42 43
void Expert::setHeader(const char *header)
{
  m_header = SA(header);
}

void Expert::add(const char *name,const char *docs)
{
  Input *opt = m_options[SA(name)];
  if (opt)
  {
    opt->setTemplateDocs(SA(docs));
  }
}

44
//------------------------------------------------------------------------------------
45

46
Expert::Expert()
47
{
48 49 50
  m_treeWidget = new QTreeWidget;
  m_treeWidget->setColumnCount(1);
  m_topicStack = new QStackedWidget;
Dimitri van Heesch's avatar
Dimitri van Heesch committed
51
  m_inShowHelp = FALSE;
52

53 54 55 56 57
  QFile file(SA(":/config.xml"));
  QString err;
  int errLine,errCol;
  QDomDocument configXml;
  if (file.open(QIODevice::ReadOnly))
58
  {
59
    if (!configXml.setContent(&file,false,&err,&errLine,&errCol))
60
    {
61 62 63 64 65
      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);
    }
66
  }
67 68 69
  m_rootElement = configXml.documentElement();

  createTopics(m_rootElement);
70
  m_helper = new QTextBrowser;
71
  m_helper->setReadOnly(true);
72
  m_helper->setOpenExternalLinks(TRUE);
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
  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()));
91

92
  connect(m_prev,SIGNAL(clicked()),SLOT(prevTopic()));
93 94

  addConfigDocs(this);
95 96 97 98 99 100
}

Expert::~Expert()
{
  QHashIterator<QString,Input*> i(m_options);
  while (i.hasNext()) 
101
  {
102 103
    i.next();
    delete i.value();
104
  }
105
}
106

107 108 109 110 111 112 113 114
void Expert::createTopics(const QDomElement &rootElem)
{
  QList<QTreeWidgetItem*> items;
  QDomElement childElem = rootElem.firstChildElement();
  while (!childElem.isNull())
  {
    if (childElem.tagName()==SA("group"))
    {
115 116
      // Remove _ from a group name like: Source_Browser
      QString name = childElem.attribute(SA("name")).replace(SA("_"),SA(" "));
117 118 119 120 121 122 123 124 125 126 127 128 129
      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 *)));
130 131
}

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
static QString getDocsForNode(const QDomElement &child)
{
  QString type = child.attribute(SA("type"));
  QString docs = SA("");
  // read documentation text
  QDomElement docsVal = child.firstChildElement();
  while (!docsVal.isNull())
  {
    if (docsVal.tagName()==SA("docs") &&
        docsVal.attribute(SA("doxywizard")) != SA("0"))
    {
      for (QDomNode n = docsVal.firstChild(); !n.isNull(); n = n.nextSibling())
      {
        QDomText t = n.toText();
        if (!t.isNull()) docs+=t.data();
      }
148
      docs += SA("<br/>");
149 150 151 152 153 154 155
    }
    docsVal = docsVal.nextSiblingElement();
  }

  // for an enum we list the values
  if (type==SA("enum"))
  {
156
    docs += SA("<br/>");
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
    docs += SA("Possible values are: ");
    int numValues=0;
    docsVal = child.firstChildElement();
    while (!docsVal.isNull())
    {
      if (docsVal.tagName()==SA("value"))
      {
        numValues++;
      }
      docsVal = docsVal.nextSiblingElement();
    }
    int i=0;
    docsVal = child.firstChildElement();
    while (!docsVal.isNull())
    {
      if (docsVal.tagName()==SA("value"))
      {
        i++;
        docs += SA("<code>") + docsVal.attribute(SA("name")) + SA("</code>");
        QString desc = docsVal.attribute(SA("desc"));
        if (!desc.isEmpty())
        {
          docs+= SA(" ")+desc;
        }
        if (i==numValues-1)
        {
          docs+=SA(" and ");
        }
        else if (i==numValues)
        {
          docs+=SA(".");
        }
        else
        {
          docs+=SA(", ");
        }
      }
      docsVal = docsVal.nextSiblingElement();
    }
    docs+=SA("<br/>");
197 198
    docs+=SA("<br/>");
    docs+=SA(" The default value is: <code>")+
199 200
          child.attribute(SA("defval"))+
          SA("</code>.");
201
    docs+= SA("<br/>");
202 203 204
  }
  else if (type==SA("int"))
  {
205
    docs+=SA("<br/>");
206 207 208
    docs+=SA("Minimum value: ")+child.attribute(SA("minval"))+SA(", ");
    docs+=SA("maximum value: ")+child.attribute(SA("maxval"))+SA(", ");
    docs+=SA("default value: ")+child.attribute(SA("defval"))+SA(".");
209
    docs+= SA("<br/>");
210 211 212
  }
  else if (type==SA("bool"))
  {
213
    docs+=SA("<br/>");
214 215
    if (child.hasAttribute(SA("altdefval")))
    {
216
      docs+=SA(" The default value is: system dependent.");
217 218 219 220
    }
    else
    {
      QString defval = child.attribute(SA("defval"));
221
      docs+=SA(" The default value is: <code>")+
222 223 224
            (defval==SA("1")?SA("YES"):SA("NO"))+
            SA("</code>.");
    }
225
    docs+= SA("<br/>");
226 227 228 229 230 231 232 233 234 235 236
  }
  else if (type==SA("list"))
  {
    if (child.attribute(SA("format"))==SA("string"))
    {
      int numValues = 0;
      docsVal = child.firstChildElement();
      while (!docsVal.isNull())
      {
        if (docsVal.tagName()==SA("value"))
        {
237 238 239 240 241 242
          QString showDocu = SA("");
          if (docsVal.hasAttribute(SA("show_docu")))
          {
            showDocu = docsVal.attribute(SA("show_docu")).toLower();
          }
          if ((showDocu != SA("no")) && (docsVal.attribute(SA("name"))!=SA(""))) numValues++;
243 244 245 246 247 248 249 250 251 252 253
        }
        docsVal = docsVal.nextSiblingElement();
      }
      if (numValues>0)
      {
        int i = 0;
        docsVal = child.firstChildElement();
        while (!docsVal.isNull())
        {
          if (docsVal.tagName()==SA("value"))
          {
254 255
            QString showDocu = SA("");
            if (docsVal.hasAttribute(SA("show_docu")))
256
            {
257
              showDocu = docsVal.attribute(SA("show_docu")).toLower();
258
            }
259
            if ((showDocu != SA("no")) && (docsVal.attribute(SA("name"))!=SA("")))
260
            {
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
              i++;
              docs += SA("<code>") + docsVal.attribute(SA("name")) + SA("</code>");
              QString desc = docsVal.attribute(SA("desc"));
              if (desc != SA(""))
              {
                docs += SA(" ") + desc;
              }
              if (i==numValues-1)
              {
                docs += SA(" and ");
              }
              else if (i==numValues)
              {
                docs += SA(".");
              }
              else
              {
                docs += SA(", ");
              }
280 281 282 283 284
            }
          }
          docsVal = docsVal.nextSiblingElement();
        }
      }
285
      // docs+= SA("<br/>");
286 287 288 289 290 291 292 293 294
    }
  }
  else if (type==SA("string"))
  {
    QString defval = child.attribute(SA("defval"));
    if (child.attribute(SA("format")) == SA("dir"))
    {
      if (defval != SA(""))
      {
295 296 297
        docs+=SA("<br/>");
        docs += SA(" The default directory is: <code>") + defval + SA("</code>.");
        docs += SA("<br/>");
298 299 300 301 302 303 304
      }
    }
    else if (child.attribute(SA("format")) == SA("file"))
    {
      QString abspath = child.attribute(SA("abspath"));
      if (defval != SA(""))
      {
305
        docs+=SA("<br/>");
306 307
        if (abspath != SA("1"))
        {
308
          docs += SA(" The default file is: <code>") + defval + SA("</code>.");
309 310 311
        }
        else
        {
312
          docs += SA(" The default file (with absolute path) is: <code>") + defval + SA("</code>.");
313
        }
314
        docs += SA("<br/>");
315 316 317 318 319
      }
      else
      {
        if (abspath == SA("1"))
        {
320 321 322
          docs+=SA("<br/>");
          docs += SA(" The file has to be specified with full path.");
          docs += SA("<br/>");
323 324 325 326 327 328 329
        }
      }
    }
    else // if (child.attribute(SA("format")) == SA("string"))
    {
      if (defval != SA(""))
      {
330 331 332
        docs+=SA("<br/>");
        docs += SA(" The default value is: <code>") + defval + SA("</code>.");
        docs += SA("<br/>");
333 334 335 336 337 338 339
      }
    }
  }
  
  if (child.hasAttribute(SA("depends")))
  {
    QString dependsOn = child.attribute(SA("depends"));
340 341
    docs+=SA("<br/>");
    docs+=  SA(" This tag requires that the tag \\ref cfg_");
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
    docs+=  dependsOn.toLower();
    docs+=  SA(" \"");
    docs+=  dependsOn.toUpper();
    docs+=  SA("\" is set to <code>YES</code>.");
  }

  // Remove / replace doxygen markup strings
  // the regular expressions are hard to read so the intention will be given
  QRegExp regexp;
  // remove \n at end and replace by a space
  regexp.setPattern(SA("\\n$"));
  docs.replace(regexp,SA(" "));
  // remove <br> at end
  regexp.setPattern(SA("<br> *$"));
  docs.replace(regexp,SA(" "));
357 358 359 360 361 362 363 364 365 366
  // \c word -> <code>word</code>; word ends with ')', ',', '.' or ' '
  regexp.setPattern(SA("\\\\c[ ]+([^ \\)]+)\\)"));
  docs.replace(regexp,SA("<code>\\1</code>)"));

  regexp.setPattern(SA("\\\\c[ ]+([^ ,]+),"));
  docs.replace(regexp,SA("<code>\\1</code>,"));

  regexp.setPattern(SA("\\\\c[ ]+([^ \\.]+)\\."));
  docs.replace(regexp,SA("<code>\\1</code>."));

367 368 369 370 371
  regexp.setPattern(SA("\\\\c[ ]+([^ ]+) "));
  docs.replace(regexp,SA("<code>\\1</code> "));
  // `word` -> <code>word</code>
  docs.replace(SA("``"),SA(""));
  regexp.setPattern(SA("`([^`]+)`"));
372
  docs.replace(regexp,SA("<code>\\1</code>"));
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
  // \ref key "desc" -> <code>desc</code>
  regexp.setPattern(SA("\\\\ref[ ]+[^ ]+[ ]+\"([^ ]+)\""));
  docs.replace(regexp,SA("<code>\\1</code> "));
  //\ref specials
  // \ref <key> -> description
  regexp.setPattern(SA("\\\\ref[ ]+doxygen_usage"));
  docs.replace(regexp,SA("\"Doxygen usage\""));
  regexp.setPattern(SA("\\\\ref[ ]+extsearch"));
  docs.replace(regexp,SA("\"External Indexing and Searching\""));
  regexp.setPattern(SA("\\\\ref[ ]+external"));
  docs.replace(regexp,SA("\"Linking to external documentation\""));
  // fallback for not handled
  docs.replace(SA("\\\\ref"),SA(""));
  // \b word -> <b>word<\b>
  regexp.setPattern(SA("\\\\b[ ]+([^ ]+) "));
  docs.replace(regexp,SA("<b>\\1</b> "));
  // \e word -> <em>word<\em>
  regexp.setPattern(SA("\\\\e[ ]+([^ ]+) "));
  docs.replace(regexp,SA("<em>\\1</em> "));
  // \note -> <br>Note:
  // @note -> <br>Note:
  docs.replace(SA("\\note"),SA("<br>Note:"));
  docs.replace(SA("@note"),SA("<br>Note:"));
  // \#include -> #include
  // \#undef -> #undef
  docs.replace(SA("\\#include"),SA("#include"));
  docs.replace(SA("\\#undef"),SA("#undef"));
  // -# -> <br>-
  // " - " -> <br>-
  docs.replace(SA("-#"),SA("<br>-"));
  docs.replace(SA(" - "),SA("<br>-"));
  // \verbatim -> <pre>
  // \endverbatim -> </pre>
  docs.replace(SA("\\verbatim"),SA("<pre>"));
  docs.replace(SA("\\endverbatim"),SA("</pre>"));
  // \sa -> <br>See also:
  // \par -> <br>
  docs.replace(SA("\\sa"),SA("<br>See also:"));
  docs.replace(SA("\\par"),SA("<br>"));
  // 2xbackslash -> backslash
  // \@ -> @
  docs.replace(SA("\\\\"),SA("\\"));
  docs.replace(SA("\\@"),SA("@"));
  // \& -> &
  // \$ -> $
  docs.replace(SA("\\&"),SA("&"));
  docs.replace(SA("\\$"),SA("$"));
  // \< -> &lt;
  // \> -> &gt;
  docs.replace(SA("\\<"),SA("&lt;"));
  docs.replace(SA("\\>"),SA("&gt;"));
  regexp.setPattern(SA(" (http:[^ \\)]*)([ \\)])"));
  docs.replace(regexp,SA(" <a href=\"\\1\">\\1</a>\\2"));
  // LaTeX name as formula -> LaTeX
  regexp.setPattern(SA("\\\\f\\$\\\\mbox\\{\\\\LaTeX\\}\\\\f\\$"));
  docs.replace(regexp,SA("LaTeX"));
  // Other forula's (now just 2) so explicitely mentioned.
  regexp.setPattern(SA("\\\\f\\$2\\^\\{\\(16\\+\\\\mbox\\{LOOKUP\\\\_CACHE\\\\_SIZE\\}\\)\\}\\\\f\\$"));
  docs.replace(regexp,SA("2^(16+LOOKUP_CACHE_SIZE)"));
  regexp.setPattern(SA("\\\\f\\$2\\^\\{16\\} = 65536\\\\f\\$"));
  docs.replace(regexp,SA("2^16=65536"));

  return docs.trimmed();
}
437 438 439 440 441 442 443 444 445 446

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())
  {
447 448
    QString setting = child.attribute(SA("setting"));
    if (setting.isEmpty() || IS_SUPPORTED(setting.toAscii()))
449
    {
450
      QString type = child.attribute(SA("type"));
451
      QString docs = getDocsForNode(child);
452 453 454
      if (type==SA("bool"))
      {
        InputBool *boolOption = 
455
          new InputBool(
456 457 458
              layout,row,
              child.attribute(SA("id")),
              child.attribute(SA("defval"))==SA("1"),
459
              docs
460 461
              );
        m_options.insert(
462
            child.attribute(SA("id")),
463 464 465 466
            boolOption
            );
        connect(boolOption,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
        connect(boolOption,SIGNAL(changed()),SIGNAL(changed()));
467
      }
468
      else if (type==SA("string"))
469
      {
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
        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 = 
485
          new InputString(
486 487 488 489
              layout,row,
              child.attribute(SA("id")),
              child.attribute(SA("defval")),
              mode,
490
              docs,
491 492 493
              child.attribute(SA("abspath"))
              );
        m_options.insert(
494
            child.attribute(SA("id")),
495 496 497 498 499 500 501 502
            stringOption
            );
        connect(stringOption,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
        connect(stringOption,SIGNAL(changed()),SIGNAL(changed()));
      }
      else if (type==SA("enum"))
      {
        InputString *enumList = new InputString(
503 504 505 506
            layout,row,
            child.attribute(SA("id")),
            child.attribute(SA("defval")),
            InputString::StringFixed,
507
            docs
508 509 510 511
            );
        QDomElement enumVal = child.firstChildElement();
        while (!enumVal.isNull())
        {
512 513 514 515
          if (enumVal.tagName()==SA("value"))
          {
            enumList->addValue(enumVal.attribute(SA("name")));
          }
516 517 518
          enumVal = enumVal.nextSiblingElement();
        }
        enumList->setDefault();
519

520 521 522
        m_options.insert(child.attribute(SA("id")),enumList);
        connect(enumList,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
        connect(enumList,SIGNAL(changed()),SIGNAL(changed()));
523
      }
524
      else if (type==SA("int"))
525
      {
526 527 528 529 530 531 532
        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(),
533
              docs
534 535 536 537 538 539 540
              );
        m_options.insert(
            child.attribute(SA("id")),
            intOption
            );
        connect(intOption,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
        connect(intOption,SIGNAL(changed()),SIGNAL(changed()));
541
      }
542
      else if (type==SA("list"))
543
      {
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
        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())
        {
566 567 568 569
          if (listVal.tagName()==SA("value"))
          {
            sl.append(listVal.attribute(SA("name")));
          }
570 571 572 573 574 575 576 577
          listVal = listVal.nextSiblingElement();
        }
        InputStrList *listOption = 
          new InputStrList(
              layout,row,
              child.attribute(SA("id")),
              sl,
              mode,
578
              docs
579 580 581 582 583 584 585
              );
        m_options.insert(
            child.attribute(SA("id")),
            listOption
            );
        connect(listOption,SIGNAL(showHelp(Input*)),SLOT(showHelp(Input*)));
        connect(listOption,SIGNAL(changed()),SIGNAL(changed()));
586
      }
587
      else if (type==SA("obsolete"))
588
      {
589
        // ignore
590
      }
591
      else // should not happen
592
      {
593
        printf("Unsupported type %s\n",qPrintable(child.attribute(SA("type"))));
594
      }
595
    } // IS_SUPPORTED
596 597 598 599 600 601 602
    child = child.nextSiblingElement();
  }

  // compute dependencies between options
  child = elem.firstChildElement();
  while (!child.isNull())
  {
603
    QString setting = child.attribute(SA("setting"));
604 605
    QString dependsOn = child.attribute(SA("depends"));
    QString id        = child.attribute(SA("id"));
606 607
    if (!dependsOn.isEmpty() && 
        (setting.isEmpty() || IS_SUPPORTED(setting.toAscii())))
608 609
    {
       Input *parentOption = m_options[dependsOn];
610 611 612 613 614
       if (parentOption==0)
       {
         printf("%s has depends=%s that is not valid\n",
             qPrintable(id),qPrintable(dependsOn));
       }
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
       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());
    if (i.value())
    {
669
      //printf("Loading key %s: type=%d value='%s'\n",qPrintable(i.key()),var.type(),qPrintable(var.toString()));
670 671 672 673 674 675 676
      i.value()->value() = var;
      i.value()->update();
    }
  }
}

void Expert::saveSettings(QSettings *s)
677
{
678 679 680 681
  QHashIterator<QString,Input*> i(m_options);
  while (i.hasNext()) 
  {
    i.next();
682
    //printf("Saving key %s: type=%d value='%s'\n",qPrintable(i.key()),i.value()->value().type(),qPrintable(i.value()->value().toString()));
683 684
    if (i.value())
    {
685
      s->setValue(SA("config/")+i.key(),i.value()->value());
686 687
    }
  }
688 689
}

690
void Expert::loadConfig(const QString &fileName)
691
{
692 693
  //printf("Expert::loadConfig(%s)\n",qPrintable(fileName));
  parseConfig(fileName,m_options);
694 695
}

696 697
void Expert::saveTopic(QTextStream &t,QDomElement &elem,QTextCodec *codec,
                       bool brief)
698
{
699 700 701 702
  if (!brief)
  {
    t << endl;
  }
703 704 705 706 707 708
  t << "#---------------------------------------------------------------------------" << endl;
  t << "# " << elem.attribute(SA("docs")) << endl;
  t << "#---------------------------------------------------------------------------" << endl;
  // write options...
  QDomElement childElem = elem.firstChildElement();
  while (!childElem.isNull())
709
  {
710
    QString setting = childElem.attribute(SA("setting"));
711 712
    QString type = childElem.attribute(SA("type"));
    QString name = childElem.attribute(SA("id"));
713
    if (setting.isEmpty() || IS_SUPPORTED(setting.toAscii()))
714
    {
715 716
      QHash<QString,Input*>::const_iterator i = m_options.find(name);
      if (i!=m_options.end())
717
      {
718 719 720 721 722 723 724
        Input *option = i.value();
        if (option && !brief)
        {
          t << endl;
          t << convertToComment(option->templateDocs());
          t << endl;
        }
725
        t << name.leftJustified(MAX_OPTION_LENGTH) << "= ";
726 727 728 729
        if (option)
        {
          option->writeValue(t,codec);
        }
730 731
        t << endl;
      }
732
    }
733 734 735 736 737 738
    childElem = childElem.nextSiblingElement();
  }
}

bool Expert::writeConfig(QTextStream &t,bool brief)
{
739 740
  // write global header
  t << "# Doxyfile " << versionString << endl << endl; 
741 742
  if (!brief)
  {
743
    t << convertToComment(m_header);
744 745 746 747 748 749 750 751
  }

  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
752
    {
753
      codec = QTextCodec::codecForName("UTF-8");
754 755
    }
  }
756 757 758
  QDomElement childElem = m_rootElement.firstChildElement();
  while (!childElem.isNull())
  {
759 760 761 762
    if (childElem.tagName()==SA("group"))
    {
      saveTopic(t,childElem,codec,brief);
    }
763 764 765
    childElem = childElem.nextSiblingElement();
  }
  return true;
766 767
}

768
QByteArray Expert::saveInnerState () const
769
{
770 771 772 773 774 775 776 777 778 779
  return m_splitter->saveState();
}

bool Expert::restoreInnerState ( const QByteArray & state )
{
  return m_splitter->restoreState(state);
}

void Expert::showHelp(Input *option)
{
Dimitri van Heesch's avatar
Dimitri van Heesch committed
780 781 782 783 784 785
  if (!m_inShowHelp)
  {
    m_inShowHelp = TRUE;
    m_helper->setText(
        QString::fromAscii("<qt><b>")+option->id()+
        QString::fromAscii("</b><br>")+
786
        QString::fromAscii("<br/>")+
Dimitri van Heesch's avatar
Dimitri van Heesch committed
787 788
        option->docs().
        replace(QChar::fromAscii('\n'),QChar::fromAscii(' '))+
789
        QString::fromAscii("</qt>")
Dimitri van Heesch's avatar
Dimitri van Heesch committed
790 791 792
        );
    m_inShowHelp = FALSE;
  }
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
}

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()) 
816
  {
817 818 819 820 821
    i.next();
    if (i.value())
    {
      i.value()->reset();
    }
822 823 824
  }
}

825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
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"));
851
  if (!generateHtml || workingDir.isEmpty()) return false;
852 853 854 855 856 857
  QString indexFile = getHtmlOutputIndex(workingDir);
  QFileInfo fi(indexFile);
  return fi.exists() && fi.isFile();
}

QString Expert::getHtmlOutputIndex(const QString &workingDir) const
858
{
859 860 861 862 863 864
  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
865
  {
866
    indexFile = outputDir;
867
  }
868 869 870 871 872 873 874 875 876
  else // append
  { 
    indexFile += QString::fromAscii("/")+outputDir;
  }
  if (QFileInfo(htmlOutputDir).isAbsolute()) // override
  {
    indexFile = htmlOutputDir;
  }
  else // append
877
  {
878
    indexFile += QString::fromAscii("/")+htmlOutputDir;
879
  }
880 881
  indexFile+=QString::fromAscii("/index.html");
  return indexFile;
882 883
}

884
bool Expert::pdfOutputPresent(const QString &workingDir) const
Dimitri van Heesch's avatar
Dimitri van Heesch committed
885
{
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
  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();
Dimitri van Heesch's avatar
Dimitri van Heesch committed
902 903
}