b04da62970e8280399c26ae009698990d9c5e1a9
[platform/upstream/doxygen.git] / src / ftvhelp.cpp
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 <qlist.h>
24 #include <qdict.h>
25 #include <qfileinfo.h>
26
27 #include "ftvhelp.h"
28 #include "config.h"
29 #include "message.h"
30 #include "doxygen.h"
31 #include "language.h"
32 #include "htmlgen.h"
33 #include "layout.h"
34 #include "pagedef.h"
35 #include "docparser.h"
36 #include "htmldocvisitor.h"
37 #include "filedef.h"
38 #include "util.h"
39 #include "resourcemgr.h"
40
41 #define MAX_INDENT 1024
42
43 static int folderId=1;
44
45 struct FTVNode
46 {
47   FTVNode(bool dir,const char *r,const char *f,const char *a,
48           const char *n,bool sepIndex,bool navIndex,Definition *df)
49     : isLast(TRUE), isDir(dir),ref(r),file(f),anchor(a),name(n), index(0),
50       parent(0), separateIndex(sepIndex), addToNavIndex(navIndex),
51       def(df) { children.setAutoDelete(TRUE); }
52   int computeTreeDepth(int level) const;
53   int numNodesAtLevel(int level,int maxLevel) const;
54   bool isLast;
55   bool isDir;
56   QCString ref;
57   QCString file;
58   QCString anchor;
59   QCString name;
60   int index;
61   QList<FTVNode> children;
62   FTVNode *parent;
63   bool separateIndex;
64   bool addToNavIndex;
65   Definition *def;
66 };
67
68 int FTVNode::computeTreeDepth(int level) const
69 {
70   int maxDepth=level;
71   QListIterator<FTVNode> li(children);
72   FTVNode *n;
73   for (;(n=li.current());++li)
74   {
75     if (n->children.count()>0)
76     {
77       int d = n->computeTreeDepth(level+1);
78       if (d>maxDepth) maxDepth=d;
79     }
80   }
81   return maxDepth;
82 }
83
84 int FTVNode::numNodesAtLevel(int level,int maxLevel) const
85 {
86   int num=0;
87   if (level<maxLevel)
88   {
89     num++; // this node
90     QListIterator<FTVNode> li(children);
91     FTVNode *n;
92     for (;(n=li.current());++li)
93     {
94       num+=n->numNodesAtLevel(level+1,maxLevel);
95     }
96   }
97   return num;
98 }
99
100 //----------------------------------------------------------------------------
101
102 /*! Constructs an ftv help object.
103  *  The object has to be \link initialize() initialized\endlink before it can
104  *  be used.
105  */
106 FTVHelp::FTVHelp(bool TLI)
107 {
108   /* initial depth */
109   m_indentNodes = new QList<FTVNode>[MAX_INDENT];
110   m_indentNodes[0].setAutoDelete(TRUE);
111   m_indent=0;
112   m_topLevelIndex = TLI;
113 }
114
115 /*! Destroys the ftv help object. */
116 FTVHelp::~FTVHelp()
117 {
118   delete[] m_indentNodes;
119 }
120
121 /*! This will create a folder tree view table of contents file (tree.js).
122  *  \sa finalize()
123  */
124 void FTVHelp::initialize()
125 {
126 }
127
128 /*! Finalizes the FTV help. This will finish and close the
129  *  contents file (index.js).
130  *  \sa initialize()
131  */
132 void FTVHelp::finalize()
133 {
134   generateTreeView();
135 }
136
137 /*! Increase the level of the contents hierarchy.
138  *  This will start a new sublist in contents file.
139  *  \sa decContentsDepth()
140  */
141 void FTVHelp::incContentsDepth()
142 {
143   //printf("incContentsDepth() indent=%d\n",m_indent);
144   m_indent++;
145   ASSERT(m_indent<MAX_INDENT);
146 }
147
148 /*! Decrease the level of the contents hierarchy.
149  *  This will end the current sublist.
150  *  \sa incContentsDepth()
151  */
152 void FTVHelp::decContentsDepth()
153 {
154   //printf("decContentsDepth() indent=%d\n",m_indent);
155   ASSERT(m_indent>0);
156   if (m_indent>0)
157   {
158     m_indent--;
159     QList<FTVNode> *nl = &m_indentNodes[m_indent];
160     FTVNode *parent = nl->getLast();
161     if (parent)
162     {
163       QList<FTVNode> *children = &m_indentNodes[m_indent+1];
164       while (!children->isEmpty())
165       {
166         parent->children.append(children->take(0));
167       }
168     }
169   }
170 }
171
172 /*! Add a list item to the contents file.
173  *  \param isDir TRUE if the item is a directory, FALSE if it is a text
174  *  \param name The name of the item.
175  *  \param ref  the URL of to the item.
176  *  \param file the file containing the definition of the item
177  *  \param anchor the anchor within the file.
178  *  \param name the name of the item.
179  *  \param separateIndex put the entries in a separate index file
180  *  \param addToNavIndex add this entry to the quick navigation index
181  *  \param def Definition corresponding to this entry
182  */
183 void FTVHelp::addContentsItem(bool isDir,
184                               const char *name,
185                               const char *ref,
186                               const char *file,
187                               const char *anchor,
188                               bool separateIndex,
189                               bool addToNavIndex,
190                               Definition *def
191                               )
192 {
193   //printf("%p: m_indent=%d addContentsItem(%s,%s,%s,%s)\n",this,m_indent,name,ref,file,anchor);
194   QList<FTVNode> *nl = &m_indentNodes[m_indent];
195   FTVNode *newNode = new FTVNode(isDir,ref,file,anchor,name,separateIndex,addToNavIndex,def);
196   if (!nl->isEmpty())
197   {
198     nl->getLast()->isLast=FALSE;
199   }
200   nl->append(newNode);
201   newNode->index = nl->count()-1;
202   if (m_indent>0)
203   {
204     QList<FTVNode> *pnl = &m_indentNodes[m_indent-1];
205     newNode->parent = pnl->getLast();
206   }
207
208 }
209
210 static QCString node2URL(FTVNode *n,bool overruleFile=FALSE,bool srcLink=FALSE)
211 {
212   QCString url = n->file;
213   if (!url.isEmpty() && url.at(0)=='!')  // relative URL
214   {
215     // remove leading !
216     url = url.mid(1);
217   }
218   else if (!url.isEmpty() && url.at(0)=='^') // absolute URL
219   {
220     // skip, keep ^ in the output
221   }
222   else // local file (with optional anchor)
223   {
224     if (overruleFile && n->def && n->def->definitionType()==Definition::TypeFile)
225     {
226       FileDef *fd = (FileDef*)n->def;
227       if (srcLink)
228       {
229         url = fd->getSourceFileBase();
230       }
231       else
232       {
233         url = fd->getOutputFileBase();
234       }
235     }
236     url+=Doxygen::htmlFileExtension;
237     if (!n->anchor.isEmpty()) url+="#"+n->anchor;
238   }
239   return url;
240 }
241
242 QCString FTVHelp::generateIndentLabel(FTVNode *n,int level)
243 {
244   QCString result;
245   if (n->parent)
246   {
247     result=generateIndentLabel(n->parent,level+1);
248   }
249   result+=QCString().setNum(n->index)+"_";
250   return result;
251 }
252
253 void FTVHelp::generateIndent(FTextStream &t, FTVNode *n,bool opened)
254 {
255   int indent=0;
256   FTVNode *p = n->parent;
257   while (p) { indent++; p=p->parent; }
258   if (n->isDir)
259   {
260     QCString dir = opened ? "&#9660;" : "&#9654;";
261     t << "<span style=\"width:" << (indent*16) << "px;display:inline-block;\">&#160;</span>"
262       << "<span id=\"arr_" << generateIndentLabel(n,0) << "\" class=\"arrow\" ";
263     t << "onclick=\"toggleFolder('" << generateIndentLabel(n,0) << "')\"";
264     t << ">" << dir
265       << "</span>";
266   }
267   else
268   {
269     t << "<span style=\"width:" << ((indent+1)*16) << "px;display:inline-block;\">&#160;</span>";
270   }
271 }
272
273 void FTVHelp::generateLink(FTextStream &t,FTVNode *n)
274 {
275   //printf("FTVHelp::generateLink(ref=%s,file=%s,anchor=%s\n",
276   //    n->ref.data(),n->file.data(),n->anchor.data());
277   bool setTarget = FALSE;
278   if (n->file.isEmpty()) // no link
279   {
280     t << "<b>" << convertToHtml(n->name) << "</b>";
281   }
282   else // link into other frame
283   {
284     if (!n->ref.isEmpty()) // link to entity imported via tag file
285     {
286       t << "<a class=\"elRef\" ";
287       QCString result = externalLinkTarget();
288       if (result != "") setTarget = TRUE;
289       t << result << externalRef("",n->ref,FALSE);
290     }
291     else // local link
292     {
293       t << "<a class=\"el\" ";
294     }
295     t << "href=\"";
296     t << externalRef("",n->ref,TRUE);
297     t << node2URL(n);
298     if (!setTarget)
299     {
300       if (m_topLevelIndex)
301         t << "\" target=\"basefrm\">";
302       else
303         t << "\" target=\"_self\">";
304     }
305     t << convertToHtml(n->name);
306     t << "</a>";
307     if (!n->ref.isEmpty())
308     {
309       t << "&#160;[external]";
310     }
311   }
312 }
313
314 static void generateBriefDoc(FTextStream &t,Definition *def)
315 {
316   QCString brief = def->briefDescription(TRUE);
317   //printf("*** %p: generateBriefDoc(%s)='%s'\n",def,def->name().data(),brief.data());
318   if (!brief.isEmpty())
319   {
320     DocNode *root = validatingParseDoc(def->briefFile(),def->briefLine(),
321         def,0,brief,FALSE,FALSE,0,TRUE,TRUE);
322     QCString relPath = relativePathToRoot(def->getOutputFileBase());
323     HtmlCodeGenerator htmlGen(t,relPath);
324     HtmlDocVisitor *visitor = new HtmlDocVisitor(t,htmlGen,def);
325     root->accept(visitor);
326     delete visitor;
327     delete root;
328   }
329 }
330
331 void FTVHelp::generateTree(FTextStream &t, const QList<FTVNode> &nl,int level,int maxLevel,int &index)
332 {
333   QListIterator<FTVNode> nli(nl);
334   FTVNode *n;
335   for (nli.toFirst();(n=nli.current());++nli)
336   {
337     t << "<tr id=\"row_" << generateIndentLabel(n,0) << "\"";
338     if ((index&1)==0) // even row
339       t << " class=\"even\"";
340     if (level>=maxLevel) // item invisible by default
341       t << " style=\"display:none;\"";
342     else // item visible by default
343       index++;
344     t << "><td class=\"entry\">";
345     bool nodeOpened = level+1<maxLevel;
346     generateIndent(t,n,nodeOpened);
347     if (n->isDir)
348     {
349       if (n->def && n->def->definitionType()==Definition::TypeGroup)
350       {
351         // no icon
352       }
353       else if (n->def && n->def->definitionType()==Definition::TypePage)
354       {
355         // no icon
356       }
357       else if (n->def && n->def->definitionType()==Definition::TypeNamespace)
358       {
359         t << "<span class=\"icona\"><span class=\"icon\">N</span></span>";
360       }
361       else if (n->def && n->def->definitionType()==Definition::TypeClass)
362       {
363         t << "<span class=\"icona\"><span class=\"icon\">C</span></span>";
364       }
365       else
366       {
367         t << "<span id=\"img_" << generateIndentLabel(n,0)
368           << "\" class=\"iconf"
369           << (nodeOpened?"open":"closed")
370           << "\" onclick=\"toggleFolder('" << generateIndentLabel(n,0)
371           << "')\">&#160;</span>";
372       }
373       generateLink(t,n);
374       t << "</td><td class=\"desc\">";
375       if (n->def)
376       {
377         generateBriefDoc(t,n->def);
378       }
379       t << "</td></tr>" << endl;
380       folderId++;
381       generateTree(t,n->children,level+1,maxLevel,index);
382     }
383     else // leaf node
384     {
385       FileDef *srcRef=0;
386       if (n->def && n->def->definitionType()==Definition::TypeFile &&
387           ((FileDef*)n->def)->generateSourceFile())
388       {
389         srcRef = (FileDef*)n->def;
390       }
391       if (srcRef)
392       {
393         t << "<a href=\"" << srcRef->getSourceFileBase()
394           << Doxygen::htmlFileExtension
395           << "\">";
396       }
397       if (n->def && n->def->definitionType()==Definition::TypeGroup)
398       {
399         // no icon
400       }
401       else if (n->def && n->def->definitionType()==Definition::TypePage)
402       {
403         // no icon
404       }
405       else if (n->def && n->def->definitionType()==Definition::TypeNamespace)
406       {
407         t << "<span class=\"icona\"><span class=\"icon\">N</span></span>";
408       }
409       else if (n->def && n->def->definitionType()==Definition::TypeClass)
410       {
411         t << "<span class=\"icona\"><span class=\"icon\">C</span></span>";
412       }
413       else
414       {
415         t << "<span class=\"icondoc\"></span>";
416       }
417       if (srcRef)
418       {
419         t << "</a>";
420       }
421       generateLink(t,n);
422       t << "</td><td class=\"desc\">";
423       if (n->def)
424       {
425         generateBriefDoc(t,n->def);
426       }
427       t << "</td></tr>" << endl;
428     }
429   }
430 }
431
432 //-----------------------------------------------------------
433
434 struct NavIndexEntry
435 {
436   NavIndexEntry(const QCString &u,const QCString &p) : url(u), path(p) {}
437   QCString url;
438   QCString path;
439 };
440
441 class NavIndexEntryList : public QList<NavIndexEntry>
442 {
443   public:
444     NavIndexEntryList() : QList<NavIndexEntry>() { setAutoDelete(TRUE); }
445    ~NavIndexEntryList() {}
446   private:
447     int compareValues(const NavIndexEntry *item1,const NavIndexEntry *item2) const
448     {
449       // sort list based on url
450       return qstrcmp(item1->url,item2->url);
451     }
452 };
453
454 static QCString pathToNode(FTVNode *leaf,FTVNode *n)
455 {
456   QCString result;
457   if (n->parent)
458   {
459     result+=pathToNode(leaf,n->parent);
460   }
461   result+=QCString().setNum(n->index);
462   if (leaf!=n) result+=",";
463   return result;
464 }
465
466 static bool dupOfParent(const FTVNode *n)
467 {
468   if (n->parent==0) return FALSE;
469   if (n->file==n->parent->file) return TRUE;
470   return FALSE;
471 }
472
473 static void generateJSLink(FTextStream &t,FTVNode *n)
474 {
475   if (n->file.isEmpty()) // no link
476   {
477     t << "\"" << convertToJSString(n->name) << "\", null, ";
478   }
479   else // link into other page
480   {
481     t << "\"" << convertToJSString(n->name) << "\", \"";
482     t << externalRef("",n->ref,TRUE);
483     t << node2URL(n);
484     t << "\", ";
485   }
486 }
487
488 static QCString convertFileId2Var(const QCString &fileId)
489 {
490   QCString varId = fileId;
491   int i=varId.findRev('/');
492   if (i>=0) varId = varId.mid(i+1);
493   return substitute(varId,"-","_");
494 }
495
496 static bool generateJSTree(NavIndexEntryList &navIndex,FTextStream &t,
497                            const QList<FTVNode> &nl,int level,bool &first)
498 {
499   static QCString htmlOutput = Config_getString(HTML_OUTPUT);
500   QCString indentStr;
501   indentStr.fill(' ',level*2);
502   bool found=FALSE;
503   QListIterator<FTVNode> nli(nl);
504   FTVNode *n;
505   for (nli.toFirst();(n=nli.current());++nli)
506   {
507     // terminate previous entry
508     if (!first) t << "," << endl;
509     first=FALSE;
510
511     // start entry
512     if (!found)
513     {
514       t << "[" << endl;
515     }
516     found=TRUE;
517
518     if (n->addToNavIndex) // add entry to the navigation index
519     {
520       if (n->def && n->def->definitionType()==Definition::TypeFile)
521       {
522         FileDef *fd = (FileDef*)n->def;
523         bool doc,src;
524         doc = fileVisibleInIndex(fd,src);
525         if (doc)
526         {
527           navIndex.append(new NavIndexEntry(node2URL(n,TRUE,FALSE),pathToNode(n,n)));
528         }
529         if (src)
530         {
531           navIndex.append(new NavIndexEntry(node2URL(n,TRUE,TRUE),pathToNode(n,n)));
532         }
533       }
534       else
535       {
536         navIndex.append(new NavIndexEntry(node2URL(n),pathToNode(n,n)));
537       }
538     }
539
540     if (n->separateIndex) // store items in a separate file for dynamic loading
541     {
542       bool firstChild=TRUE;
543       t << indentStr << "  [ ";
544       generateJSLink(t,n);
545       if (n->children.count()>0) // write children to separate file for dynamic loading
546       {
547         QCString fileId = n->file;
548         if (n->anchor)
549         {
550           fileId+="_"+n->anchor;
551         }
552         if (dupOfParent(n))
553         {
554           fileId+="_dup";
555         }
556         QFile f(htmlOutput+"/"+fileId+".js");
557         if (f.open(IO_WriteOnly))
558         {
559           FTextStream tt(&f);
560           tt << "var " << convertFileId2Var(fileId) << " =" << endl;
561           generateJSTree(navIndex,tt,n->children,1,firstChild);
562           tt << endl << "];";
563         }
564         t << "\"" << fileId << "\" ]";
565       }
566       else // no children
567       {
568         t << "null ]";
569       }
570     }
571     else // show items in this file
572     {
573       bool firstChild=TRUE;
574       t << indentStr << "  [ ";
575       generateJSLink(t,n);
576       bool emptySection = !generateJSTree(navIndex,t,n->children,level+1,firstChild);
577       if (emptySection)
578         t << "null ]";
579       else
580         t << endl << indentStr << "  ] ]";
581     }
582   }
583   return found;
584 }
585
586 static void generateJSNavTree(const QList<FTVNode> &nodeList)
587 {
588   QCString htmlOutput = Config_getString(HTML_OUTPUT);
589   QFile f(htmlOutput+"/navtreedata.js");
590   NavIndexEntryList navIndex;
591   if (f.open(IO_WriteOnly) /*&& fidx.open(IO_WriteOnly)*/)
592   {
593     //FTextStream tidx(&fidx);
594     //tidx << "var NAVTREEINDEX =" << endl;
595     //tidx << "{" << endl;
596     FTextStream t(&f);
597                 t << "/*\n@ @licstart  The following is the entire license notice for the\n"
598                         "JavaScript code in this file.\n\nCopyright (C) 1997-2017 by Dimitri van Heesch\n\n"
599                         "This program is free software; you can redistribute it and/or modify\n"
600                         "it under the terms of the GNU General Public License as published by\n"
601                         "the Free Software Foundation; either version 2 of the License, or\n"
602                         "(at your option) any later version.\n\n"
603                         "This program is distributed in the hope that it will be useful,\n"
604                         "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
605                         " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
606                         " GNU General Public License for more details.\n\n"
607                         "You should have received a copy of the GNU General Public License along\n"
608                         "with this program; if not, write to the Free Software Foundation, Inc.,\n"
609                         "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n"
610                         "@licend  The above is the entire license notice\n"
611                         "for the JavaScript code in this file\n"
612                         "*/\n";
613     t << "var NAVTREE =" << endl;
614     t << "[" << endl;
615     t << "  [ ";
616     QCString &projName = Config_getString(PROJECT_NAME);
617     if (projName.isEmpty())
618     {
619       if (Doxygen::mainPage && !Doxygen::mainPage->title().isEmpty()) // Use title of main page as root
620       {
621         t << "\"" << convertToJSString(Doxygen::mainPage->title()) << "\", ";
622       }
623       else // Use default section title as root
624       {
625         LayoutNavEntry *lne = LayoutDocManager::instance().rootNavEntry()->find(LayoutNavEntry::MainPage);
626         t << "\"" << convertToJSString(lne->title()) << "\", ";
627       }
628     }
629     else // use PROJECT_NAME as root tree element
630     {
631       t << "\"" << convertToJSString(projName) << "\", ";
632     }
633     t << "\"index" << Doxygen::htmlFileExtension << "\", ";
634
635     // add special entry for index page
636     navIndex.append(new NavIndexEntry("index"+Doxygen::htmlFileExtension,""));
637     // related page index is written as a child of index.html, so add this as well
638     navIndex.append(new NavIndexEntry("pages"+Doxygen::htmlFileExtension,""));
639
640     bool first=TRUE;
641     generateJSTree(navIndex,t,nodeList,1,first);
642
643     if (first)
644       t << "]" << endl;
645     else
646       t << endl << "  ] ]" << endl;
647     t << "];" << endl << endl;
648
649     // write the navigation index (and sub-indices)
650     navIndex.sort();
651     int subIndex=0;
652     int elemCount=0;
653     const int maxElemCount=250;
654     //QFile fidx(htmlOutput+"/navtreeindex.js");
655     QFile fsidx(htmlOutput+"/navtreeindex0.js");
656     if (/*fidx.open(IO_WriteOnly) &&*/ fsidx.open(IO_WriteOnly))
657     {
658       //FTextStream tidx(&fidx);
659       FTextStream tsidx(&fsidx);
660       t << "var NAVTREEINDEX =" << endl;
661       t << "[" << endl;
662       tsidx << "var NAVTREEINDEX" << subIndex << " =" << endl;
663       tsidx << "{" << endl;
664       QListIterator<NavIndexEntry> li(navIndex);
665       NavIndexEntry *e;
666       bool first=TRUE;
667       for (li.toFirst();(e=li.current());) // for each entry
668       {
669         if (elemCount==0)
670         {
671           if (!first)
672           {
673             t << "," << endl;
674           }
675           else
676           {
677             first=FALSE;
678           }
679           t << "\"" << e->url << "\"";
680         }
681         tsidx << "\"" << e->url << "\":[" << e->path << "]";
682         ++li;
683         if (li.current() && elemCount<maxElemCount-1) tsidx << ","; // not last entry
684         tsidx << endl;
685
686         elemCount++;
687         if (li.current() && elemCount>=maxElemCount) // switch to new sub-index
688         {
689           tsidx << "};" << endl;
690           elemCount=0;
691           fsidx.close();
692           subIndex++;
693           fsidx.setName(htmlOutput+"/navtreeindex"+QCString().setNum(subIndex)+".js");
694           if (!fsidx.open(IO_WriteOnly)) break;
695           tsidx.setDevice(&fsidx);
696           tsidx << "var NAVTREEINDEX" << subIndex << " =" << endl;
697           tsidx << "{" << endl;
698         }
699       }
700       tsidx << "};" << endl;
701       t << endl << "];" << endl;
702     }
703     t << endl << "var SYNCONMSG = '"  << theTranslator->trPanelSynchronisationTooltip(FALSE) << "';";
704     t << endl << "var SYNCOFFMSG = '" << theTranslator->trPanelSynchronisationTooltip(TRUE)  << "';";
705   }
706   ResourceMgr::instance().copyResource("navtree.js",htmlOutput);
707 }
708
709 //-----------------------------------------------------------
710
711 // new style images
712 void FTVHelp::generateTreeViewImages()
713 {
714   QCString dname=Config_getString(HTML_OUTPUT);
715   const ResourceMgr &rm = ResourceMgr::instance();
716   rm.copyResource("doc.luma",dname);
717   rm.copyResource("folderopen.luma",dname);
718   rm.copyResource("folderclosed.luma",dname);
719   rm.copyResource("splitbar.lum",dname);
720 }
721
722 // new style scripts
723 void FTVHelp::generateTreeViewScripts()
724 {
725   QCString htmlOutput = Config_getString(HTML_OUTPUT);
726
727   // generate navtree.js & navtreeindex.js
728   generateJSNavTree(m_indentNodes[0]);
729
730   // copy resize.js & navtree.css
731   ResourceMgr::instance().copyResource("resize.js",htmlOutput);
732   ResourceMgr::instance().copyResource("navtree.css",htmlOutput);
733 }
734
735 // write tree inside page
736 void FTVHelp::generateTreeViewInline(FTextStream &t)
737 {
738   int preferredNumEntries = Config_getInt(HTML_INDEX_NUM_ENTRIES);
739   t << "<div class=\"directory\">\n";
740   QListIterator<FTVNode> li(m_indentNodes[0]);
741   FTVNode *n;
742   int d=1, depth=1;
743   for (;(n=li.current());++li)
744   {
745     if (n->children.count()>0)
746     {
747       d = n->computeTreeDepth(2);
748       if (d>depth) depth=d;
749     }
750   }
751   int preferredDepth = depth;
752   // write level selector
753   if (depth>1)
754   {
755     t << "<div class=\"levels\">[";
756     t << theTranslator->trDetailLevel();
757     t << " ";
758     int i;
759     for (i=1;i<=depth;i++)
760     {
761       t << "<span onclick=\"javascript:toggleLevel(" << i << ");\">" << i << "</span>";
762     }
763     t << "]</div>";
764
765     if (preferredNumEntries>0)
766     {
767       preferredDepth=1;
768       for (int i=1;i<=depth;i++)
769       {
770         int num=0;
771         QListIterator<FTVNode> li(m_indentNodes[0]);
772         FTVNode *n;
773         for (;(n=li.current());++li)
774         {
775           num+=n->numNodesAtLevel(0,i);
776         }
777         if (num<=preferredNumEntries)
778         {
779           preferredDepth=i;
780         }
781         else
782         {
783           break;
784         }
785       }
786     }
787   }
788   //printf("preferred depth=%d\n",preferredDepth);
789
790   t << "<table class=\"directory\">\n";
791   int index=0;
792   generateTree(t,m_indentNodes[0],0,preferredDepth,index);
793   t << "</table>\n";
794
795   t << "</div><!-- directory -->\n";
796 }
797
798 // write old style index.html and tree.html
799 void FTVHelp::generateTreeView()
800 {
801   generateTreeViewImages();
802   generateTreeViewScripts();
803 }