#include #include #include "expert.h" #include "inputbool.h" #include "inputstring.h" #include "inputint.h" #include "inputstring.h" #include "inputstrlist.h" #include "config.h" #include "version.h" #include "configdoc.h" #include "settings.h" #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# ")).replace(SA("# \n"), SA("#\n"))+ SA("\n"); } } 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)); } } //------------------------------------------------------------------------------------ Expert::Expert() { m_treeWidget = new QTreeWidget; m_treeWidget->setColumnCount(1); m_topicStack = new QStackedWidget; m_inShowHelp = FALSE; 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 QTextBrowser; m_helper->setReadOnly(true); m_helper->setOpenExternalLinks(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())); addConfigDocs(this); } Expert::~Expert() { QHashIterator i(m_options); while (i.hasNext()) { i.next(); delete i.value(); } } void Expert::createTopics(const QDomElement &rootElem) { QList items; QDomElement childElem = rootElem.firstChildElement(); while (!childElem.isNull()) { if (childElem.tagName()==SA("group")) { // Remove _ from a group name like: Source_Browser QString name = childElem.attribute(SA("name")).replace(SA("_"),SA(" ")); 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 *))); } 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(); } docs += SA("
"); } docsVal = docsVal.nextSiblingElement(); } // for an enum we list the values if (type==SA("enum")) { docs += SA("
"); 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("") + docsVal.attribute(SA("name")) + SA(""); 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("
"); docs+=SA("
"); docs+=SA(" The default value is: ")+ child.attribute(SA("defval"))+ SA("."); docs+= SA("
"); } else if (type==SA("int")) { docs+=SA("
"); 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("."); docs+= SA("
"); } else if (type==SA("bool")) { docs+=SA("
"); if (child.hasAttribute(SA("altdefval"))) { docs+=SA(" The default value is: system dependent."); } else { QString defval = child.attribute(SA("defval")); docs+=SA(" The default value is: ")+ (defval==SA("1")?SA("YES"):SA("NO"))+ SA("."); } docs+= SA("
"); } 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")) { 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++; } docsVal = docsVal.nextSiblingElement(); } if (numValues>0) { int i = 0; docsVal = child.firstChildElement(); while (!docsVal.isNull()) { if (docsVal.tagName()==SA("value")) { 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(""))) { i++; docs += SA("") + docsVal.attribute(SA("name")) + SA(""); 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(", "); } } } docsVal = docsVal.nextSiblingElement(); } } // docs+= SA("
"); } } else if (type==SA("string")) { QString defval = child.attribute(SA("defval")); if (child.attribute(SA("format")) == SA("dir")) { if (defval != SA("")) { docs+=SA("
"); docs += SA(" The default directory is: ") + defval + SA("."); docs += SA("
"); } } else if (child.attribute(SA("format")) == SA("file")) { QString abspath = child.attribute(SA("abspath")); if (defval != SA("")) { docs+=SA("
"); if (abspath != SA("1")) { docs += SA(" The default file is: ") + defval + SA("."); } else { docs += SA(" The default file (with absolute path) is: ") + defval + SA("."); } docs += SA("
"); } else { if (abspath == SA("1")) { docs+=SA("
"); docs += SA(" The file has to be specified with full path."); docs += SA("
"); } } } else if (child.attribute(SA("format")) == SA("image")) { QString abspath = child.attribute(SA("abspath")); if (defval != SA("")) { docs+=SA("
"); if (abspath != SA("1")) { docs += SA(" The default image is: ") + defval + SA("."); } else { docs += SA(" The default image (with absolute path) is: ") + defval + SA("."); } docs += SA("
"); } else { if (abspath == SA("1")) { docs+=SA("
"); docs += SA(" The image has to be specified with full path."); docs += SA("
"); } } } else // if (child.attribute(SA("format")) == SA("string")) { if (defval != SA("")) { docs+=SA("
"); docs += SA(" The default value is: ") + defval + SA("."); docs += SA("
"); } } } if (child.hasAttribute(SA("depends"))) { QString dependsOn = child.attribute(SA("depends")); docs+=SA("
"); docs+= SA(" This tag requires that the tag \\ref cfg_"); docs+= dependsOn.toLower(); docs+= SA(" \""); docs+= dependsOn.toUpper(); docs+= SA("\" is set to YES."); } // 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
at end regexp.setPattern(SA("
*$")); docs.replace(regexp,SA(" ")); // \c word -> word; word ends with ')', ',', '.' or ' ' regexp.setPattern(SA("\\\\c[ ]+([^ \\)]+)\\)")); docs.replace(regexp,SA("\\1)")); regexp.setPattern(SA("\\\\c[ ]+([^ ,]+),")); docs.replace(regexp,SA("\\1,")); regexp.setPattern(SA("\\\\c[ ]+([^ \\.]+)\\.")); docs.replace(regexp,SA("\\1.")); regexp.setPattern(SA("\\\\c[ ]+([^ ]+) ")); docs.replace(regexp,SA("\\1 ")); // `word` -> word docs.replace(SA("``"),SA("")); regexp.setPattern(SA("`([^`]+)`")); docs.replace(regexp,SA("\\1")); // \ref key "desc" -> desc regexp.setPattern(SA("\\\\ref[ ]+[^ ]+[ ]+\"([^ ]+)\"")); docs.replace(regexp,SA("\\1 ")); //\ref specials // \ref -> 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 -> word<\b> regexp.setPattern(SA("\\\\b[ ]+([^ ]+) ")); docs.replace(regexp,SA("\\1 ")); // \e word -> word<\em> regexp.setPattern(SA("\\\\e[ ]+([^ ]+) ")); docs.replace(regexp,SA("\\1 ")); // \note ->
Note: // @note ->
Note: docs.replace(SA("\\note"),SA("
Note:")); docs.replace(SA("@note"),SA("
Note:")); // \#include -> #include // \#undef -> #undef docs.replace(SA("\\#include"),SA("#include")); docs.replace(SA("\\#undef"),SA("#undef")); // -# ->
- // " - " ->
- docs.replace(SA("-#"),SA("
-")); docs.replace(SA(" - "),SA("
-")); // \verbatim ->
  // \endverbatim -> 
docs.replace(SA("\\verbatim"),SA("
"));
  docs.replace(SA("\\endverbatim"),SA("
")); // \sa ->
See also: // \par ->
docs.replace(SA("\\sa"),SA("
See also:")); docs.replace(SA("\\par"),SA("
")); // 2xbackslash -> backslash // \@ -> @ docs.replace(SA("\\\\"),SA("\\")); docs.replace(SA("\\@"),SA("@")); // \& -> & // \$ -> $ docs.replace(SA("\\&"),SA("&")); docs.replace(SA("\\$"),SA("$")); // \< -> < // \> -> > docs.replace(SA("\\<"),SA("<")); docs.replace(SA("\\>"),SA(">")); regexp.setPattern(SA(" (http:[^ \\)]*)([ \\)])")); docs.replace(regexp,SA(" \\1\\2")); // LaTeX name as formula -> LaTeX regexp.setPattern(SA("\\\\f\\$\\\\mbox\\{\\\\LaTeX\\}\\\\f\\$")); docs.replace(regexp,SA("LaTeX")); // Other formula's (now just 2) so explicitly 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(); } 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 setting = child.attribute(SA("setting")); if (setting.isEmpty() || IS_SUPPORTED(setting.toAscii())) { QString type = child.attribute(SA("type")); QString docs = getDocsForNode(child); if (type==SA("bool")) { InputBool *boolOption = new InputBool( layout,row, child.attribute(SA("id")), child.attribute(SA("defval"))==SA("1"), 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 if (format==SA("image")) { mode = InputString::StringImage; } else // format=="string" { mode = InputString::StringFree; } InputString *stringOption = new InputString( layout,row, child.attribute(SA("id")), child.attribute(SA("defval")), mode, 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, docs ); QDomElement enumVal = child.firstChildElement(); while (!enumVal.isNull()) { if (enumVal.tagName()==SA("value")) { 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(), 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()) { if (listVal.tagName()==SA("value")) { sl.append(listVal.attribute(SA("name"))); } listVal = listVal.nextSiblingElement(); } InputStrList *listOption = new InputStrList( layout,row, child.attribute(SA("id")), sl, mode, 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")))); } } // IS_SUPPORTED child = child.nextSiblingElement(); } // compute dependencies between options child = elem.firstChildElement(); while (!child.isNull()) { QString setting = child.attribute(SA("setting")); QString dependsOn = child.attribute(SA("depends")); QString id = child.attribute(SA("id")); if (!dependsOn.isEmpty() && (setting.isEmpty() || IS_SUPPORTED(setting.toAscii()))) { Input *parentOption = m_options[dependsOn]; if (parentOption==0) { printf("%s has depends=%s that is not valid\n", qPrintable(id),qPrintable(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 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(true); } } void Expert::loadSettings(QSettings *s) { QHashIterator i(m_options); while (i.hasNext()) { i.next(); QVariant var = s->value(SA("config/")+i.key()); if (i.value()) { //printf("Loading key %s: type=%d value='%s'\n",qPrintable(i.key()),var.type(),qPrintable(var.toString())); i.value()->value() = var; i.value()->update(); } } } void Expert::saveSettings(QSettings *s) { QHashIterator i(m_options); while (i.hasNext()) { i.next(); //printf("Saving key %s: type=%d value='%s'\n",qPrintable(i.key()),i.value()->value().type(),qPrintable(i.value()->value().toString())); if (i.value()) { s->setValue(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) { if (!brief) { t << endl; } t << "#---------------------------------------------------------------------------" << endl; t << "# " << elem.attribute(SA("docs")) << endl; t << "#---------------------------------------------------------------------------" << endl; // write options... QDomElement childElem = elem.firstChildElement(); while (!childElem.isNull()) { QString setting = childElem.attribute(SA("setting")); QString type = childElem.attribute(SA("type")); QString name = childElem.attribute(SA("id")); if (setting.isEmpty() || IS_SUPPORTED(setting.toAscii())) { QHash::const_iterator i = m_options.find(name); if (i!=m_options.end()) { Input *option = i.value(); if (option && !brief) { t << endl; t << convertToComment(option->templateDocs()); t << endl; } t << name.leftJustified(MAX_OPTION_LENGTH) << "= "; if (option) { option->writeValue(t,codec); } t << endl; } } childElem = childElem.nextSiblingElement(); } } bool Expert::writeConfig(QTextStream &t,bool brief) { // write global header t << "# Doxyfile " << versionString << endl << endl; if (!brief) { t << convertToComment(m_header); } 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()) { if (childElem.tagName()==SA("group")) { 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) { if (!m_inShowHelp) { m_inShowHelp = TRUE; m_helper->setText( QString::fromAscii("")+option->id()+ QString::fromAscii("
")+ QString::fromAscii("
")+ option->docs(). replace(QChar::fromAscii('\n'),QChar::fromAscii(' '))+ QString::fromAscii("
") ); m_inShowHelp = FALSE; } } void Expert::nextTopic() { if (m_topicStack->currentIndex()+1==m_topicStack->count()) // last topic { done(); } else { 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 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&model,const QString &name) { Input *option = model[name]; Q_ASSERT(option!=0); return stringVariantToBool(option->value()); } static QString getStringOption( const QHash&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(); } void Expert::refresh() { m_treeWidget->setCurrentItem(m_treeWidget->invisibleRootItem()->child(0)); }