Doxygen
Loading...
Searching...
No Matches
qhp.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 1997-2022 Dimitri van Heesch.
3 *
4 * Permission to use, copy, modify, and distribute this software and its
5 * documentation under the terms of the GNU General Public License is hereby
6 * granted. No representations are made about the suitability of this software
7 * for any purpose. It is provided "as is" without express or implied warranty.
8 * See the GNU General Public License for more details.
9 *
10 * Documents produced by Doxygen are derivative works derived from the
11 * input used in their production; they are not affected by this license.
12 */
13
14// own header
15#include "qhp.h"
16
17// standard includes
18#include <algorithm>
19#include <memory>
20#include <string.h>
21#include <vector>
22#include <mutex>
23
24// other includes
25#include "config.h"
26#include "debug.h"
27#include "doxygen.h"
28#include "groupdef.h"
29#include "language.h"
30#include "memberdef.h"
31#include "message.h"
32#include "portable.h"
33#include "stringutil.h"
34#include "textstream.h"
35#include "util.h"
36#include "version.h"
37
38static std::once_flag g_blankWritten;
39
40
41static inline void writeIndent(TextStream &t,int indent)
42{
44 {
45 for (int i=0;i<indent;i++) t << " ";
46 }
47}
48
50{
51 private:
52 struct Node
53 {
54 enum class Type { Root, Dir, Section };
55 // constructor for root node
56 Node() : type(Type::Root), parent(nullptr) {}
57 // constructor for dir node
58 Node(Node *parent_) : type(Type::Dir), parent(parent_) {}
59 // constructor for section node
60 Node(Node *parent_, const DString &title_,const DString &ref_)
61 : type(Type::Section), parent(parent_), title(title_), ref(ref_) {}
63 Node *parent = nullptr;
66 std::vector<std::unique_ptr<Node>> children;
67 };
68
71
72 void traverse(const Node &root,TextStream &t,int indent) const
73 {
74 /* Input: Output:
75 * =================================================
76 * Section1 <section title=".." ref="..">
77 * Dir1
78 * Section2 <section title=".." ref="..">
79 * Dir2
80 * Section3 <section title=".." ref=".."/>
81 * </section>
82 * </section>
83 * Section4 <section title=".." ref="..>
84 * Dir3
85 * Dir4
86 * Section5 <section title=.." ref=.."/>
87 * </section>
88 * Section6 <section title=".." ref=".."/>
89 */
90 size_t numChildren = root.children.size();
91 size_t i=0;
92 while (i<numChildren)
93 {
94 if (root.children[i]->type==Node::Type::Section)
95 {
96 i++;
97 if (i<numChildren && root.children[i]->type==Node::Type::Dir)
98 {
99 // we have a dir node
100 writeIndent(t,indent);
101 t << "<section title=\"" << convertToXML(root.children[i-1]->title) << "\""
102 << " ref=\"" << convertToXML(root.children[i-1]->ref) << "\">\n";
103 while (i<numChildren && root.children[i]->type==Node::Type::Dir)
104 {
105 traverse(*root.children[i].get(),t,indent+1);
106 i++;
107 }
108 writeIndent(t,indent);
109 t << "</section>\n";
110 }
111 else // we have a leaf section node
112 {
113 writeIndent(t,indent);
114 t << "<section title=\"" << convertToXML(root.children[i-1]->title) << "\""
115 << " ref=\"" << convertToXML(root.children[i-1]->ref) << "\"/>\n";
116 }
117 }
118 else // dir without preceding section (no extra indent)
119 {
120 traverse(*root.children[i].get(),t,indent);
121 i++;
122 }
123 }
124 }
125
126 public:
127 void addSection(const DString &title,const DString &ref)
128 {
129 m_current->children.push_back(std::make_unique<Node>(m_current,title,ref));
130 }
131 void incLevel()
132 {
133 auto newNode = new Node(m_current);
134 m_current->children.push_back(std::unique_ptr<Node>(newNode));
135 m_current = newNode;
136 }
137 void decLevel()
138 {
139 ASSERT(m_current->parent!=nullptr);
140 if (m_current->parent)
141 {
143 }
144 }
145 void writeToc(TextStream &t) const
146 {
147 writeIndent(t,2);
148 t << "<toc>\n";
149 traverse(m_root,t,3);
150 writeIndent(t,2);
151 t << "</toc>\n";
152 }
153};
154
164
166{
167 DString projectName = Config_getString(PROJECT_NAME);
168 DString versionText = Config_getString(PROJECT_NUMBER);
169 if (projectName.empty()) projectName="Root";
170 if (!versionText.empty()) projectName+=" "+versionText;
171 return projectName;
172}
173
174static DString makeFileName(const DString & withoutExtension)
175{
176 DString result=withoutExtension;
177 if (!result.empty())
178 {
179 if (result.at(0)=='!') // relative URL -> strip marker
180 {
181 result=result.mid(1);
182 }
183 else // add specified HTML extension
184 {
186 }
187 }
188 return result;
189}
190
191static DString makeRef(const DString & withoutExtension, const DString & anchor)
192{
193 //printf("QHP::makeRef(%s,%s)\n",withoutExtension,anchor);
194 if (withoutExtension.empty()) return DString();
195 DString result = makeFileName(withoutExtension);
196 if (anchor.empty()) return result;
197 return result+"#"+anchor;
198}
199
200Qhp::Qhp() : p(std::make_unique<Private>()) {}
201Qhp::~Qhp() = default;
202
204{
205 /*
206 <QtHelpProject version="1.0">
207 <namespace>mycompany.com.myapplication.1_0</namespace>
208 <virtualFolder>doc</virtualFolder>
209 <customFilter name="My Application 1.0">
210 <filterAttribute>myapp</filterAttribute>
211 <filterAttribute>1.0</filterAttribute>
212 </customFilter>
213 <filterSection>
214 <filterAttribute>myapp</filterAttribute>
215 <filterAttribute>1.0</filterAttribute>
216 ..
217 */
218 DString fileName = Config_getString(HTML_OUTPUT) + "/" + qhpFileName;
219 p->docFile = Portable::openOutputStream(fileName);
220 if (!p->docFile.is_open())
221 {
222 term("Could not open file {} for writing\n",fileName);
223 }
224 p->doc.setStream(&p->docFile);
225
226 p->doc << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
227 p->doc << "<QtHelpProject version=\"1.0\">\n";
228 writeIndent(p->doc,1);
229 p->doc << "<namespace>" << convertToXML(Config_getString(QHP_NAMESPACE)) << "</namespace>\n";
230 writeIndent(p->doc,1);
231 p->doc << "<virtualFolder>" << convertToXML(Config_getString(QHP_VIRTUAL_FOLDER)) << "</virtualFolder>\n";
232
233 // Add custom filter
234 DString filterName = Config_getString(QHP_CUST_FILTER_NAME);
235 if (!filterName.empty())
236 {
237 writeIndent(p->doc,1);
238 p->doc << "<customFilter name=\"" << convertToXML(filterName) << "\">\n";
239
240 StringVector customFilterAttributes =
241 split(Config_getString(QHP_CUST_FILTER_ATTRS).str(), " ");
242 for (const auto &attr : customFilterAttributes)
243 {
244 writeIndent(p->doc,2);
245 p->doc << "<filterAttribute>" << convertToXML(attr) << "</filterAttribute>\n";
246 }
247 writeIndent(p->doc,1);
248 p->doc << "</customFilter>\n";
249 }
250
251 writeIndent(p->doc,1);
252 p->doc << "<filterSection>\n";
253
254 // Add section attributes
255 StringVector sectionFilterAttributes = split(Config_getString(QHP_SECT_FILTER_ATTRS).str(), " ");
256 // always add doxygen as filter attribute
257 if (std::find(sectionFilterAttributes.begin(), sectionFilterAttributes.end(), "doxygen") ==
258 sectionFilterAttributes.end())
259 {
260 sectionFilterAttributes.emplace_back("doxygen");
261 }
262 for (const auto &attr : sectionFilterAttributes)
263 {
264 writeIndent(p->doc,2);
265 p->doc << "<filterAttribute>" << convertToXML(attr) << "</filterAttribute>\n";
266 }
267
268 // Add extra root node to the TOC
269 p->sectionTree.addSection(getFullProjectName(),"index"+Doxygen::htmlFileExtension);
270 p->sectionTree.incLevel();
271
272 writeIndent(p->index,2);
273 p->index << "<keywords>\n";
274}
275
277{
278 // close root node
279 p->sectionTree.decLevel();
280
281 // Finish TOC
282 p->sectionTree.writeToc(p->doc);
283
284 // Finish index
285 writeIndent(p->index,2);
286 p->index << "</keywords>\n";
287 p->doc << p->index.str();
288
289 // Finish files
290 writeIndent(p->doc,2);
291 p->doc << "<files>\n";
292 for (auto &s : p->files)
293 {
294 writeIndent(p->doc,3);
295 p->doc << s << "\n";
296 }
297 writeIndent(p->doc,2);
298 p->doc << "</files>\n";
299
300 writeIndent(p->doc,1);
301 p->doc << "</filterSection>\n";
302 p->doc << "</QtHelpProject>\n";
303
304 p->doc.flush();
305 p->docFile.close();
306}
307
309{
310 p->sectionTree.incLevel();
311}
312
314{
315 p->sectionTree.decLevel();
316}
317
318void Qhp::addContentsItem(bool /* isDir */, const DString & name, const DString & /*ref*/,
319 const DString & file, const DString &anchor, bool /* separateIndex */,
320 bool /* addToNavIndex */, const Definition * /*def*/, const DString & /*nameAsHtml*/)
321{
322 /*
323 <toc>
324 <section title="My Application Manual" ref="index.html">
325 <section title="Chapter 1" ref="doc.html#chapter1"/>
326 <section title="Chapter 2" ref="doc.html#chapter2"/>
327 <section title="Chapter 3" ref="doc.html#chapter3"/>
328 </section>
329 </toc>
330 */
331
332 DString f = file;
333 if (!f.empty() && f.at(0)=='^') return; // absolute URL not supported
334
335 if (f.empty())
336 {
337 f = "doxygen_blank";
339 std::call_once(g_blankWritten,[this,&f]()
340 {
341 DString fileName = Config_getString(HTML_OUTPUT) + "/" + f;
342 std::ofstream blankFile = Portable::openOutputStream(fileName); // we just need an empty file
343 if (!blankFile.is_open())
344 {
345 term("Could not open file {} for writing\n",fileName);
346 }
347 TextStream blank;
348 blank.setStream(&blankFile);
349 blank << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
350 blank << "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"" + theTranslator->trISOLang() + "\">\n";
351 blank << "<head>\n";
352 blank << "<title>Validator / crawler helper</title>\n";
353 blank << "<meta http-equiv=\"Content-Type\" content=\"text/xhtml;charset=UTF-8\"/>\n";
354 blank << "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=11\"/>\n";
355
356 blank << "<meta name=\"generator\" content=\"Doxygen " + getDoxygenVersion() + "\"/>\n";
357 blank << "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n";
358 blank << "</head>\n";
359 blank << "<body>\n";
360 blank << "</body>\n";
361 blank << "</html>\n";
362 blank.flush();
363 blankFile.close();
364 addFile(f);
365 });
366 }
367 DString finalRef = makeRef(f, anchor);
368 p->sectionTree.addSection(name,finalRef);
369}
370
371void Qhp::addIndexItem(const Definition *context,const MemberDef *md,
372 const DString &sectionAnchor,const DString &word)
373{
374 //printf("addIndexItem(%s %s %s\n",
375 // context?context->name().data():"<none>",
376 // md?md->name().data():"<none>",
377 // qPrint(word));
378
379 if (context && md) // member
380 {
381 if (sectionAnchor.empty() && !md->hasDocumentation()) return;
382 DString cfname = md->getOutputFileBase();
383 DString argStr = md->argsString();
384 DString level1 = context->name();
385 DString level2 = !word.empty() ? word : md->name();
386 DString anchor = !sectionAnchor.empty() ? sectionAnchor : md->anchor();
387 DString ref;
388
389 // <keyword name="foo" id="MyApplication::foo" ref="doc.html#foo"/>
390 ref = makeRef(cfname, anchor);
391 DString id = level1+"::"+level2;
392 writeIndent(p->index,3);
393 p->index << "<keyword name=\"" << convertToXML(level2 + argStr) << "\""
394 " id=\"" << convertToXML(id + "_" + anchor) << "\""
395 " ref=\"" << convertToXML(ref) << "\"/>\n";
396 }
397 else if (context) // container
398 {
399 // <keyword name="Foo" id="Foo" ref="doc.html#Foo"/>
400 DString contRef = context->getOutputFileBase();
401 DString level1 = !word.empty() ? word : context->name();
402 DString ref = makeRef(contRef,sectionAnchor);
403 writeIndent(p->index,3);
404 p->index << "<keyword name=\"" << convertToXML(level1) << "\""
405 << " id=\"" << convertToXML(level1 +"_" + sectionAnchor) << "\""
406 << " ref=\"" << convertToXML(ref) << "\"/>\n";
407 }
408}
409
410void Qhp::addFile(const DString & fileName)
411{
412 p->files.insert(("<file>" + convertToXML(fileName) + "</file>").str());
413}
414
415void Qhp::addIndexFile(const DString & fileName)
416{
417 addFile(fileName);
418}
419
420void Qhp::addImageFile(const DString &fileName)
421{
422 addFile(fileName);
423}
424
425void Qhp::addStyleSheetFile(const DString &fileName)
426{
427 addFile(fileName);
428}
429
431{
432 DString qchFile = Config_getString(QCH_FILE);
433 if (!qchFile.empty())
434 {
435 return qchFile;
436 }
437
438 DString projectName = Config_getString(PROJECT_NAME);
439 DString versionText = Config_getString(PROJECT_NUMBER);
440
441 return DString("../qch/")
442 + (projectName.empty() ? DString("index") : projectName)
443 + (versionText.empty() ? DString("") : DString("-") + versionText)
444 + DString(".qch");
445}
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:686
@ Qhp
Definition debug.h:45
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual const DString & name() const =0
virtual bool hasDocumentation() const =0
virtual DString anchor() const =0
virtual DString getOutputFileBase() const =0
Class representing a directory in the file system.
Definition dir.h:73
static DString htmlFileExtension
Definition doxygen.h:115
A model of a class/file/namespace member symbol.
Definition memberdef.h:45
virtual DString argsString() const =0
QhpSectionTree sectionTree
Definition qhp.cpp:162
TextStream doc
Definition qhp.cpp:159
TextStream index
Definition qhp.cpp:160
StringSet files
Definition qhp.cpp:161
std::ofstream docFile
Definition qhp.cpp:158
Qhp()
Definition qhp.cpp:200
void addIndexFile(const DString &name)
Definition qhp.cpp:415
void incContentsDepth()
Definition qhp.cpp:308
void decContentsDepth()
Definition qhp.cpp:313
void initialize()
Definition qhp.cpp:203
void finalize()
Definition qhp.cpp:276
std::unique_ptr< Private > p
Definition qhp.h:55
~Qhp()
static DString getQchFileName()
Definition qhp.cpp:430
void addContentsItem(bool isDir, const DString &name, const DString &ref, const DString &file, const DString &anchor, bool separateIndex, bool addToNavIndex, const Definition *def, const DString &)
Definition qhp.cpp:318
void addIndexItem(const Definition *context, const MemberDef *md, const DString &sectionAnchor, const DString &title)
Definition qhp.cpp:371
void addStyleSheetFile(const DString &name)
Definition qhp.cpp:425
void addFile(const DString &)
Definition qhp.cpp:410
static const DString qhpFileName
Definition qhp.h:49
void addImageFile(const DString &name)
Definition qhp.cpp:420
Node m_root
Definition qhp.cpp:69
void writeToc(TextStream &t) const
Definition qhp.cpp:145
void incLevel()
Definition qhp.cpp:131
void traverse(const Node &root, TextStream &t, int indent) const
Definition qhp.cpp:72
Node * m_current
Definition qhp.cpp:70
void decLevel()
Definition qhp.cpp:137
void addSection(const DString &title, const DString &ref)
Definition qhp.cpp:127
Text streaming class that buffers data.
Definition textstream.h:36
void setStream(std::ostream *s)
Sets or changes the std::ostream to write to.
Definition textstream.h:67
void flush()
Flushes the buffer.
Definition textstream.h:212
virtual DString trISOLang()=0
#define Config_getString(name)
Definition config.h:32
std::set< std::string > StringSet
Definition containers.h:31
std::vector< std::string > StringVector
Definition containers.h:33
Translator * theTranslator
Definition language.cpp:76
#define ASSERT(x)
Definition message.h:142
#define term(fmt,...)
Definition message.h:137
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:681
Definition dstring.h:913
Portable versions of functions that are platform dependent.
static DString makeFileName(const DString &withoutExtension)
Definition qhp.cpp:174
static DString getFullProjectName()
Definition qhp.cpp:165
static DString makeRef(const DString &withoutExtension, const DString &anchor)
Definition qhp.cpp:191
static std::once_flag g_blankWritten
Definition qhp.cpp:38
static void writeIndent(TextStream &t, int indent)
Definition qhp.cpp:41
Some helper functions for std::string.
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition stringutil.h:117
Node(Node *parent_, const DString &title_, const DString &ref_)
Definition qhp.cpp:60
std::vector< std::unique_ptr< Node > > children
Definition qhp.cpp:66
Node(Node *parent_)
Definition qhp.cpp:58
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3931
DString convertToXML(const DString &s, bool keepEntities, const bool citeEntry)
Definition util.cpp:3232
A bunch of utility functions.