/* * Copyright (C) 1997-2022 Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. * * Documents produced by Doxygen are derivative works derived from the * input used in their production; they are not affected by this license. */ #include #include #include #include #include #include #include "config.h" #include "debug.h" #include "doxygen.h" #include "groupdef.h" #include "memberdef.h" #include "message.h" #include "qhp.h" #include "textstream.h" #include "util.h" static inline void writeIndent(TextStream &t,int indent) { if (Debug::isFlagSet(Debug::Qhp)) { for (int i=0;i> children; }; Node m_root; Node *m_current = &m_root; void traverse(const Node &root,TextStream &t,int indent) const { /* Input: Output: * ================================================= * Section1
* Dir1 * Section2
* Dir2 * Section3
*
*
* Section4
*
* Section6
*/ size_t numChildren = root.children.size(); size_t i=0; while (itype==Node::Type::Section) { i++; if (itype==Node::Type::Dir) { // we have a dir node writeIndent(t,indent); t << "
title) << "\"" << " ref=\"" << convertToXML(root.children[i-1]->ref) << "\">\n"; while (itype==Node::Type::Dir) { traverse(*root.children[i].get(),t,indent+1); i++; } writeIndent(t,indent); t << "
\n"; } else // we have a leaf section node { writeIndent(t,indent); t << "
title) << "\"" << " ref=\"" << convertToXML(root.children[i-1]->ref) << "\"/>\n"; } } else // dir without preceding section (no extra indent) { traverse(*root.children[i].get(),t,indent); i++; } } } public: void addSection(const QCString &title,const QCString &ref) { m_current->children.push_back(std::make_unique(m_current,title,ref)); } void incLevel() { auto newNode = new Node(m_current); m_current->children.push_back(std::unique_ptr(newNode)); m_current = newNode; } void decLevel() { assert(m_current->parent!=0); if (m_current->parent) { m_current = m_current->parent; } } void writeToc(TextStream &t) const { writeIndent(t,2); t << "\n"; traverse(m_root,t,3); writeIndent(t,2); t << "\n"; } }; class Qhp::Private { public: std::ofstream docFile; TextStream doc; TextStream index; StringSet files; QhpSectionTree sectionTree; }; static QCString getFullProjectName() { QCString projectName = Config_getString(PROJECT_NAME); QCString versionText = Config_getString(PROJECT_NUMBER); if (projectName.isEmpty()) projectName="Root"; if (!versionText.isEmpty()) projectName+=" "+versionText; return projectName; } static QCString makeFileName(const QCString & withoutExtension) { QCString result=withoutExtension; if (!result.isEmpty()) { if (result.at(0)=='!') // relative URL -> strip marker { result=result.mid(1); } else // add specified HTML extension { result = addHtmlExtensionIfMissing(result); } } return result; } static QCString makeRef(const QCString & withoutExtension, const QCString & anchor) { //printf("QHP::makeRef(%s,%s)\n",withoutExtension,anchor); if (withoutExtension.isEmpty()) return QCString(); QCString result = makeFileName(withoutExtension); if (anchor.isEmpty()) return result; return result+"#"+anchor; } Qhp::Qhp() : p(std::make_unique()) {} Qhp::~Qhp() = default; Qhp::Qhp(Qhp &&) = default; void Qhp::initialize() { /* mycompany.com.myapplication.1_0 doc myapp 1.0 myapp 1.0 .. */ QCString fileName = Config_getString(HTML_OUTPUT) + "/" + qhpFileName; p->docFile.open( fileName.str(), std::ofstream::out | std::ofstream::binary); if (!p->docFile.is_open()) { term("Could not open file %s for writing\n", fileName.data()); } p->doc.setStream(&p->docFile); p->doc << "\n"; p->doc << "\n"; writeIndent(p->doc,1); p->doc << "" << convertToXML(Config_getString(QHP_NAMESPACE)) << "\n"; writeIndent(p->doc,1); p->doc << "" << convertToXML(Config_getString(QHP_VIRTUAL_FOLDER)) << "\n"; // Add custom filter QCString filterName = Config_getString(QHP_CUST_FILTER_NAME); if (!filterName.isEmpty()) { writeIndent(p->doc,1); p->doc << "\n"; StringVector customFilterAttributes = split(Config_getString(QHP_CUST_FILTER_ATTRS).str(), " "); for (const auto &attr : customFilterAttributes) { writeIndent(p->doc,2); p->doc << "" << convertToXML(QCString(attr)) << "\n"; } writeIndent(p->doc,1); p->doc << "\n"; } writeIndent(p->doc,1); p->doc << "\n"; // Add section attributes StringVector sectionFilterAttributes = split(Config_getString(QHP_SECT_FILTER_ATTRS).str(), " "); // always add doxygen as filter attribute if (std::find(sectionFilterAttributes.begin(), sectionFilterAttributes.end(), "doxygen") == sectionFilterAttributes.end()) { sectionFilterAttributes.push_back("doxygen"); } for (const auto &attr : sectionFilterAttributes) { writeIndent(p->doc,2); p->doc << "" << convertToXML(QCString(attr)) << "\n"; } // Add extra root node to the TOC p->sectionTree.addSection(getFullProjectName(),"index"+Doxygen::htmlFileExtension); p->sectionTree.incLevel(); writeIndent(p->index,2); p->index << "\n"; } void Qhp::finalize() { // close root node p->sectionTree.decLevel(); // Finish TOC p->sectionTree.writeToc(p->doc); // Finish index writeIndent(p->index,2); p->index << "\n"; p->doc << p->index.str(); // Finish files writeIndent(p->doc,2); p->doc << "\n"; for (auto &s : p->files) { writeIndent(p->doc,3); p->doc << s.c_str() << "\n"; } writeIndent(p->doc,2); p->doc << "\n"; writeIndent(p->doc,1); p->doc << "\n"; p->doc << "\n"; p->doc.flush(); p->docFile.close(); } void Qhp::incContentsDepth() { p->sectionTree.incLevel(); } void Qhp::decContentsDepth() { p->sectionTree.decLevel(); } void Qhp::addContentsItem(bool isDir, const QCString & name, const QCString & /*ref*/, const QCString & file, const QCString &anchor, bool /* separateIndex */, bool /* addToNavIndex */, const Definition * /*def*/) { /*
*/ QCString f = file; if (!f.isEmpty() && f.at(0)=='^') return; // absolute URL not supported QCString finalRef = makeRef(f, anchor); p->sectionTree.addSection(name,finalRef); } void Qhp::addIndexItem(const Definition *context,const MemberDef *md, const QCString §ionAnchor,const QCString &word) { (void)word; //printf("addIndexItem(%s %s %s\n", // context?context->name().data():"", // md?md->name().data():"", // qPrint(word)); if (md) // member { bool separateMemberPages = Config_getBool(SEPARATE_MEMBER_PAGES); if (context==0) // global member { if (md->getGroupDef()) context = md->getGroupDef(); else if (md->getFileDef()) context = md->getFileDef(); } if (context==0) return; // should not happen QCString cfname = md->getOutputFileBase(); QCString argStr = md->argsString(); QCString cfiname = context->getOutputFileBase(); QCString level1 = context->name(); QCString level2 = !word.isEmpty() ? word : md->name(); QCString contRef = separateMemberPages ? cfname : cfiname; QCString anchor = !sectionAnchor.isEmpty() ? sectionAnchor : md->anchor(); QCString ref; // ref = makeRef(contRef, anchor); QCString id = level1+"::"+level2; writeIndent(p->index,3); p->index << "\n"; } else if (context) // container { // QCString contRef = context->getOutputFileBase(); QCString level1 = !word.isEmpty() ? word : context->name(); QCString ref = makeRef(contRef,sectionAnchor); writeIndent(p->index,3); p->index << "\n"; } } void Qhp::addFile(const QCString & fileName) { p->files.insert(("" + convertToXML(fileName) + "").str()); } void Qhp::addIndexFile(const QCString & fileName) { addFile(fileName); } void Qhp::addImageFile(const QCString &fileName) { addFile(fileName); } void Qhp::addStyleSheetFile(const QCString &fileName) { addFile(fileName); } QCString Qhp::getQchFileName() { QCString const & qchFile = Config_getString(QCH_FILE); if (!qchFile.isEmpty()) { return qchFile; } QCString const & projectName = Config_getString(PROJECT_NAME); QCString const & versionText = Config_getString(PROJECT_NUMBER); return QCString("../qch/") + (projectName.isEmpty() ? QCString("index") : projectName) + (versionText.isEmpty() ? QCString("") : QCString("-") + versionText) + QCString(".qch"); }