1 /******************************************************************************
2 * ftvhelp.cpp,v 1.0 2000/09/06 16:09:00
3 *
4 * Copyright (C) 1997-2015 by Dimitri van Heesch.
5 *
6 * Permission to use, copy, modify, and distribute this software and its
7 * documentation under the terms of the GNU General Public License is hereby
8 * granted. No representations are made about the suitability of this software
9 * for any purpose. It is provided "as is" without express or implied warranty.
10 * See the GNU General Public License for more details.
11 *
12 * Documents produced by Doxygen are derivative works derived from the
13 * input used in their production; they are not affected by this license.
14 *
15 * Original version contributed by Kenney Wong <kwong@ea.com>
16 * Modified by Dimitri van Heesch
17 *
18 * Folder Tree View for offline help on browsers that do not support HTML Help.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <algorithm>
24
25 #include "ftvhelp.h"
26 #include "config.h"
27 #include "message.h"
28 #include "doxygen.h"
29 #include "language.h"
30 #include "htmlgen.h"
31 #include "layout.h"
32 #include "pagedef.h"
33 #include "docparser.h"
34 #include "htmldocvisitor.h"
35 #include "filedef.h"
36 #include "classdef.h"
37 #include "util.h"
38 #include "resourcemgr.h"
39
40 static int folderId=1;
41
42 const char *JAVASCRIPT_LICENSE_TEXT = R"LIC(/*
43 @licstart The following is the entire license notice for the JavaScript code in this file.
44
45 The MIT License (MIT)
46
47 Copyright (C) 1997-2020 by Dimitri van Heesch
48
49 Permission is hereby granted, free of charge, to any person obtaining a copy of this software
50 and associated documentation files (the "Software"), to deal in the Software without restriction,
51 including without limitation the rights to use, copy, modify, merge, publish, distribute,
52 sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
53 furnished to do so, subject to the following conditions:
54
55 The above copyright notice and this permission notice shall be included in all copies or
56 substantial portions of the Software.
57
58 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
59 BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
60 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
61 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
62 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63
64 @licend The above is the entire license notice for the JavaScript code in this file
65 */
66 )LIC";
67
68 struct FTVNode
69 {
FTVNodeFTVNode70 FTVNode(bool dir,const QCString &r,const QCString &f,const QCString &a,
71 const QCString &n,bool sepIndex,bool navIndex,const Definition *df)
72 : isLast(TRUE), isDir(dir),ref(r),file(f),anchor(a),name(n), index(0),
73 parent(0), separateIndex(sepIndex), addToNavIndex(navIndex),
74 def(df) {}
~FTVNodeFTVNode75 ~FTVNode() { for (const auto &child : children) delete child; }
76 int computeTreeDepth(int level) const;
77 int numNodesAtLevel(int level,int maxLevel) const;
78 bool isLast;
79 bool isDir;
80 QCString ref;
81 QCString file;
82 QCString anchor;
83 QCString name;
84 int index;
85 std::vector<FTVNode*> children;
86 FTVNode *parent;
87 bool separateIndex;
88 bool addToNavIndex;
89 const Definition *def;
90 };
91
computeTreeDepth(int level) const92 int FTVNode::computeTreeDepth(int level) const
93 {
94 int maxDepth=level;
95 for (const auto &n : children)
96 {
97 if (!n->children.empty())
98 {
99 int d = n->computeTreeDepth(level+1);
100 if (d>maxDepth) maxDepth=d;
101 }
102 }
103 return maxDepth;
104 }
105
numNodesAtLevel(int level,int maxLevel) const106 int FTVNode::numNodesAtLevel(int level,int maxLevel) const
107 {
108 int num=0;
109 if (level<maxLevel)
110 {
111 num++; // this node
112 for (const auto &n : children)
113 {
114 num+=n->numNodesAtLevel(level+1,maxLevel);
115 }
116 }
117 return num;
118 }
119
120 //----------------------------------------------------------------------------
121
122 /*! Constructs an ftv help object.
123 * The object has to be \link initialize() initialized\endlink before it can
124 * be used.
125 */
FTVHelp(bool TLI)126 FTVHelp::FTVHelp(bool TLI)
127 {
128 /* initial depth */
129 m_indentNodes.resize(1);
130 m_indent=0;
131 m_topLevelIndex = TLI;
132 }
133
134 /*! Destroys the ftv help object. */
~FTVHelp()135 FTVHelp::~FTVHelp()
136 {
137 for (auto &idx : m_indentNodes)
138 {
139 for (auto &n : idx)
140 {
141 delete n;
142 }
143 idx.clear();
144 }
145 m_indentNodes.clear();
146 }
147
148 /*! This will create a folder tree view table of contents file (tree.js).
149 * \sa finalize()
150 */
initialize()151 void FTVHelp::initialize()
152 {
153 }
154
155 /*! Finalizes the FTV help. This will finish and close the
156 * contents file (index.js).
157 * \sa initialize()
158 */
finalize()159 void FTVHelp::finalize()
160 {
161 generateTreeView();
162 }
163
164 /*! Increase the level of the contents hierarchy.
165 * This will start a new sublist in contents file.
166 * \sa decContentsDepth()
167 */
incContentsDepth()168 void FTVHelp::incContentsDepth()
169 {
170 //printf("%p: incContentsDepth() indent=%d\n",this,m_indent);
171 m_indent++;
172 m_indentNodes.resize(m_indent+1);
173 }
174
175 /*! Decrease the level of the contents hierarchy.
176 * This will end the current sublist.
177 * \sa incContentsDepth()
178 */
decContentsDepth()179 void FTVHelp::decContentsDepth()
180 {
181 //printf("%p: decContentsDepth() indent=%d\n",this,m_indent);
182 ASSERT(m_indent>0);
183 if (m_indent>0)
184 {
185 m_indent--;
186 std::vector<FTVNode*> &nl = m_indentNodes[m_indent];
187 if (!nl.empty())
188 {
189 FTVNode *parent = nl.back();
190 std::vector<FTVNode*> &children = m_indentNodes[m_indent+1];
191 for (const auto &child : children)
192 {
193 parent->children.push_back(child);
194 }
195 children.clear();
196 }
197 }
198 }
199
200 /*! Add a list item to the contents file.
201 * \param isDir TRUE if the item is a directory, FALSE if it is a text
202 * \param name the name of the item.
203 * \param ref the URL of to the item.
204 * \param file the file containing the definition of the item
205 * \param anchor the anchor within the file.
206 * \param separateIndex put the entries in a separate index file
207 * \param addToNavIndex add this entry to the quick navigation index
208 * \param def Definition corresponding to this entry
209 */
addContentsItem(bool isDir,const QCString & name,const QCString & ref,const QCString & file,const QCString & anchor,bool separateIndex,bool addToNavIndex,const Definition * def)210 void FTVHelp::addContentsItem(bool isDir,
211 const QCString &name,
212 const QCString &ref,
213 const QCString &file,
214 const QCString &anchor,
215 bool separateIndex,
216 bool addToNavIndex,
217 const Definition *def
218 )
219 {
220 //printf("%p: m_indent=%d addContentsItem(%s,%s,%s,%s)\n",this,m_indent,name,ref,file,anchor);
221 std::vector<FTVNode*> &nl = m_indentNodes[m_indent];
222 FTVNode *newNode = new FTVNode(isDir,ref,file,anchor,name,separateIndex,addToNavIndex,def);
223 if (!nl.empty())
224 {
225 nl.back()->isLast=FALSE;
226 }
227 nl.push_back(newNode);
228 newNode->index = static_cast<int>(nl.size()-1);
229 if (m_indent>0)
230 {
231 std::vector<FTVNode*> &pnl = m_indentNodes[m_indent-1];
232 if (!pnl.empty())
233 {
234 newNode->parent = pnl.back();
235 }
236 }
237 }
238
node2URL(const FTVNode * n,bool overruleFile=FALSE,bool srcLink=FALSE)239 static QCString node2URL(const FTVNode *n,bool overruleFile=FALSE,bool srcLink=FALSE)
240 {
241 QCString url = n->file;
242 if (!url.isEmpty() && url.at(0)=='!') // relative URL
243 {
244 // remove leading !
245 url = url.mid(1);
246 }
247 else if (!url.isEmpty() && url.at(0)=='^') // absolute URL
248 {
249 // skip, keep ^ in the output
250 }
251 else // local file (with optional anchor)
252 {
253 if (overruleFile && n->def && n->def->definitionType()==Definition::TypeFile)
254 {
255 const FileDef *fd = toFileDef(n->def);
256 if (srcLink)
257 {
258 url = fd->getSourceFileBase();
259 }
260 else
261 {
262 url = fd->getOutputFileBase();
263 }
264 }
265 url = addHtmlExtensionIfMissing(url);
266 if (!n->anchor.isEmpty()) url+="#"+n->anchor;
267 }
268 return url;
269 }
270
generateIndentLabel(FTVNode * n,int level)271 QCString FTVHelp::generateIndentLabel(FTVNode *n,int level)
272 {
273 QCString result;
274 if (n->parent)
275 {
276 result=generateIndentLabel(n->parent,level+1);
277 }
278 result+=QCString().setNum(n->index)+"_";
279 return result;
280 }
281
generateIndent(TextStream & t,FTVNode * n,bool opened)282 void FTVHelp::generateIndent(TextStream &t, FTVNode *n,bool opened)
283 {
284 int indent=0;
285 FTVNode *p = n->parent;
286 while (p) { indent++; p=p->parent; }
287 if (n->isDir)
288 {
289 QCString dir = opened ? "▼" : "►";
290 t << "<span style=\"width:" << (indent*16) << "px;display:inline-block;\"> </span>"
291 << "<span id=\"arr_" << generateIndentLabel(n,0) << "\" class=\"arrow\" ";
292 t << "onclick=\"toggleFolder('" << generateIndentLabel(n,0) << "')\"";
293 t << ">" << dir
294 << "</span>";
295 }
296 else
297 {
298 t << "<span style=\"width:" << ((indent+1)*16) << "px;display:inline-block;\"> </span>";
299 }
300 }
301
generateLink(TextStream & t,FTVNode * n)302 void FTVHelp::generateLink(TextStream &t,FTVNode *n)
303 {
304 //printf("FTVHelp::generateLink(ref=%s,file=%s,anchor=%s\n",
305 // qPrint(n->ref),qPrint(n->file),qPrint(n->anchor));
306 bool setTarget = FALSE;
307 if (n->file.isEmpty()) // no link
308 {
309 t << "<b>" << convertToHtml(n->name) << "</b>";
310 }
311 else // link into other frame
312 {
313 if (!n->ref.isEmpty()) // link to entity imported via tag file
314 {
315 t << "<a class=\"elRef\" ";
316 QCString result = externalLinkTarget();
317 if (result != "") setTarget = TRUE;
318 t << result;
319 }
320 else // local link
321 {
322 t << "<a class=\"el\" ";
323 }
324 t << "href=\"";
325 t << externalRef("",n->ref,TRUE);
326 t << node2URL(n);
327 if (!setTarget)
328 {
329 if (m_topLevelIndex)
330 t << "\" target=\"basefrm\">";
331 else
332 t << "\" target=\"_self\">";
333 }
334 else
335 {
336 t << "\">";
337 }
338 t << convertToHtml(n->name);
339 t << "</a>";
340 if (!n->ref.isEmpty())
341 {
342 t << " [external]";
343 }
344 }
345 }
346
generateBriefDoc(TextStream & t,const Definition * def)347 static void generateBriefDoc(TextStream &t,const Definition *def)
348 {
349 QCString brief = def->briefDescription(TRUE);
350 //printf("*** %p: generateBriefDoc(%s)='%s'\n",def,qPrint(def->name()),qPrint(brief));
351 if (!brief.isEmpty())
352 {
353 std::unique_ptr<IDocParser> parser { createDocParser() };
354 std::unique_ptr<DocRoot> root { validatingParseDoc(*parser.get(),
355 def->briefFile(),def->briefLine(),
356 def,0,brief,FALSE,FALSE,
357 QCString(),TRUE,TRUE,Config_getBool(MARKDOWN_SUPPORT)) };
358 QCString relPath = relativePathToRoot(def->getOutputFileBase());
359 HtmlCodeGenerator htmlGen(t,relPath);
360 auto visitor = std::make_unique<HtmlDocVisitor>(t,htmlGen,def);
361 root->accept(visitor.get());
362 }
363 }
364
compoundIcon(const ClassDef * cd)365 static char compoundIcon(const ClassDef *cd)
366 {
367 char icon='C';
368 if (cd->getLanguage() == SrcLangExt_Slice)
369 {
370 if (cd->compoundType()==ClassDef::Interface)
371 {
372 icon='I';
373 }
374 else if (cd->compoundType()==ClassDef::Struct)
375 {
376 icon='S';
377 }
378 else if (cd->compoundType()==ClassDef::Exception)
379 {
380 icon='E';
381 }
382 }
383 return icon;
384 }
385
generateTree(TextStream & t,const std::vector<FTVNode * > & nl,int level,int maxLevel,int & index)386 void FTVHelp::generateTree(TextStream &t, const std::vector<FTVNode*> &nl,int level,int maxLevel,int &index)
387 {
388 for (const auto &n : nl)
389 {
390 t << "<tr id=\"row_" << generateIndentLabel(n,0) << "\"";
391 if ((index&1)==0) // even row
392 t << " class=\"even\"";
393 if (level>=maxLevel) // item invisible by default
394 t << " style=\"display:none;\"";
395 else // item visible by default
396 index++;
397 t << "><td class=\"entry\">";
398 bool nodeOpened = level+1<maxLevel;
399 generateIndent(t,n,nodeOpened);
400 if (n->isDir)
401 {
402 if (n->def && n->def->definitionType()==Definition::TypeGroup)
403 {
404 // no icon
405 }
406 else if (n->def && n->def->definitionType()==Definition::TypePage)
407 {
408 // no icon
409 }
410 else if (n->def && n->def->definitionType()==Definition::TypeNamespace)
411 {
412 if (n->def->getLanguage() == SrcLangExt_Slice)
413 {
414 t << "<span class=\"icona\"><span class=\"icon\">M</span></span>";
415 }
416 else
417 {
418 t << "<span class=\"icona\"><span class=\"icon\">N</span></span>";
419 }
420 }
421 else if (n->def && n->def->definitionType()==Definition::TypeClass)
422 {
423 char icon=compoundIcon(toClassDef(n->def));
424 t << "<span class=\"icona\"><span class=\"icon\">" << icon << "</span></span>";
425 }
426 else
427 {
428 t << "<span id=\"img_" << generateIndentLabel(n,0)
429 << "\" class=\"iconf"
430 << (nodeOpened?"open":"closed")
431 << "\" onclick=\"toggleFolder('" << generateIndentLabel(n,0)
432 << "')\"> </span>";
433 }
434 generateLink(t,n);
435 t << "</td><td class=\"desc\">";
436 if (n->def)
437 {
438 generateBriefDoc(t,n->def);
439 }
440 t << "</td></tr>\n";
441 folderId++;
442 generateTree(t,n->children,level+1,maxLevel,index);
443 }
444 else // leaf node
445 {
446 const FileDef *srcRef=0;
447 if (n->def && n->def->definitionType()==Definition::TypeFile &&
448 (toFileDef(n->def))->generateSourceFile())
449 {
450 srcRef = toFileDef(n->def);
451 }
452 if (srcRef)
453 {
454 t << "<a href=\"" << addHtmlExtensionIfMissing(srcRef->getSourceFileBase())
455 << "\">";
456 }
457 if (n->def && n->def->definitionType()==Definition::TypeGroup)
458 {
459 // no icon
460 }
461 else if (n->def && n->def->definitionType()==Definition::TypePage)
462 {
463 // no icon
464 }
465 else if (n->def && n->def->definitionType()==Definition::TypeNamespace)
466 {
467 if (n->def->getLanguage() == SrcLangExt_Slice)
468 {
469 t << "<span class=\"icona\"><span class=\"icon\">M</span></span>";
470 }
471 else
472 {
473 t << "<span class=\"icona\"><span class=\"icon\">N</span></span>";
474 }
475 }
476 else if (n->def && n->def->definitionType()==Definition::TypeClass)
477 {
478 char icon=compoundIcon(toClassDef(n->def));
479 t << "<span class=\"icona\"><span class=\"icon\">" << icon << "</span></span>";
480 }
481 else if (n->def && n->def->definitionType()==Definition::TypeConcept)
482 {
483 t << "<span class=\"icona\"><span class=\"icon\">R</span></span>";
484 }
485 else if (n->def && n->def->definitionType()==Definition::TypeDir)
486 {
487 t << "<span class=\"iconfclosed\"></span>";
488 }
489 else
490 {
491 t << "<span class=\"icondoc\"></span>";
492 }
493 if (srcRef)
494 {
495 t << "</a>";
496 }
497 generateLink(t,n);
498 t << "</td><td class=\"desc\">";
499 if (n->def)
500 {
501 generateBriefDoc(t,n->def);
502 }
503 t << "</td></tr>\n";
504 }
505 }
506 }
507
508 //-----------------------------------------------------------
509
510 struct NavIndexEntry
511 {
NavIndexEntryNavIndexEntry512 NavIndexEntry(const QCString &u,const QCString &p) : url(u), path(p) {}
513 QCString url;
514 QCString path;
515 };
516
517 class NavIndexEntryList : public std::vector<NavIndexEntry>
518 {
519 };
520
pathToNode(const FTVNode * leaf,const FTVNode * n)521 static QCString pathToNode(const FTVNode *leaf,const FTVNode *n)
522 {
523 QCString result;
524 if (n->parent)
525 {
526 result+=pathToNode(leaf,n->parent);
527 }
528 result+=QCString().setNum(n->index);
529 if (leaf!=n) result+=",";
530 return result;
531 }
532
dupOfParent(const FTVNode * n)533 static bool dupOfParent(const FTVNode *n)
534 {
535 if (n->parent==0) return FALSE;
536 if (n->file==n->parent->file) return TRUE;
537 return FALSE;
538 }
539
generateJSLink(TextStream & t,const FTVNode * n)540 static void generateJSLink(TextStream &t,const FTVNode *n)
541 {
542 if (n->file.isEmpty()) // no link
543 {
544 t << "\"" << convertToJSString(n->name) << "\", null, ";
545 }
546 else // link into other page
547 {
548 t << "\"" << convertToJSString(n->name) << "\", \"";
549 t << externalRef("",n->ref,TRUE);
550 t << node2URL(n);
551 t << "\", ";
552 }
553 }
554
convertFileId2Var(const QCString & fileId)555 static QCString convertFileId2Var(const QCString &fileId)
556 {
557 QCString varId = fileId;
558 int i=varId.findRev('/');
559 if (i>=0) varId = varId.mid(i+1);
560 return substitute(varId,"-","_");
561 }
562
generateJSTree(NavIndexEntryList & navIndex,TextStream & t,const std::vector<FTVNode * > & nl,int level,bool & first)563 static bool generateJSTree(NavIndexEntryList &navIndex,TextStream &t,
564 const std::vector<FTVNode*> &nl,int level,bool &first)
565 {
566 static QCString htmlOutput = Config_getString(HTML_OUTPUT);
567 QCString indentStr;
568 indentStr.fill(' ',level*2);
569 bool found=FALSE;
570 for (const auto &n : nl)
571 {
572 // terminate previous entry
573 if (!first) t << ",\n";
574 first=FALSE;
575
576 // start entry
577 if (!found)
578 {
579 t << "[\n";
580 }
581 found=TRUE;
582
583 if (n->addToNavIndex) // add entry to the navigation index
584 {
585 if (n->def && n->def->definitionType()==Definition::TypeFile)
586 {
587 const FileDef *fd = toFileDef(n->def);
588 bool doc,src;
589 doc = fileVisibleInIndex(fd,src);
590 if (doc)
591 {
592 navIndex.emplace_back(node2URL(n,TRUE,FALSE),pathToNode(n,n));
593 }
594 if (src)
595 {
596 navIndex.emplace_back(node2URL(n,TRUE,TRUE),pathToNode(n,n));
597 }
598 }
599 else
600 {
601 navIndex.emplace_back(node2URL(n),pathToNode(n,n));
602 }
603 }
604
605 if (n->separateIndex) // store items in a separate file for dynamic loading
606 {
607 bool firstChild=TRUE;
608 t << indentStr << " [ ";
609 generateJSLink(t,n);
610 if (!n->children.empty()) // write children to separate file for dynamic loading
611 {
612 QCString fileId = n->file;
613 if (!n->anchor.isEmpty())
614 {
615 fileId+="_"+n->anchor;
616 }
617 if (dupOfParent(n))
618 {
619 fileId+="_dup";
620 }
621 QCString fileName = htmlOutput+"/"+fileId+".js";
622 std::ofstream f(fileName.str(),std::ofstream::out | std::ofstream::binary);
623 if (f.is_open())
624 {
625 TextStream tt(&f);
626 tt << "var " << convertFileId2Var(fileId) << " =\n";
627 generateJSTree(navIndex,tt,n->children,1,firstChild);
628 tt << "\n];";
629 }
630 f.close();
631 t << "\"" << fileId << "\" ]";
632 }
633 else // no children
634 {
635 t << "null ]";
636 }
637 }
638 else // show items in this file
639 {
640 bool firstChild=TRUE;
641 t << indentStr << " [ ";
642 generateJSLink(t,n);
643 bool emptySection = !generateJSTree(navIndex,t,n->children,level+1,firstChild);
644 if (emptySection)
645 t << "null ]";
646 else
647 t << "\n" << indentStr << " ] ]";
648 }
649 }
650 return found;
651 }
652
generateJSNavTree(const std::vector<FTVNode * > & nodeList)653 static void generateJSNavTree(const std::vector<FTVNode*> &nodeList)
654 {
655 QCString htmlOutput = Config_getString(HTML_OUTPUT);
656 std::ofstream f(htmlOutput.str()+"/navtreedata.js",std::ofstream::out | std::ofstream::binary);
657 NavIndexEntryList navIndex;
658 if (f.is_open())
659 {
660 TextStream t(&f);
661 //TextStream tidx(&fidx);
662 //tidx << "var NAVTREEINDEX =\n";
663 //tidx << "{\n";
664 t << JAVASCRIPT_LICENSE_TEXT;
665 t << "var NAVTREE =\n";
666 t << "[\n";
667 t << " [ ";
668 QCString projName = Config_getString(PROJECT_NAME);
669 if (projName.isEmpty())
670 {
671 if (mainPageHasTitle()) // Use title of main page as root
672 {
673 t << "\"" << convertToJSString(Doxygen::mainPage->title()) << "\", ";
674 }
675 else // Use default section title as root
676 {
677 LayoutNavEntry *lne = LayoutDocManager::instance().rootNavEntry()->find(LayoutNavEntry::MainPage);
678 t << "\"" << convertToJSString(lne->title()) << "\", ";
679 }
680 }
681 else // use PROJECT_NAME as root tree element
682 {
683 t << "\"" << convertToJSString(projName) << "\", ";
684 }
685 t << "\"index" << Doxygen::htmlFileExtension << "\", ";
686
687 // add special entry for index page
688 navIndex.emplace_back("index"+Doxygen::htmlFileExtension,"");
689 // related page index is written as a child of index.html, so add this as well
690 navIndex.emplace_back("pages"+Doxygen::htmlFileExtension,"");
691
692 bool first=TRUE;
693 generateJSTree(navIndex,t,nodeList,1,first);
694
695 if (first)
696 t << "]\n";
697 else
698 t << "\n ] ]\n";
699 t << "];\n\n";
700
701 // write the navigation index (and sub-indices)
702 std::sort(navIndex.begin(),navIndex.end(),[](const auto &n1,const auto &n2)
703 { return !n1.url.isEmpty() && (n2.url.isEmpty() || (n1.url<n2.url)); });
704
705 int subIndex=0;
706 int elemCount=0;
707 const int maxElemCount=250;
708 std::ofstream tsidx(htmlOutput.str()+"/navtreeindex0.js",std::ofstream::out | std::ofstream::binary);
709 if (tsidx.is_open())
710 {
711 t << "var NAVTREEINDEX =\n";
712 t << "[\n";
713 tsidx << "var NAVTREEINDEX" << subIndex << " =\n";
714 tsidx << "{\n";
715 first=TRUE;
716 auto it = navIndex.begin();
717 while (it!=navIndex.end())
718 {
719 const NavIndexEntry &e = *it;
720 if (elemCount==0)
721 {
722 if (!first)
723 {
724 t << ",\n";
725 }
726 else
727 {
728 first=FALSE;
729 }
730 t << "\"" << e.url << "\"";
731 }
732 tsidx << "\"" << e.url << "\":[" << e.path << "]";
733 ++it;
734 if (it!=navIndex.end() && elemCount<maxElemCount-1) tsidx << ","; // not last entry
735 tsidx << "\n";
736
737 elemCount++;
738 if (it!=navIndex.end() && elemCount>=maxElemCount) // switch to new sub-index
739 {
740 tsidx << "};\n";
741 elemCount=0;
742 tsidx.close();
743 subIndex++;
744 QCString fileName = htmlOutput+"/navtreeindex"+QCString().setNum(subIndex)+".js";
745 tsidx.open(fileName.str(),std::ofstream::out | std::ofstream::binary);
746 if (!tsidx.is_open()) break;
747 tsidx << "var NAVTREEINDEX" << subIndex << " =\n";
748 tsidx << "{\n";
749 }
750 }
751 tsidx << "};\n";
752 t << "\n];\n";
753 }
754 t << "\nvar SYNCONMSG = '" << theTranslator->trPanelSynchronisationTooltip(FALSE) << "';";
755 t << "\nvar SYNCOFFMSG = '" << theTranslator->trPanelSynchronisationTooltip(TRUE) << "';";
756 }
757 ResourceMgr::instance().copyResource("navtree.js",htmlOutput);
758 }
759
760 //-----------------------------------------------------------
761
762 // new style images
generateTreeViewImages()763 void FTVHelp::generateTreeViewImages()
764 {
765 QCString dname=Config_getString(HTML_OUTPUT);
766 const ResourceMgr &rm = ResourceMgr::instance();
767 rm.copyResource("doc.luma",dname);
768 rm.copyResource("folderopen.luma",dname);
769 rm.copyResource("folderclosed.luma",dname);
770 rm.copyResource("splitbar.lum",dname);
771 }
772
773 // new style scripts
generateTreeViewScripts()774 void FTVHelp::generateTreeViewScripts()
775 {
776 QCString htmlOutput = Config_getString(HTML_OUTPUT);
777
778 // generate navtree.js & navtreeindex.js
779 generateJSNavTree(m_indentNodes[0]);
780
781 // copy resize.js & navtree.css
782 ResourceMgr::instance().copyResource("resize.js",htmlOutput);
783 ResourceMgr::instance().copyResource("navtree.css",htmlOutput);
784 }
785
786 // write tree inside page
generateTreeViewInline(TextStream & t)787 void FTVHelp::generateTreeViewInline(TextStream &t)
788 {
789 int preferredNumEntries = Config_getInt(HTML_INDEX_NUM_ENTRIES);
790 t << "<div class=\"directory\">\n";
791 int d=1, depth=1;
792 for (const auto &n : m_indentNodes[0])
793 {
794 if (!n->children.empty())
795 {
796 d = n->computeTreeDepth(2);
797 if (d>depth) depth=d;
798 }
799 }
800 int preferredDepth = depth;
801 // write level selector
802 if (depth>1)
803 {
804 t << "<div class=\"levels\">[";
805 t << theTranslator->trDetailLevel();
806 t << " ";
807 for (int i=1;i<=depth;i++)
808 {
809 t << "<span onclick=\"javascript:toggleLevel(" << i << ");\">" << i << "</span>";
810 }
811 t << "]</div>";
812
813 if (preferredNumEntries>0)
814 {
815 preferredDepth=1;
816 for (int i=1;i<=depth;i++)
817 {
818 int num=0;
819 for (const auto &n : m_indentNodes[0])
820 {
821 num+=n->numNodesAtLevel(0,i);
822 }
823 if (num<=preferredNumEntries)
824 {
825 preferredDepth=i;
826 }
827 else
828 {
829 break;
830 }
831 }
832 }
833 }
834 //printf("preferred depth=%d\n",preferredDepth);
835
836 if (!m_indentNodes[0].empty())
837 {
838 t << "<table class=\"directory\">\n";
839 int index=0;
840 generateTree(t,m_indentNodes[0],0,preferredDepth,index);
841 t << "</table>\n";
842 }
843
844 t << "</div><!-- directory -->\n";
845 }
846
847 // write old style index.html and tree.html
generateTreeView()848 void FTVHelp::generateTreeView()
849 {
850 generateTreeViewImages();
851 generateTreeViewScripts();
852 }
853