1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2021 by Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15 
16 #include <stdlib.h>
17 #include <assert.h>
18 
19 #include <mutex>
20 #include <sstream>
21 
22 #include "message.h"
23 #include "htmlgen.h"
24 #include "config.h"
25 #include "util.h"
26 #include "doxygen.h"
27 #include "diagram.h"
28 #include "version.h"
29 #include "dot.h"
30 #include "dotcallgraph.h"
31 #include "dotclassgraph.h"
32 #include "dotdirdeps.h"
33 #include "dotgfxhierarchytable.h"
34 #include "dotgroupcollaboration.h"
35 #include "dotincldepgraph.h"
36 #include "language.h"
37 #include "htmlhelp.h"
38 #include "docparser.h"
39 #include "htmldocvisitor.h"
40 #include "searchindex.h"
41 #include "pagedef.h"
42 #include "debug.h"
43 #include "dirdef.h"
44 #include "vhdldocgen.h"
45 #include "layout.h"
46 #include "image.h"
47 #include "ftvhelp.h"
48 #include "bufstr.h"
49 #include "resourcemgr.h"
50 #include "tooltip.h"
51 #include "growbuf.h"
52 #include "fileinfo.h"
53 #include "dir.h"
54 #include "utf8.h"
55 #include "textstream.h"
56 
57 //#define DBG_HTML(x) x;
58 #define DBG_HTML(x)
59 
60 static QCString g_header;
61 static QCString g_footer;
62 static QCString g_mathjax_code;
63 static QCString g_latex_macro;
64 static const char *hex="0123456789ABCDEF";
65 
66 // note: this is only active if DISABLE_INDEX=YES, if DISABLE_INDEX is disabled, this
67 // part will be rendered inside menu.js
writeClientSearchBox(TextStream & t,const QCString & relPath)68 static void writeClientSearchBox(TextStream &t,const QCString &relPath)
69 {
70   t << "        <div id=\"MSearchBox\" class=\"MSearchBoxInactive\">\n";
71   t << "        <span class=\"left\">\n";
72   t << "          <img id=\"MSearchSelect\" src=\"" << relPath << "search/mag_sel.svg\"\n";
73   t << "               onmouseover=\"return searchBox.OnSearchSelectShow()\"\n";
74   t << "               onmouseout=\"return searchBox.OnSearchSelectHide()\"\n";
75   t << "               alt=\"\"/>\n";
76   t << "          <input type=\"text\" id=\"MSearchField\" value=\""
77     << theTranslator->trSearch() << "\" accesskey=\"S\"\n";
78   t << "               onfocus=\"searchBox.OnSearchFieldFocus(true)\" \n";
79   t << "               onblur=\"searchBox.OnSearchFieldFocus(false)\" \n";
80   t << "               onkeyup=\"searchBox.OnSearchFieldChange(event)\"/>\n";
81   t << "          </span><span class=\"right\">\n";
82   t << "            <a id=\"MSearchClose\" href=\"javascript:searchBox.CloseResultsWindow()\">"
83     << "<img id=\"MSearchCloseImg\" border=\"0\" src=\"" << relPath << "search/close.svg\" alt=\"\"/></a>\n";
84   t << "          </span>\n";
85   t << "        </div>\n";
86 }
87 
88 // note: this is only active if DISABLE_INDEX=YES. if DISABLE_INDEX is disabled, this
89 // part will be rendered inside menu.js
writeServerSearchBox(TextStream & t,const QCString & relPath,bool highlightSearch)90 static void writeServerSearchBox(TextStream &t,const QCString &relPath,bool highlightSearch)
91 {
92   bool externalSearch = Config_getBool(EXTERNAL_SEARCH);
93   t << "        <div id=\"MSearchBox\" class=\"MSearchBoxInactive\">\n";
94   t << "          <div class=\"left\">\n";
95   t << "            <form id=\"FSearchBox\" action=\"" << relPath;
96   if (externalSearch)
97   {
98     t << "search" << Doxygen::htmlFileExtension;
99   }
100   else
101   {
102     t << "search.php";
103   }
104   t << "\" method=\"get\">\n";
105   t << "              <img id=\"MSearchSelect\" src=\"" << relPath << "search/mag.svg\" alt=\"\"/>\n";
106   if (!highlightSearch)
107   {
108     t << "              <input type=\"text\" id=\"MSearchField\" name=\"query\" value=\""
109       << theTranslator->trSearch() << "\" size=\"20\" accesskey=\"S\" \n";
110     t << "                     onfocus=\"searchBox.OnSearchFieldFocus(true)\" \n";
111     t << "                     onblur=\"searchBox.OnSearchFieldFocus(false)\"/>\n";
112     t << "            </form>\n";
113     t << "          </div><div class=\"right\"></div>\n";
114     t << "        </div>\n";
115   }
116 }
117 
118 //------------------------------------------------------------------------
119 /// Convert a set of LaTeX  commands `\(re)newcommand`  to a form readable by MathJax
120 /// LaTeX syntax:
121 /// ```
122 ///       \newcommand{\cmd}{replacement}
123 ///         or
124 ///       \renewcommand{\cmd}{replacement}
125 /// ```
126 /// MathJax syntax:
127 /// ```
128 ///        cmd: "{replacement}"
129 /// ```
130 ///
131 /// LaTeX syntax:
132 /// ```
133 ///       \newcommand{\cmd}[nr]{replacement}
134 ///         or
135 ///       \renewcommand{\cmd}[nr]{replacement}
136 /// ```
137 /// MathJax syntax:
138 /// ```
139 ///        cmd: ["{replacement}",nr]
140 /// ```
getConvertLatexMacro()141 static QCString getConvertLatexMacro()
142 {
143   QCString macrofile = Config_getString(FORMULA_MACROFILE);
144   if (macrofile.isEmpty()) return "";
145   QCString s = fileToString(macrofile);
146   macrofile = FileInfo(macrofile.str()).absFilePath();
147   int size = s.length();
148   GrowBuf out(size);
149   const char *data = s.data();
150   int line = 1;
151   int cnt = 0;
152   int i = 0;
153   QCString nr;
154   while (i < size)
155   {
156     nr = "";
157     // skip initial white space, but count lines
158     while (i < size && (data[i] == ' ' || data[i] == '\t' || data[i] == '\n'))
159     {
160       if (data[i] == '\n') line++;
161       i++;
162     }
163     if (i >= size) break;
164     // check for \newcommand or \renewcommand
165     if (data[i] != '\\')
166     {
167       warn(macrofile,line, "file contains non valid code, expected '\\' got '%c'\n",data[i]);
168       return "";
169     }
170     i++;
171     if (!qstrncmp(data + i, "newcommand", (uint)strlen("newcommand")))
172     {
173       i += (int)strlen("newcommand");
174     }
175     else if (!qstrncmp(data + i, "renewcommand", (uint)strlen("renewcommand")))
176     {
177       i += (int)strlen("renewcommand");
178     }
179     else
180     {
181       warn(macrofile,line, "file contains non valid code, expected 'newcommand' or 'renewcommand'");
182       return "";
183     }
184     // handle {cmd}
185     if (data[i] != '{')
186     {
187       warn(macrofile,line, "file contains non valid code, expected '{' got '%c'\n",data[i]);
188       return "";
189     }
190     i++;
191     if (data[i] != '\\')
192     {
193       warn(macrofile,line, "file contains non valid code, expected '\\' got '%c'\n",data[i]);
194       return "";
195     }
196     i++;
197     // run till }, i.e. cmd
198     out.addStr("    ");
199     while (i < size && (data[i] != '}')) out.addChar(data[i++]);
200     if (i >= size)
201     {
202       warn(macrofile,line, "file contains non valid code, no closing '}' for command");
203       return "";
204     }
205     out.addChar(':');
206     out.addChar(' ');
207     i++;
208 
209     if (data[i] == '[')
210     {
211       // handle [nr]
212       // run till ]
213       out.addChar('[');
214       i++;
215       while (i < size && (data[i] != ']')) nr += data[i++];
216       if (i >= size)
217       {
218         warn(macrofile,line, "file contains non valid code, no closing ']'");
219         return "";
220       }
221       i++;
222     }
223     else if (data[i] != '{')
224     {
225       warn(macrofile,line, "file contains non valid code, expected '[' or '{' got '%c'\n",data[i]);
226       return "";
227     }
228     // handle {replacement}
229     // retest as the '[' part might have advanced so we can have a new '{'
230     if (data[i] != '{')
231     {
232       warn(macrofile,line, "file contains non valid code, expected '{' got '%c'\n",data[i]);
233       return "";
234     }
235     out.addChar('"');
236     out.addChar('{');
237     i++;
238     // run till }
239     cnt = 1;
240     while (i < size && cnt)
241     {
242       switch(data[i])
243       {
244         case '\\':
245           out.addChar('\\'); // need to escape it for MathJax js code
246           out.addChar('\\');
247           i++;
248           if (data[i] == '\\') // we have an escaped backslash
249           {
250             out.addChar('\\');
251             out.addChar('\\');
252             i++;
253           }
254           else if (data[i] != '"') out.addChar(data[i++]); // double quote handled separately
255           break;
256         case '{':
257           cnt++;
258           out.addChar(data[i++]);
259           break;
260         case '}':
261           cnt--;
262           if (cnt) out.addChar(data[i]);
263           i++;
264           break;
265         case '"':
266           out.addChar('\\'); // need to escape it for MathJax js code
267           out.addChar(data[i++]);
268           break;
269         case '\n':
270           line++;
271           out.addChar(data[i++]);
272           break;
273         default:
274           out.addChar(data[i++]);
275           break;
276       }
277     }
278     if (i > size)
279     {
280       warn(macrofile,line, "file contains non valid code, no closing '}' for replacement");
281       return "";
282     }
283     out.addChar('}');
284     out.addChar('"');
285     if (!nr.isEmpty())
286     {
287       out.addChar(',');
288       out.addStr(nr);
289     }
290     if (!nr.isEmpty())
291     {
292       out.addChar(']');
293     }
294     out.addChar(',');
295     out.addChar('\n');
296   }
297   out.addChar(0);
298   return out.get();
299 }
300 
getSearchBox(bool serverSide,QCString relPath,bool highlightSearch)301 static QCString getSearchBox(bool serverSide, QCString relPath, bool highlightSearch)
302 {
303   TextStream t;
304   if (serverSide)
305   {
306     writeServerSearchBox(t, relPath, highlightSearch);
307   }
308   else
309   {
310     writeClientSearchBox(t, relPath);
311   }
312   return t.str();
313 }
314 
substituteHtmlKeywords(const QCString & str,const QCString & title,const QCString & relPath,const QCString & navPath=QCString ())315 static QCString substituteHtmlKeywords(const QCString &str,
316                                        const QCString &title,
317                                        const QCString &relPath,
318                                        const QCString &navPath=QCString())
319 {
320   // Build CSS/JavaScript tags depending on treeview, search engine settings
321   QCString cssFile;
322   QCString generatedBy;
323   QCString treeViewCssJs;
324   QCString searchCssJs;
325   QCString searchBox;
326   QCString mathJaxJs;
327   QCString extraCssText;
328 
329   QCString projectName = Config_getString(PROJECT_NAME);
330   bool timeStamp = Config_getBool(HTML_TIMESTAMP);
331   bool treeView = Config_getBool(GENERATE_TREEVIEW);
332   bool searchEngine = Config_getBool(SEARCHENGINE);
333   bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
334   bool mathJax = Config_getBool(USE_MATHJAX);
335   QCString mathJaxFormat = Config_getEnumAsString(MATHJAX_FORMAT);
336   bool disableIndex = Config_getBool(DISABLE_INDEX);
337   bool hasProjectName = !projectName.isEmpty();
338   bool hasProjectNumber = !Config_getString(PROJECT_NUMBER).isEmpty();
339   bool hasProjectBrief = !Config_getString(PROJECT_BRIEF).isEmpty();
340   bool hasProjectLogo = !Config_getString(PROJECT_LOGO).isEmpty();
341   bool hasFullSideBar = Config_getBool(FULL_SIDEBAR) && disableIndex && treeView;
342   static bool titleArea = (hasProjectName || hasProjectBrief || hasProjectLogo || (disableIndex && searchEngine));
343 
344   cssFile = Config_getString(HTML_STYLESHEET);
345   if (cssFile.isEmpty())
346   {
347     cssFile = "doxygen.css";
348   }
349   else
350   {
351     FileInfo cssfi(cssFile.str());
352     if (cssfi.exists())
353     {
354       cssFile = cssfi.fileName();
355     }
356     else
357     {
358       cssFile = "doxygen.css";
359     }
360   }
361 
362   extraCssText = "";
363   const StringVector &extraCssFile = Config_getList(HTML_EXTRA_STYLESHEET);
364   for (const auto &fileName : extraCssFile)
365   {
366     if (!fileName.empty())
367     {
368       FileInfo fi(fileName);
369       if (fi.exists())
370       {
371         extraCssText += "<link href=\"$relpath^"+stripPath(fileName.c_str())+"\" rel=\"stylesheet\" type=\"text/css\"/>\n";
372       }
373     }
374   }
375 
376   if (timeStamp)
377   {
378     generatedBy = theTranslator->trGeneratedAt(dateToString(TRUE),
379                                 convertToHtml(Config_getString(PROJECT_NAME)));
380   }
381   else
382   {
383     generatedBy = theTranslator->trGeneratedBy();
384   }
385 
386   if (treeView)
387   {
388     treeViewCssJs = "<link href=\"$relpath^navtree.css\" rel=\"stylesheet\" type=\"text/css\"/>\n"
389     //                    "<script type=\"text/javascript\">var page_layout=";
390     //treeViewCssJs += Config_getBool(DISABLE_INDEX) ? "1" : "0";
391     //treeViewCssJs += ";</script>\n"
392 			"<script type=\"text/javascript\" src=\"$relpath^resize.js\"></script>\n"
393 			"<script type=\"text/javascript\" src=\"$relpath^navtreedata.js\"></script>\n"
394 			"<script type=\"text/javascript\" src=\"$relpath^navtree.js\"></script>\n";
395   }
396 
397   if (searchEngine)
398   {
399     searchCssJs = "<link href=\"$relpath^search/search.css\" rel=\"stylesheet\" type=\"text/css\"/>\n";
400     if (!serverBasedSearch)
401     {
402       searchCssJs += "<script type=\"text/javascript\" src=\"$relpath^search/searchdata.js\"></script>\n";
403     }
404     searchCssJs += "<script type=\"text/javascript\" src=\"$relpath^search/search.js\"></script>\n";
405 
406     if (!serverBasedSearch)
407     {
408       if (disableIndex || !Config_getBool(HTML_DYNAMIC_MENUS))
409       {
410         searchCssJs += "<script type=\"text/javascript\">\n"
411 					"/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */\n"
412 				"  $(document).ready(function() { init_search(); });\n"
413 					"/* @license-end */\n"
414 					"</script>";
415       }
416     }
417     else
418     {
419       if (disableIndex || !Config_getBool(HTML_DYNAMIC_MENUS))
420       {
421         searchCssJs += "<script type=\"text/javascript\">\n"
422 					"/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */\n"
423 					"  $(document).ready(function() {\n"
424 					"    if ($('.searchresults').length > 0) { searchBox.DOMSearchField().focus(); }\n"
425 					"  });\n"
426 					"  /* @license-end */\n"
427 					"</script>\n";
428       }
429 
430       // OPENSEARCH_PROVIDER {
431       searchCssJs += "<link rel=\"search\" href=\"" + relPath +
432                      "search_opensearch.php?v=opensearch.xml\" "
433                      "type=\"application/opensearchdescription+xml\" title=\"" +
434                      (hasProjectName ? projectName : QCString("Doxygen")) +
435                      "\"/>";
436       // OPENSEARCH_PROVIDER }
437     }
438     searchBox = getSearchBox(serverBasedSearch, relPath, FALSE);
439   }
440 
441   if (mathJax)
442   {
443     auto mathJaxVersion = Config_getEnum(MATHJAX_VERSION);
444     QCString path = Config_getString(MATHJAX_RELPATH);
445     if (path.isEmpty() || path.left(2)=="..") // relative path
446     {
447       path.prepend(relPath);
448     }
449 
450     switch (mathJaxVersion)
451     {
452       case MATHJAX_VERSION_t::MathJax_3:
453         {
454           mathJaxJs += "<script src=\"https://polyfill.io/v3/polyfill.min.js?features=es6\"></script>\n"
455                        "<script type=\"text/javascript\">\n"
456                        "window.MathJax = {\n"
457                        "  options: {\n"
458                        "    ignoreHtmlClass: 'tex2jax_ignore',\n"
459                        "    processHtmlClass: 'tex2jax_process'\n"
460                        "  }";
461          const StringVector &mathJaxExtensions = Config_getList(MATHJAX_EXTENSIONS);
462          if (!mathJaxExtensions.empty() || !g_latex_macro.isEmpty())
463          {
464            mathJaxJs+= ",\n"
465                        "  tex: {\n"
466                        "    macros: {";
467            if (!g_latex_macro.isEmpty())
468            {
469              mathJaxJs += g_latex_macro+"    ";
470            }
471            mathJaxJs+="},\n"
472                        "    packages: ['base','configmacros'";
473            if (!g_latex_macro.isEmpty())
474            {
475              mathJaxJs+= ",'newcommand'";
476            }
477            for (const auto &s : mathJaxExtensions)
478            {
479              mathJaxJs+= ",'"+QCString(s.c_str())+"'";
480            }
481            mathJaxJs += "]\n"
482                          "  }\n";
483          }
484          else
485          {
486            mathJaxJs += "\n";
487          }
488          mathJaxJs += "};\n";
489          // MATHJAX_CODEFILE
490          if (!g_mathjax_code.isEmpty())
491          {
492            mathJaxJs += g_mathjax_code;
493            mathJaxJs += "\n";
494          }
495          mathJaxJs += "</script>\n";
496          mathJaxJs += "<script type=\"text/javascript\" id=\"MathJax-script\" async=\"async\" src=\"" +
497                       path + "es5/tex-" + mathJaxFormat.lower() + ".js\">";
498          mathJaxJs+="</script>\n";
499         }
500         break;
501       case MATHJAX_VERSION_t::MathJax_2:
502         {
503           mathJaxJs = "<script type=\"text/x-mathjax-config\">\n"
504                       "MathJax.Hub.Config({\n"
505                       "  extensions: [\"tex2jax.js\"";
506           const StringVector &mathJaxExtensions = Config_getList(MATHJAX_EXTENSIONS);
507           for (const auto &s : mathJaxExtensions)
508           {
509             mathJaxJs+= ", \""+QCString(s.c_str())+".js\"";
510           }
511           if (mathJaxFormat.isEmpty())
512           {
513             mathJaxFormat = "HTML-CSS";
514           }
515           mathJaxJs += "],\n"
516                        "  jax: [\"input/TeX\",\"output/"+mathJaxFormat+"\"],\n";
517           if (!g_latex_macro.isEmpty())
518           {
519             mathJaxJs += "   TeX: { Macros: {\n";
520             mathJaxJs += g_latex_macro;
521             mathJaxJs += "\n"
522                          "  } }\n";
523           }
524           mathJaxJs +=   "});\n";
525           if (!g_mathjax_code.isEmpty())
526           {
527             mathJaxJs += g_mathjax_code;
528             mathJaxJs += "\n";
529           }
530           mathJaxJs += "</script>\n";
531           mathJaxJs += "<script type=\"text/javascript\" async=\"async\" src=\"" + path + "MathJax.js\"></script>\n";
532         }
533         break;
534     }
535   }
536 
537   // first substitute generic keywords
538   QCString result = substituteKeywords(str,title,
539     convertToHtml(Config_getString(PROJECT_NAME)),
540     convertToHtml(Config_getString(PROJECT_NUMBER)),
541         convertToHtml(Config_getString(PROJECT_BRIEF)));
542 
543   // additional HTML only keywords
544   result = substitute(result,"$navpath",navPath);
545   result = substitute(result,"$stylesheet",cssFile);
546   result = substitute(result,"$treeview",treeViewCssJs);
547   result = substitute(result,"$searchbox",searchBox);
548   result = substitute(result,"$search",searchCssJs);
549   result = substitute(result,"$mathjax",mathJaxJs);
550   result = substitute(result,"$generatedby",generatedBy);
551   result = substitute(result,"$extrastylesheet",extraCssText);
552   result = substitute(result,"$relpath$",relPath); //<-- obsolete: for backwards compatibility only
553   result = substitute(result,"$relpath^",relPath); //<-- must be last
554 
555   // additional HTML only conditional blocks
556   result = selectBlock(result,"FULL_SIDEBAR",hasFullSideBar,OutputGenerator::Html);
557   result = selectBlock(result,"DISABLE_INDEX",disableIndex,OutputGenerator::Html);
558   result = selectBlock(result,"GENERATE_TREEVIEW",treeView,OutputGenerator::Html);
559   result = selectBlock(result,"SEARCHENGINE",searchEngine,OutputGenerator::Html);
560   result = selectBlock(result,"TITLEAREA",titleArea,OutputGenerator::Html);
561   result = selectBlock(result,"PROJECT_NAME",hasProjectName,OutputGenerator::Html);
562   result = selectBlock(result,"PROJECT_NUMBER",hasProjectNumber,OutputGenerator::Html);
563   result = selectBlock(result,"PROJECT_BRIEF",hasProjectBrief,OutputGenerator::Html);
564   result = selectBlock(result,"PROJECT_LOGO",hasProjectLogo,OutputGenerator::Html);
565 
566   result = removeEmptyLines(result);
567 
568   return result;
569 }
570 
571 //--------------------------------------------------------------------------
572 
HtmlCodeGenerator(TextStream & t)573 HtmlCodeGenerator::HtmlCodeGenerator(TextStream &t) : m_t(t)
574 {
575 }
576 
HtmlCodeGenerator(TextStream & t,const QCString & relPath)577 HtmlCodeGenerator::HtmlCodeGenerator(TextStream &t,const QCString &relPath)
578   : m_t(t), m_relPath(relPath)
579 {
580 }
581 
setRelativePath(const QCString & path)582 void HtmlCodeGenerator::setRelativePath(const QCString &path)
583 {
584   m_relPath = path;
585 }
586 
codify(const QCString & str)587 void HtmlCodeGenerator::codify(const QCString &str)
588 {
589   int tabSize = Config_getInt(TAB_SIZE);
590   if (!str.isEmpty())
591   {
592     const char *p=str.data();
593     char c;
594     int spacesToNextTabStop;
595     while (*p)
596     {
597       c=*p++;
598       switch(c)
599       {
600         case '\t': spacesToNextTabStop =
601                          tabSize - (m_col%tabSize);
602                    m_t << Doxygen::spaces.left(spacesToNextTabStop);
603                    m_col+=spacesToNextTabStop;
604                    break;
605         case '\n': m_t << "\n"; m_col=0;
606                    break;
607         case '\r': break;
608         case '<':  m_t << "&lt;"; m_col++;
609                    break;
610         case '>':  m_t << "&gt;"; m_col++;
611                    break;
612         case '&':  m_t << "&amp;"; m_col++;
613                    break;
614         case '\'': m_t << "&#39;"; m_col++; // &apos; is not valid XHTML
615                    break;
616         case '"':  m_t << "&quot;"; m_col++;
617                    break;
618         case '\\':
619                    if (*p=='<')
620                      { m_t << "&lt;"; p++; }
621                    else if (*p=='>')
622                      { m_t << "&gt;"; p++; }
623 		   else if (*p=='(')
624                      { m_t << "\\&zwj;("; m_col++;p++; }
625                    else if (*p==')')
626                      { m_t << "\\&zwj;)"; m_col++;p++; }
627                    else
628                      m_t << "\\";
629                    m_col++;
630                    break;
631         default:
632           {
633             uchar uc = static_cast<uchar>(c);
634             if (uc<32)
635             {
636               m_t << "&#x24" << hex[uc>>4] << hex[uc&0xF] << ";";
637               m_col++;
638             }
639             else
640             {
641               p=writeUTF8Char(m_t,p-1);
642               m_col++;
643             }
644           }
645           break;
646       }
647     }
648   }
649 }
650 
docify(const QCString & str)651 void HtmlCodeGenerator::docify(const QCString &str)
652 {
653   //m_t << getHtmlDirEmbeddingChar(getTextDirByConfig(str));
654 
655   if (!str.isEmpty())
656   {
657     const char *p=str.data();
658     char c;
659     while (*p)
660     {
661       c=*p++;
662       switch(c)
663       {
664         case '<':  m_t << "&lt;"; break;
665         case '>':  m_t << "&gt;"; break;
666         case '&':  m_t << "&amp;"; break;
667         case '"':  m_t << "&quot;"; break;
668         case '\\':
669           if (*p=='<')
670             { m_t << "&lt;"; p++; }
671           else if (*p=='>')
672             { m_t << "&gt;"; p++; }
673 	  else if (*p=='(')
674             { m_t << "\\&zwj;("; p++; }
675           else if (*p==')')
676             { m_t << "\\&zwj;)"; p++; }
677           else
678             m_t << "\\";
679           break;
680         default:
681           {
682             uchar uc = static_cast<uchar>(c);
683             if (uc<32 && !isspace(c))
684             {
685               m_t << "&#x24" << hex[uc>>4] << hex[uc&0xF] << ";";
686             }
687             else
688             {
689               m_t << c;
690             }
691           }
692           break;
693       }
694     }
695   }
696 }
697 
writeLineNumber(const QCString & ref,const QCString & filename,const QCString & anchor,int l,bool writeLineAnchor)698 void HtmlCodeGenerator::writeLineNumber(const QCString &ref,const QCString &filename,
699                                     const QCString &anchor,int l,bool writeLineAnchor)
700 {
701   const int maxLineNrStr = 10;
702   char lineNumber[maxLineNrStr];
703   char lineAnchor[maxLineNrStr];
704   qsnprintf(lineNumber,maxLineNrStr,"%5d",l);
705   qsnprintf(lineAnchor,maxLineNrStr,"l%05d",l);
706 
707   if (!m_lineOpen)
708   {
709     m_t << "<div class=\"line\">";
710     m_lineOpen = TRUE;
711   }
712 
713   if (writeLineAnchor) m_t << "<a id=\"" << lineAnchor << "\" name=\"" << lineAnchor << "\"></a>";
714   m_t << "<span class=\"lineno\">";
715   if (!filename.isEmpty())
716   {
717     _writeCodeLink("line",ref,filename,anchor,lineNumber,QCString());
718   }
719   else
720   {
721     codify(lineNumber);
722   }
723   m_t << "</span>";
724   m_col=0;
725 }
726 
writeCodeLink(CodeSymbolType type,const QCString & ref,const QCString & f,const QCString & anchor,const QCString & name,const QCString & tooltip)727 void HtmlCodeGenerator::writeCodeLink(CodeSymbolType type,
728                                       const QCString &ref,const QCString &f,
729                                       const QCString &anchor, const QCString &name,
730                                       const QCString &tooltip)
731 {
732   const char *hl = codeSymbolType2Str(type);
733   QCString hlClass = "code";
734   if (hl)
735   {
736     hlClass+=" hl_";
737     hlClass+=hl;
738   }
739   _writeCodeLink(hlClass,ref,f,anchor,name,tooltip);
740 }
741 
_writeCodeLink(const QCString & className,const QCString & ref,const QCString & f,const QCString & anchor,const QCString & name,const QCString & tooltip)742 void HtmlCodeGenerator::_writeCodeLink(const QCString &className,
743                                       const QCString &ref,const QCString &f,
744                                       const QCString &anchor, const QCString &name,
745                                       const QCString &tooltip)
746 {
747   if (!ref.isEmpty())
748   {
749     m_t << "<a class=\"" << className << "Ref\" ";
750     m_t << externalLinkTarget();
751   }
752   else
753   {
754     m_t << "<a class=\"" << className << "\" ";
755   }
756   m_t << "href=\"";
757   m_t << externalRef(m_relPath,ref,TRUE);
758   if (!f.isEmpty()) m_t << addHtmlExtensionIfMissing(f);
759   if (!anchor.isEmpty()) m_t << "#" << anchor;
760   m_t << "\"";
761   if (!tooltip.isEmpty()) m_t << " title=\"" << convertToHtml(tooltip) << "\"";
762   m_t << ">";
763   docify(name);
764   m_t << "</a>";
765   m_col+=name.length();
766 }
767 
writeTooltip(const QCString & id,const DocLinkInfo & docInfo,const QCString & decl,const QCString & desc,const SourceLinkInfo & defInfo,const SourceLinkInfo & declInfo)768 void HtmlCodeGenerator::writeTooltip(const QCString &id, const DocLinkInfo &docInfo,
769                                      const QCString &decl, const QCString &desc,
770                                      const SourceLinkInfo &defInfo,
771                                      const SourceLinkInfo &declInfo)
772 {
773   m_t << "<div class=\"ttc\" id=\"" << id << "\">";
774   m_t << "<div class=\"ttname\">";
775   if (!docInfo.url.isEmpty())
776   {
777     m_t << "<a href=\"";
778     m_t << externalRef(m_relPath,docInfo.ref,TRUE);
779     m_t << addHtmlExtensionIfMissing(docInfo.url);
780     if (!docInfo.anchor.isEmpty())
781     {
782       m_t << "#" << docInfo.anchor;
783     }
784     m_t << "\">";
785   }
786   docify(docInfo.name);
787   if (!docInfo.url.isEmpty())
788   {
789     m_t << "</a>";
790   }
791   m_t << "</div>";
792   if (!decl.isEmpty())
793   {
794     m_t << "<div class=\"ttdeci\">";
795     docify(decl);
796     m_t << "</div>";
797   }
798   if (!desc.isEmpty())
799   {
800     m_t << "<div class=\"ttdoc\">";
801     docify(desc);
802     m_t << "</div>";
803   }
804   if (!defInfo.file.isEmpty())
805   {
806     m_t << "<div class=\"ttdef\"><b>Definition:</b> ";
807     if (!defInfo.url.isEmpty())
808     {
809       m_t << "<a href=\"";
810       m_t << externalRef(m_relPath,defInfo.ref,TRUE);
811       m_t << addHtmlExtensionIfMissing(defInfo.url);
812       if (!defInfo.anchor.isEmpty())
813       {
814         m_t << "#" << defInfo.anchor;
815       }
816       m_t << "\">";
817     }
818     m_t << defInfo.file << ":" << defInfo.line;
819     if (!defInfo.url.isEmpty())
820     {
821       m_t << "</a>";
822     }
823     m_t << "</div>";
824   }
825   if (!declInfo.file.isEmpty())
826   {
827     m_t << "<div class=\"ttdecl\"><b>Declaration:</b> ";
828     if (!declInfo.url.isEmpty())
829     {
830       m_t << "<a href=\"";
831       m_t << externalRef(m_relPath,declInfo.ref,TRUE);
832       m_t << addHtmlExtensionIfMissing(declInfo.url);
833       if (!declInfo.anchor.isEmpty())
834       {
835         m_t << "#" << declInfo.anchor;
836       }
837       m_t << "\">";
838     }
839     m_t << declInfo.file << ":" << declInfo.line;
840     if (!declInfo.url.isEmpty())
841     {
842       m_t << "</a>";
843     }
844     m_t << "</div>";
845   }
846   m_t << "</div>\n";
847 }
848 
849 
startCodeLine(bool)850 void HtmlCodeGenerator::startCodeLine(bool)
851 {
852   m_col=0;
853   if (!m_lineOpen)
854   {
855     m_t << "<div class=\"line\">";
856     m_lineOpen = TRUE;
857   }
858 }
859 
endCodeLine()860 void HtmlCodeGenerator::endCodeLine()
861 {
862   if (m_col == 0)
863   {
864     m_t << " ";
865     m_col++;
866   }
867   if (m_lineOpen)
868   {
869     m_t << "</div>\n";
870     m_lineOpen = FALSE;
871   }
872 }
873 
startFontClass(const QCString & s)874 void HtmlCodeGenerator::startFontClass(const QCString &s)
875 {
876   m_t << "<span class=\"" << s << "\">";
877 }
878 
endFontClass()879 void HtmlCodeGenerator::endFontClass()
880 {
881   m_t << "</span>";
882 }
883 
writeCodeAnchor(const QCString & anchor)884 void HtmlCodeGenerator::writeCodeAnchor(const QCString &anchor)
885 {
886   m_t << "<a id=\"" << anchor << "\" name=\"" << anchor << "\"></a>";
887 }
888 
startCodeFragment(const QCString &)889 void HtmlCodeGenerator::startCodeFragment(const QCString &)
890 {
891   m_t << "<div class=\"fragment\">";
892 }
893 
endCodeFragment(const QCString &)894 void HtmlCodeGenerator::endCodeFragment(const QCString &)
895 {
896   //endCodeLine checks is there is still an open code line, if so closes it.
897   endCodeLine();
898 
899   m_t << "</div><!-- fragment -->";
900 }
901 
902 
903 //--------------------------------------------------------------------------
904 
HtmlGenerator()905 HtmlGenerator::HtmlGenerator() : OutputGenerator(Config_getString(HTML_OUTPUT)), m_codeGen(m_t)
906 {
907 }
908 
HtmlGenerator(const HtmlGenerator & og)909 HtmlGenerator::HtmlGenerator(const HtmlGenerator &og) : OutputGenerator(og), m_codeGen(m_t)
910 {
911 }
912 
operator =(const HtmlGenerator & og)913 HtmlGenerator &HtmlGenerator::operator=(const HtmlGenerator &og)
914 {
915   OutputGenerator::operator=(og);
916   return *this;
917 }
918 
clone() const919 std::unique_ptr<OutputGenerator> HtmlGenerator::clone() const
920 {
921   return std::make_unique<HtmlGenerator>(*this);
922 }
923 
~HtmlGenerator()924 HtmlGenerator::~HtmlGenerator()
925 {
926   //printf("HtmlGenerator::~HtmlGenerator()\n");
927 }
928 
929 
init()930 void HtmlGenerator::init()
931 {
932   QCString dname = Config_getString(HTML_OUTPUT);
933   Dir d(dname.str());
934   if (!d.exists() && !d.mkdir(dname.str()))
935   {
936     term("Could not create output directory %s\n",qPrint(dname));
937   }
938   //writeLogo(dname);
939   if (!Config_getString(HTML_HEADER).isEmpty())
940   {
941     g_header=fileToString(Config_getString(HTML_HEADER));
942     //printf("g_header='%s'\n",qPrint(g_header));
943   }
944   else
945   {
946     g_header = ResourceMgr::instance().getAsString("header.html");
947   }
948 
949   if (!Config_getString(HTML_FOOTER).isEmpty())
950   {
951     g_footer=fileToString(Config_getString(HTML_FOOTER));
952     //printf("g_footer='%s'\n",qPrint(g_footer));
953   }
954   else
955   {
956     g_footer = ResourceMgr::instance().getAsString("footer.html");
957   }
958 
959   if (Config_getBool(USE_MATHJAX))
960   {
961     if (!Config_getString(MATHJAX_CODEFILE).isEmpty())
962     {
963       g_mathjax_code=fileToString(Config_getString(MATHJAX_CODEFILE));
964       //printf("g_mathjax_code='%s'\n",qPrint(g_mathjax_code));
965     }
966     g_latex_macro=getConvertLatexMacro();
967     //printf("converted g_latex_macro='%s'\n",qPrint(g_latex_macro));
968   }
969   createSubDirs(d);
970 
971   ResourceMgr &mgr = ResourceMgr::instance();
972   if (Config_getBool(HTML_DYNAMIC_MENUS))
973   {
974     mgr.copyResourceAs("tabs.css",dname,"tabs.css");
975   }
976   else // stylesheet for the 'old' static tabs
977   {
978     mgr.copyResourceAs("fixed_tabs.css",dname,"tabs.css");
979   }
980   mgr.copyResource("jquery.js",dname);
981   if (Config_getBool(INTERACTIVE_SVG))
982   {
983     mgr.copyResource("svgpan.js",dname);
984   }
985 
986   if (!Config_getBool(DISABLE_INDEX) && Config_getBool(HTML_DYNAMIC_MENUS))
987   {
988     mgr.copyResource("menu.js",dname);
989   }
990 
991   {
992     std::ofstream f(dname.str()+"/dynsections.js",std::ofstream::out | std::ofstream::binary);
993     if (f.is_open())
994     {
995       TextStream t(&f);
996       t << mgr.getAsString("dynsections.js");
997       if (Config_getBool(SOURCE_BROWSER) && Config_getBool(SOURCE_TOOLTIPS))
998       {
999         t << mgr.getAsString("dynsections_tooltips.js");
1000       }
1001     }
1002   }
1003 }
1004 
cleanup()1005 void HtmlGenerator::cleanup()
1006 {
1007   QCString dname = Config_getString(HTML_OUTPUT);
1008   Dir d(dname.str());
1009   clearSubDirs(d);
1010 }
1011 
1012 /// Additional initialization after indices have been created
writeTabData()1013 void HtmlGenerator::writeTabData()
1014 {
1015   Doxygen::indexList->addStyleSheetFile("tabs.css");
1016   QCString dname=Config_getString(HTML_OUTPUT);
1017   ResourceMgr &mgr = ResourceMgr::instance();
1018   //writeColoredImgData(dname,colored_tab_data);
1019   mgr.copyResource("tab_a.lum",dname);
1020   mgr.copyResource("tab_b.lum",dname);
1021   mgr.copyResource("tab_h.lum",dname);
1022   mgr.copyResource("tab_s.lum",dname);
1023   mgr.copyResource("nav_h.lum",dname);
1024   mgr.copyResource("nav_f.lum",dname);
1025   mgr.copyResource("bc_s.luma",dname);
1026   mgr.copyResource("doxygen.svg",dname);
1027   Doxygen::indexList->addImageFile("doxygen.svg");
1028   mgr.copyResource("closed.luma",dname);
1029   mgr.copyResource("open.luma",dname);
1030   mgr.copyResource("bdwn.luma",dname);
1031   mgr.copyResource("sync_on.luma",dname);
1032   mgr.copyResource("sync_off.luma",dname);
1033 
1034   //{
1035   //  unsigned char shadow[6] = { 5, 5, 5, 5, 5, 5 };
1036   //  unsigned char shadow_alpha[6]  = { 80, 60, 40, 20, 10, 0 };
1037   //  ColoredImage img(1,6,shadow,shadow_alpha,0,0,100);
1038   //  img.save(dname+"/nav_g.png");
1039   //}
1040   mgr.copyResource("nav_g.png",dname);
1041   Doxygen::indexList->addImageFile("nav_g.png");
1042 }
1043 
writeSearchData(const QCString & dname)1044 void HtmlGenerator::writeSearchData(const QCString &dname)
1045 {
1046   bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
1047   //writeImgData(dname,serverBasedSearch ? search_server_data : search_client_data);
1048   ResourceMgr &mgr = ResourceMgr::instance();
1049 
1050   mgr.copyResource("search_l.png",dname);
1051   Doxygen::indexList->addImageFile("search/search_l.png");
1052   mgr.copyResource("search_m.png",dname);
1053   Doxygen::indexList->addImageFile("search/search_m.png");
1054   mgr.copyResource("search_r.png",dname);
1055   Doxygen::indexList->addImageFile("search/search_r.png");
1056   if (serverBasedSearch)
1057   {
1058     mgr.copyResource("mag.svg",dname);
1059     Doxygen::indexList->addImageFile("search/mag.svg");
1060   }
1061   else
1062   {
1063     mgr.copyResource("close.svg",dname);
1064     Doxygen::indexList->addImageFile("search/close.svg");
1065     mgr.copyResource("mag_sel.svg",dname);
1066     Doxygen::indexList->addImageFile("search/mag_sel.svg");
1067   }
1068 
1069   QCString searchDirName = dname;
1070   std::ofstream f(searchDirName.str()+"/search.css",std::ofstream::out | std::ofstream::binary);
1071   if (f.is_open())
1072   {
1073     TextStream t(&f);
1074     QCString searchCss;
1075     if (Config_getBool(DISABLE_INDEX))
1076     {
1077       if (Config_getBool(GENERATE_TREEVIEW) && Config_getBool(FULL_SIDEBAR))
1078       {
1079         searchCss = mgr.getAsString("search_sidebar.css");
1080       }
1081       else
1082       {
1083         searchCss = mgr.getAsString("search_nomenu.css");
1084       }
1085     }
1086     else if (!Config_getBool(HTML_DYNAMIC_MENUS))
1087     {
1088       searchCss = mgr.getAsString("search_fixedtabs.css");
1089     }
1090     else
1091     {
1092       searchCss = mgr.getAsString("search.css");
1093     }
1094     searchCss += mgr.getAsString("search_common.css");
1095     searchCss = substitute(replaceColorMarkers(searchCss),"$doxygenversion",getDoxygenVersion());
1096     t << searchCss;
1097     Doxygen::indexList->addStyleSheetFile("search/search.css");
1098   }
1099 }
1100 
writeStyleSheetFile(TextStream & t)1101 void HtmlGenerator::writeStyleSheetFile(TextStream &t)
1102 {
1103   t << replaceColorMarkers(substitute(ResourceMgr::instance().getAsString("doxygen.css"),"$doxygenversion",getDoxygenVersion()));
1104 }
1105 
writeHeaderFile(TextStream & t,const QCString &)1106 void HtmlGenerator::writeHeaderFile(TextStream &t, const QCString & /*cssname*/)
1107 {
1108   t << "<!-- HTML header for doxygen " << getDoxygenVersion() << "-->\n";
1109   t << ResourceMgr::instance().getAsString("header.html");
1110 }
1111 
writeFooterFile(TextStream & t)1112 void HtmlGenerator::writeFooterFile(TextStream &t)
1113 {
1114   t << "<!-- HTML footer for doxygen " << getDoxygenVersion() << "-->\n";
1115   t << ResourceMgr::instance().getAsString("footer.html");
1116 }
1117 
1118 static std::mutex g_indexLock;
1119 
startFile(const QCString & name,const QCString &,const QCString & title,int id)1120 void HtmlGenerator::startFile(const QCString &name,const QCString &,
1121                               const QCString &title,int id)
1122 {
1123   //printf("HtmlGenerator::startFile(%s)\n",qPrint(name));
1124   m_relPath = relativePathToRoot(name);
1125   QCString fileName = addHtmlExtensionIfMissing(name);
1126   m_lastTitle=title;
1127 
1128   startPlainFile(fileName);
1129   m_codeGen.setId(id);
1130   m_codeGen.setRelativePath(m_relPath);
1131   {
1132     std::lock_guard<std::mutex> lock(g_indexLock);
1133     Doxygen::indexList->addIndexFile(fileName);
1134   }
1135 
1136   m_lastFile = fileName;
1137   m_t << substituteHtmlKeywords(g_header,convertToHtml(filterTitle(title)),m_relPath);
1138 
1139   m_t << "<!-- " << theTranslator->trGeneratedBy() << " Doxygen "
1140       << getDoxygenVersion() << " -->\n";
1141   //bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
1142   bool searchEngine = Config_getBool(SEARCHENGINE);
1143   if (searchEngine /*&& !generateTreeView*/)
1144   {
1145     m_t << "<script type=\"text/javascript\">\n";
1146     m_t << "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */\n";
1147     m_t << "var searchBox = new SearchBox(\"searchBox\", \""
1148         << m_relPath<< "search\",'" << theTranslator->trSearch() << "','" << Doxygen::htmlFileExtension << "');\n";
1149     m_t << "/* @license-end */\n";
1150     m_t << "</script>\n";
1151   }
1152   //generateDynamicSections(t,relPath);
1153   m_sectionCount=0;
1154 }
1155 
writeSearchInfo(TextStream & t,const QCString &)1156 void HtmlGenerator::writeSearchInfo(TextStream &t,const QCString &)
1157 {
1158   bool searchEngine      = Config_getBool(SEARCHENGINE);
1159   bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
1160   if (searchEngine && !serverBasedSearch)
1161   {
1162     t << "<!-- window showing the filter options -->\n";
1163     t << "<div id=\"MSearchSelectWindow\"\n";
1164     t << "     onmouseover=\"return searchBox.OnSearchSelectShow()\"\n";
1165     t << "     onmouseout=\"return searchBox.OnSearchSelectHide()\"\n";
1166     t << "     onkeydown=\"return searchBox.OnSearchSelectKey(event)\">\n";
1167     t << "</div>\n";
1168     t << "\n";
1169     t << "<!-- iframe showing the search results (closed by default) -->\n";
1170     t << "<div id=\"MSearchResultsWindow\">\n";
1171     t << "<iframe src=\"javascript:void(0)\" frameborder=\"0\" \n";
1172     t << "        name=\"MSearchResults\" id=\"MSearchResults\">\n";
1173     t << "</iframe>\n";
1174     t << "</div>\n";
1175     t << "\n";
1176   }
1177 }
1178 
writeSearchInfo()1179 void HtmlGenerator::writeSearchInfo()
1180 {
1181   writeSearchInfo(m_t,m_relPath);
1182 }
1183 
1184 
writeLogoAsString(const QCString & path)1185 QCString HtmlGenerator::writeLogoAsString(const QCString &path)
1186 {
1187   bool timeStamp = Config_getBool(HTML_TIMESTAMP);
1188   QCString result;
1189   if (timeStamp)
1190   {
1191     result += theTranslator->trGeneratedAt(
1192                dateToString(TRUE),
1193                Config_getString(PROJECT_NAME)
1194               );
1195   }
1196   else
1197   {
1198     result += theTranslator->trGeneratedBy();
1199   }
1200   result += "&#160;\n<a href=\"https://www.doxygen.org/index.html\">\n"
1201             "<img class=\"footer\" src=\"";
1202   result += path;
1203   result += "doxygen.svg\" width=\"104\" height=\"31\" alt=\"doxygen\"/></a> ";
1204   result += getDoxygenVersion();
1205   result += " ";
1206   return result;
1207 }
1208 
writeLogo()1209 void HtmlGenerator::writeLogo()
1210 {
1211   m_t << writeLogoAsString(m_relPath);
1212 }
1213 
writePageFooter(TextStream & t,const QCString & lastTitle,const QCString & relPath,const QCString & navPath)1214 void HtmlGenerator::writePageFooter(TextStream &t,const QCString &lastTitle,
1215                               const QCString &relPath,const QCString &navPath)
1216 {
1217   t << substituteHtmlKeywords(g_footer,convertToHtml(lastTitle),relPath,navPath);
1218 }
1219 
writeFooter(const QCString & navPath)1220 void HtmlGenerator::writeFooter(const QCString &navPath)
1221 {
1222   writePageFooter(m_t,m_lastTitle,m_relPath,navPath);
1223 }
1224 
endFile()1225 void HtmlGenerator::endFile()
1226 {
1227   endPlainFile();
1228 }
1229 
startProjectNumber()1230 void HtmlGenerator::startProjectNumber()
1231 {
1232   m_t << "<h3 class=\"version\">";
1233 }
1234 
endProjectNumber()1235 void HtmlGenerator::endProjectNumber()
1236 {
1237   m_t << "</h3>";
1238 }
1239 
writeStyleInfo(int part)1240 void HtmlGenerator::writeStyleInfo(int part)
1241 {
1242   //printf("writeStyleInfo(%d)\n",part);
1243   if (part==0)
1244   {
1245     if (Config_getString(HTML_STYLESHEET).isEmpty()) // write default style sheet
1246     {
1247       //printf("write doxygen.css\n");
1248       startPlainFile("doxygen.css");
1249 
1250       // alternative, cooler looking titles
1251       //t << "H1 { text-align: center; border-width: thin none thin none;\n";
1252       //t << "     border-style : double; border-color : blue; padding-left : 1em; padding-right : 1em }\n";
1253 
1254       m_t << replaceColorMarkers(substitute(ResourceMgr::instance().getAsString("doxygen.css"),"$doxygenversion",getDoxygenVersion()));
1255       endPlainFile();
1256       Doxygen::indexList->addStyleSheetFile("doxygen.css");
1257     }
1258     else // write user defined style sheet
1259     {
1260       QCString cssname=Config_getString(HTML_STYLESHEET);
1261       FileInfo cssfi(cssname.str());
1262       if (!cssfi.exists() || !cssfi.isFile() || !cssfi.isReadable())
1263       {
1264         err("style sheet %s does not exist or is not readable!", qPrint(Config_getString(HTML_STYLESHEET)));
1265       }
1266       else
1267       {
1268         // convert style sheet to string
1269         QCString fileStr = fileToString(cssname);
1270         // write the string into the output dir
1271         startPlainFile(cssfi.fileName().c_str());
1272         m_t << fileStr;
1273         endPlainFile();
1274       }
1275       Doxygen::indexList->addStyleSheetFile(cssfi.fileName().c_str());
1276     }
1277     const StringVector &extraCssFiles = Config_getList(HTML_EXTRA_STYLESHEET);
1278     for (const auto &fileName : extraCssFiles)
1279     {
1280       if (!fileName.empty())
1281       {
1282         FileInfo fi(fileName);
1283         if (fi.exists())
1284         {
1285           Doxygen::indexList->addStyleSheetFile(fi.fileName().c_str());
1286         }
1287       }
1288     }
1289 
1290     Doxygen::indexList->addStyleSheetFile("jquery.js");
1291     Doxygen::indexList->addStyleSheetFile("dynsections.js");
1292     if (Config_getBool(INTERACTIVE_SVG))
1293     {
1294       Doxygen::indexList->addStyleSheetFile("svgpan.js");
1295     }
1296   }
1297 }
1298 
startDoxyAnchor(const QCString &,const QCString &,const QCString & anchor,const QCString &,const QCString &)1299 void HtmlGenerator::startDoxyAnchor(const QCString &,const QCString &,
1300                                     const QCString &anchor, const QCString &,
1301                                     const QCString &)
1302 {
1303   m_t << "<a id=\"" << anchor << "\" name=\"" << anchor << "\"></a>";
1304 }
1305 
endDoxyAnchor(const QCString &,const QCString &)1306 void HtmlGenerator::endDoxyAnchor(const QCString &,const QCString &)
1307 {
1308 }
1309 
1310 //void HtmlGenerator::newParagraph()
1311 //{
1312 //  t << "\n<p>\n";
1313 //}
1314 
startParagraph(const QCString & classDef)1315 void HtmlGenerator::startParagraph(const QCString &classDef)
1316 {
1317   if (!classDef.isEmpty())
1318     m_t << "\n<p class=\"" << classDef << "\">";
1319   else
1320     m_t << "\n<p>";
1321 }
1322 
endParagraph()1323 void HtmlGenerator::endParagraph()
1324 {
1325   m_t << "</p>\n";
1326 }
1327 
writeString(const QCString & text)1328 void HtmlGenerator::writeString(const QCString &text)
1329 {
1330   m_t << text;
1331 }
1332 
startIndexListItem()1333 void HtmlGenerator::startIndexListItem()
1334 {
1335   m_t << "<li>";
1336 }
1337 
endIndexListItem()1338 void HtmlGenerator::endIndexListItem()
1339 {
1340   m_t << "</li>\n";
1341 }
1342 
startIndexItem(const QCString & ref,const QCString & f)1343 void HtmlGenerator::startIndexItem(const QCString &ref,const QCString &f)
1344 {
1345   //printf("HtmlGenerator::startIndexItem(%s,%s)\n",ref,f);
1346   if (!ref.isEmpty() || !f.isEmpty())
1347   {
1348     if (!ref.isEmpty())
1349     {
1350       m_t << "<a class=\"elRef\" ";
1351       m_t << externalLinkTarget();
1352     }
1353     else
1354     {
1355       m_t << "<a class=\"el\" ";
1356     }
1357     m_t << "href=\"";
1358     m_t << externalRef(m_relPath,ref,TRUE);
1359     if (!f.isEmpty()) m_t << addHtmlExtensionIfMissing(f);
1360     m_t << "\">";
1361   }
1362   else
1363   {
1364     m_t << "<b>";
1365   }
1366 }
1367 
endIndexItem(const QCString & ref,const QCString & f)1368 void HtmlGenerator::endIndexItem(const QCString &ref,const QCString &f)
1369 {
1370   //printf("HtmlGenerator::endIndexItem(%s,%s,%s)\n",ref,f,name);
1371   if (!ref.isEmpty() || !f.isEmpty())
1372   {
1373     m_t << "</a>";
1374   }
1375   else
1376   {
1377     m_t << "</b>";
1378   }
1379 }
1380 
writeStartAnnoItem(const QCString &,const QCString & f,const QCString & path,const QCString & name)1381 void HtmlGenerator::writeStartAnnoItem(const QCString &,const QCString &f,
1382                                        const QCString &path,const QCString &name)
1383 {
1384   m_t << "<li>";
1385   if (!path.isEmpty()) docify(path);
1386   m_t << "<a class=\"el\" href=\"" << addHtmlExtensionIfMissing(f) << "\">";
1387   docify(name);
1388   m_t << "</a> ";
1389 }
1390 
writeObjectLink(const QCString & ref,const QCString & f,const QCString & anchor,const QCString & name)1391 void HtmlGenerator::writeObjectLink(const QCString &ref,const QCString &f,
1392                                     const QCString &anchor, const QCString &name)
1393 {
1394   if (!ref.isEmpty())
1395   {
1396     m_t << "<a class=\"elRef\" ";
1397     m_t << externalLinkTarget();
1398   }
1399   else
1400   {
1401     m_t << "<a class=\"el\" ";
1402   }
1403   m_t << "href=\"";
1404   m_t << externalRef(m_relPath,ref,TRUE);
1405   if (!f.isEmpty()) m_t << addHtmlExtensionIfMissing(f);
1406   if (!anchor.isEmpty()) m_t << "#" << anchor;
1407   m_t << "\">";
1408   docify(name);
1409   m_t << "</a>";
1410 }
1411 
startTextLink(const QCString & f,const QCString & anchor)1412 void HtmlGenerator::startTextLink(const QCString &f,const QCString &anchor)
1413 {
1414   m_t << "<a href=\"";
1415   if (!f.isEmpty())   m_t << m_relPath << addHtmlExtensionIfMissing(f);
1416   if (!anchor.isEmpty()) m_t << "#" << anchor;
1417   m_t << "\">";
1418 }
1419 
endTextLink()1420 void HtmlGenerator::endTextLink()
1421 {
1422   m_t << "</a>";
1423 }
1424 
startHtmlLink(const QCString & url)1425 void HtmlGenerator::startHtmlLink(const QCString &url)
1426 {
1427   bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
1428   m_t << "<a ";
1429   if (generateTreeView) m_t << "target=\"top\" ";
1430   m_t << "href=\"";
1431   if (!url.isEmpty()) m_t << url;
1432   m_t << "\">";
1433 }
1434 
endHtmlLink()1435 void HtmlGenerator::endHtmlLink()
1436 {
1437   m_t << "</a>";
1438 }
1439 
startGroupHeader(int extraIndentLevel)1440 void HtmlGenerator::startGroupHeader(int extraIndentLevel)
1441 {
1442   if (extraIndentLevel==2)
1443   {
1444     m_t << "<h4 class=\"groupheader\">";
1445   }
1446   else if (extraIndentLevel==1)
1447   {
1448     m_t << "<h3 class=\"groupheader\">";
1449   }
1450   else // extraIndentLevel==0
1451   {
1452     m_t << "<h2 class=\"groupheader\">";
1453   }
1454 }
1455 
endGroupHeader(int extraIndentLevel)1456 void HtmlGenerator::endGroupHeader(int extraIndentLevel)
1457 {
1458   if (extraIndentLevel==2)
1459   {
1460     m_t << "</h4>\n";
1461   }
1462   else if (extraIndentLevel==1)
1463   {
1464     m_t << "</h3>\n";
1465   }
1466   else
1467   {
1468     m_t << "</h2>\n";
1469   }
1470 }
1471 
startSection(const QCString & lab,const QCString &,SectionType type)1472 void HtmlGenerator::startSection(const QCString &lab,const QCString &,SectionType type)
1473 {
1474   switch(type)
1475   {
1476     case SectionType::Page:          m_t << "\n\n<h1>"; break;
1477     case SectionType::Section:       m_t << "\n\n<h2>"; break;
1478     case SectionType::Subsection:    m_t << "\n\n<h3>"; break;
1479     case SectionType::Subsubsection: m_t << "\n\n<h4>"; break;
1480     case SectionType::Paragraph:     m_t << "\n\n<h5>"; break;
1481     default: ASSERT(0); break;
1482   }
1483   m_t << "<a id=\"" << lab << "\" name=\"" << lab << "\"></a>";
1484 }
1485 
endSection(const QCString &,SectionType type)1486 void HtmlGenerator::endSection(const QCString &,SectionType type)
1487 {
1488   switch(type)
1489   {
1490     case SectionType::Page:          m_t << "</h1>"; break;
1491     case SectionType::Section:       m_t << "</h2>"; break;
1492     case SectionType::Subsection:    m_t << "</h3>"; break;
1493     case SectionType::Subsubsection: m_t << "</h4>"; break;
1494     case SectionType::Paragraph:     m_t << "</h5>"; break;
1495     default: ASSERT(0); break;
1496   }
1497 }
1498 
docify(const QCString & str)1499 void HtmlGenerator::docify(const QCString &str)
1500 {
1501   docify(str,FALSE);
1502 }
1503 
docify(const QCString & str,bool inHtmlComment)1504 void HtmlGenerator::docify(const QCString &str,bool inHtmlComment)
1505 {
1506   if (!str.isEmpty())
1507   {
1508     const char *p=str.data();
1509     char c;
1510     while (*p)
1511     {
1512       c=*p++;
1513       switch(c)
1514       {
1515         case '<':  m_t << "&lt;"; break;
1516         case '>':  m_t << "&gt;"; break;
1517         case '&':  m_t << "&amp;"; break;
1518         case '"':  m_t << "&quot;"; break;
1519         case '-':  if (inHtmlComment) m_t << "&#45;"; else m_t << "-"; break;
1520         case '\\':
1521                    if (*p=='<')
1522                      { m_t << "&lt;"; p++; }
1523                    else if (*p=='>')
1524                      { m_t << "&gt;"; p++; }
1525 		   else if (*p=='(')
1526                      { m_t << "\\&zwj;("; p++; }
1527                    else if (*p==')')
1528                      { m_t << "\\&zwj;)"; p++; }
1529                    else
1530                      m_t << "\\";
1531                    break;
1532         default:   m_t << c;
1533       }
1534     }
1535   }
1536 }
1537 
writeChar(char c)1538 void HtmlGenerator::writeChar(char c)
1539 {
1540   char cs[2];
1541   cs[0]=c;
1542   cs[1]=0;
1543   docify(cs);
1544 }
1545 
1546 //--- helper function for dynamic sections -------------------------
1547 
startSectionHeader(TextStream & t,const QCString & relPath,int sectionCount)1548 static void startSectionHeader(TextStream &t,
1549                                const QCString &relPath,int sectionCount)
1550 {
1551   //t << "<!-- startSectionHeader -->";
1552   bool dynamicSections = Config_getBool(HTML_DYNAMIC_SECTIONS);
1553   if (dynamicSections)
1554   {
1555     t << "<div id=\"dynsection-" << sectionCount << "\" "
1556          "onclick=\"return toggleVisibility(this)\" "
1557          "class=\"dynheader closed\" "
1558          "style=\"cursor:pointer;\">\n";
1559     t << "  <img id=\"dynsection-" << sectionCount << "-trigger\" src=\""
1560       << relPath << "closed.png\" alt=\"+\"/> ";
1561   }
1562   else
1563   {
1564     t << "<div class=\"dynheader\">\n";
1565   }
1566 }
1567 
endSectionHeader(TextStream & t)1568 static void endSectionHeader(TextStream &t)
1569 {
1570   //t << "<!-- endSectionHeader -->";
1571   t << "</div>\n";
1572 }
1573 
startSectionSummary(TextStream & t,int sectionCount)1574 static void startSectionSummary(TextStream &t,int sectionCount)
1575 {
1576   //t << "<!-- startSectionSummary -->";
1577   bool dynamicSections = Config_getBool(HTML_DYNAMIC_SECTIONS);
1578   if (dynamicSections)
1579   {
1580     t << "<div id=\"dynsection-" << sectionCount << "-summary\" "
1581          "class=\"dynsummary\" "
1582          "style=\"display:block;\">\n";
1583   }
1584 }
1585 
endSectionSummary(TextStream & t)1586 static void endSectionSummary(TextStream &t)
1587 {
1588   //t << "<!-- endSectionSummary -->";
1589   bool dynamicSections = Config_getBool(HTML_DYNAMIC_SECTIONS);
1590   if (dynamicSections)
1591   {
1592     t << "</div>\n";
1593   }
1594 }
1595 
startSectionContent(TextStream & t,int sectionCount)1596 static void startSectionContent(TextStream &t,int sectionCount)
1597 {
1598   //t << "<!-- startSectionContent -->";
1599   bool dynamicSections = Config_getBool(HTML_DYNAMIC_SECTIONS);
1600   if (dynamicSections)
1601   {
1602     t << "<div id=\"dynsection-" << sectionCount << "-content\" "
1603          "class=\"dyncontent\" "
1604          "style=\"display:none;\">\n";
1605   }
1606   else
1607   {
1608     t << "<div class=\"dyncontent\">\n";
1609   }
1610 }
1611 
endSectionContent(TextStream & t)1612 static void endSectionContent(TextStream &t)
1613 {
1614   //t << "<!-- endSectionContent -->";
1615   t << "</div>\n";
1616 }
1617 
1618 //----------------------------
1619 
startClassDiagram()1620 void HtmlGenerator::startClassDiagram()
1621 {
1622   startSectionHeader(m_t,m_relPath,m_sectionCount);
1623 }
1624 
endClassDiagram(const ClassDiagram & d,const QCString & fileName,const QCString & name)1625 void HtmlGenerator::endClassDiagram(const ClassDiagram &d,
1626                                 const QCString &fileName,const QCString &name)
1627 {
1628   endSectionHeader(m_t);
1629   startSectionSummary(m_t,m_sectionCount);
1630   endSectionSummary(m_t);
1631   startSectionContent(m_t,m_sectionCount);
1632   TextStream tt;
1633   d.writeImage(tt,dir(),m_relPath,fileName);
1634   if (!tt.empty())
1635   {
1636     m_t << " <div class=\"center\">\n";
1637     m_t << "  <img src=\"";
1638     m_t << m_relPath << fileName << ".png\" usemap=\"#" << convertToId(name);
1639     m_t << "_map\" alt=\"\"/>\n";
1640     m_t << "  <map id=\"" << convertToId(name);
1641     m_t << "_map\" name=\"" << convertToId(name);
1642     m_t << "_map\">\n";
1643     m_t << tt.str();
1644     m_t << "  </map>\n";
1645     m_t << "</div>";
1646   }
1647   else
1648   {
1649     m_t << " <div class=\"center\">\n";
1650     m_t << "  <img src=\"";
1651     m_t << m_relPath << fileName << ".png\" alt=\"\"/>\n";
1652     m_t << " </div>";
1653   }
1654   endSectionContent(m_t);
1655   m_sectionCount++;
1656 }
1657 
1658 
startMemberList()1659 void HtmlGenerator::startMemberList()
1660 {
1661   DBG_HTML(m_t << "<!-- startMemberList -->\n")
1662 }
1663 
endMemberList()1664 void HtmlGenerator::endMemberList()
1665 {
1666   DBG_HTML(m_t << "<!-- endMemberList -->\n")
1667 }
1668 
1669 // anonymous type:
1670 //  0 = single column right aligned
1671 //  1 = double column left aligned
1672 //  2 = single column left aligned
startMemberItem(const QCString & anchor,int annoType,const QCString & inheritId)1673 void HtmlGenerator::startMemberItem(const QCString &anchor,int annoType,const QCString &inheritId)
1674 {
1675   DBG_HTML(m_t << "<!-- startMemberItem() -->\n")
1676   if (m_emptySection)
1677   {
1678     m_t << "<table class=\"memberdecls\">\n";
1679     m_emptySection=FALSE;
1680   }
1681   m_t << "<tr class=\"memitem:" << anchor;
1682   if (!inheritId.isEmpty())
1683   {
1684     m_t << " inherit " << inheritId;
1685   }
1686   m_t << "\">";
1687   insertMemberAlignLeft(annoType, true);
1688 }
1689 
endMemberItem()1690 void HtmlGenerator::endMemberItem()
1691 {
1692   m_t << "</td></tr>\n";
1693 }
1694 
startMemberTemplateParams()1695 void HtmlGenerator::startMemberTemplateParams()
1696 {
1697 }
1698 
endMemberTemplateParams(const QCString & anchor,const QCString & inheritId)1699 void HtmlGenerator::endMemberTemplateParams(const QCString &anchor,const QCString &inheritId)
1700 {
1701   m_t << "</td></tr>\n";
1702   m_t << "<tr class=\"memitem:" << anchor;
1703   if (!inheritId.isEmpty())
1704   {
1705     m_t << " inherit " << inheritId;
1706   }
1707   m_t << "\"><td class=\"memTemplItemLeft\" align=\"right\" valign=\"top\">";
1708 }
1709 
startCompoundTemplateParams()1710 void HtmlGenerator::startCompoundTemplateParams()
1711 {
1712   m_t << "<div class=\"compoundTemplParams\">";
1713 }
1714 
endCompoundTemplateParams()1715 void HtmlGenerator::endCompoundTemplateParams()
1716 {
1717   m_t << "</div>";
1718 }
1719 
insertMemberAlign(bool templ)1720 void HtmlGenerator::insertMemberAlign(bool templ)
1721 {
1722   DBG_HTML(m_t << "<!-- insertMemberAlign -->\n")
1723   QCString className = templ ? "memTemplItemRight" : "memItemRight";
1724   m_t << "&#160;</td><td class=\"" << className << "\" valign=\"bottom\">";
1725 }
1726 
insertMemberAlignLeft(int annoType,bool initTag)1727 void HtmlGenerator::insertMemberAlignLeft(int annoType, bool initTag)
1728 {
1729   if (!initTag) m_t << "&#160;</td>";
1730   switch(annoType)
1731   {
1732     case 0:  m_t << "<td class=\"memItemLeft\" align=\"right\" valign=\"top\">"; break;
1733     case 1:  m_t << "<td class=\"memItemLeft\" >"; break;
1734     case 2:  m_t << "<td class=\"memItemLeft\" valign=\"top\">"; break;
1735     default: m_t << "<td class=\"memTemplParams\" colspan=\"2\">"; break;
1736   }
1737 }
1738 
startMemberDescription(const QCString & anchor,const QCString & inheritId,bool typ)1739 void HtmlGenerator::startMemberDescription(const QCString &anchor,const QCString &inheritId, bool typ)
1740 {
1741   DBG_HTML(m_t << "<!-- startMemberDescription -->\n")
1742   if (m_emptySection)
1743   {
1744     m_t << "<table class=\"memberdecls\">\n";
1745     m_emptySection=FALSE;
1746   }
1747   m_t << "<tr class=\"memdesc:" << anchor;
1748   if (!inheritId.isEmpty())
1749   {
1750     m_t << " inherit " << inheritId;
1751   }
1752   m_t << "\">";
1753   m_t << "<td class=\"mdescLeft\">&#160;</td>";
1754   if (typ) m_t << "<td class=\"mdescLeft\">&#160;</td>";
1755   m_t << "<td class=\"mdescRight\">";;
1756 }
1757 
endMemberDescription()1758 void HtmlGenerator::endMemberDescription()
1759 {
1760   DBG_HTML(m_t << "<!-- endMemberDescription -->\n")
1761   m_t << "<br /></td></tr>\n";
1762 }
1763 
startMemberSections()1764 void HtmlGenerator::startMemberSections()
1765 {
1766   DBG_HTML(m_t << "<!-- startMemberSections -->\n")
1767   m_emptySection=TRUE; // we postpone writing <table> until we actually
1768                        // write a row to prevent empty tables, which
1769                        // are not valid XHTML!
1770 }
1771 
endMemberSections()1772 void HtmlGenerator::endMemberSections()
1773 {
1774   DBG_HTML(m_t << "<!-- endMemberSections -->\n")
1775   if (!m_emptySection)
1776   {
1777     m_t << "</table>\n";
1778   }
1779 }
1780 
startMemberHeader(const QCString & anchor,int typ)1781 void HtmlGenerator::startMemberHeader(const QCString &anchor, int typ)
1782 {
1783   DBG_HTML(m_t << "<!-- startMemberHeader -->\n")
1784   if (!m_emptySection)
1785   {
1786     m_t << "</table>";
1787     m_emptySection=TRUE;
1788   }
1789   if (m_emptySection)
1790   {
1791     m_t << "<table class=\"memberdecls\">\n";
1792     m_emptySection=FALSE;
1793   }
1794   m_t << "<tr class=\"heading\"><td colspan=\"" << typ << "\"><h2 class=\"groupheader\">";
1795   if (!anchor.isEmpty())
1796   {
1797     m_t << "<a id=\"" << anchor << "\" name=\"" << anchor << "\"></a>\n";
1798   }
1799 }
1800 
endMemberHeader()1801 void HtmlGenerator::endMemberHeader()
1802 {
1803   DBG_HTML(m_t << "<!-- endMemberHeader -->\n")
1804   m_t << "</h2></td></tr>\n";
1805 }
1806 
startMemberSubtitle()1807 void HtmlGenerator::startMemberSubtitle()
1808 {
1809   DBG_HTML(m_t << "<!-- startMemberSubtitle -->\n")
1810   m_t << "<tr><td class=\"ititle\" colspan=\"2\">";
1811 }
1812 
endMemberSubtitle()1813 void HtmlGenerator::endMemberSubtitle()
1814 {
1815   DBG_HTML(m_t << "<!-- endMemberSubtitle -->\n")
1816   m_t << "</td></tr>\n";
1817 }
1818 
startIndexList()1819 void HtmlGenerator::startIndexList()
1820 {
1821   m_t << "<table>\n";
1822 }
1823 
endIndexList()1824 void HtmlGenerator::endIndexList()
1825 {
1826   m_t << "</table>\n";
1827 }
1828 
startIndexKey()1829 void HtmlGenerator::startIndexKey()
1830 {
1831   // inserted 'class = ...', 02 jan 2002, jh
1832   m_t << "  <tr><td class=\"indexkey\">";
1833 }
1834 
endIndexKey()1835 void HtmlGenerator::endIndexKey()
1836 {
1837   m_t << "</td>";
1838 }
1839 
startIndexValue(bool)1840 void HtmlGenerator::startIndexValue(bool)
1841 {
1842   // inserted 'class = ...', 02 jan 2002, jh
1843   m_t << "<td class=\"indexvalue\">";
1844 }
1845 
endIndexValue(const QCString &,bool)1846 void HtmlGenerator::endIndexValue(const QCString &,bool)
1847 {
1848   m_t << "</td></tr>\n";
1849 }
1850 
startMemberDocList()1851 void HtmlGenerator::startMemberDocList()
1852 {
1853   DBG_HTML(m_t << "<!-- startMemberDocList -->\n";)
1854 }
1855 
endMemberDocList()1856 void HtmlGenerator::endMemberDocList()
1857 {
1858   DBG_HTML(m_t << "<!-- endMemberDocList -->\n";)
1859 }
1860 
startMemberDoc(const QCString & clName,const QCString & memName,const QCString & anchor,const QCString & title,int memCount,int memTotal,bool showInline)1861 void HtmlGenerator::startMemberDoc( const QCString &clName, const QCString &memName,
1862                                     const QCString &anchor, const QCString &title,
1863                                     int memCount, int memTotal, bool showInline)
1864 {
1865   DBG_HTML(m_t << "<!-- startMemberDoc -->\n";)
1866   m_t << "\n<h2 class=\"memtitle\">"
1867       << "<span class=\"permalink\"><a href=\"#" << anchor << "\">&#9670;&nbsp;</a></span>";
1868   docify(title);
1869   if (memTotal>1)
1870   {
1871     m_t << " <span class=\"overload\">[" << memCount << "/" << memTotal <<"]</span>";
1872   }
1873   m_t << "</h2>\n";
1874   m_t << "\n<div class=\"memitem\">\n";
1875   m_t << "<div class=\"memproto\">\n";
1876 }
1877 
startMemberDocPrefixItem()1878 void HtmlGenerator::startMemberDocPrefixItem()
1879 {
1880   DBG_HTML(m_t << "<!-- startMemberDocPrefixItem -->\n";)
1881   m_t << "<div class=\"memtemplate\">\n";
1882 }
1883 
endMemberDocPrefixItem()1884 void HtmlGenerator::endMemberDocPrefixItem()
1885 {
1886   DBG_HTML(m_t << "<!-- endMemberDocPrefixItem -->\n";)
1887   m_t << "</div>\n";
1888 }
1889 
startMemberDocName(bool)1890 void HtmlGenerator::startMemberDocName(bool /*align*/)
1891 {
1892   DBG_HTML(m_t << "<!-- startMemberDocName -->\n";)
1893 
1894   m_t << "      <table class=\"memname\">\n";
1895 
1896   m_t << "        <tr>\n";
1897   m_t << "          <td class=\"memname\">";
1898 }
1899 
endMemberDocName()1900 void HtmlGenerator::endMemberDocName()
1901 {
1902   DBG_HTML(m_t << "<!-- endMemberDocName -->\n";)
1903   m_t << "</td>\n";
1904 }
1905 
startParameterList(bool openBracket)1906 void HtmlGenerator::startParameterList(bool openBracket)
1907 {
1908   DBG_HTML(m_t << "<!-- startParameterList -->\n";)
1909   m_t << "          <td>";
1910   if (openBracket) m_t << "(";
1911   m_t << "</td>\n";
1912 }
1913 
startParameterType(bool first,const QCString & key)1914 void HtmlGenerator::startParameterType(bool first,const QCString &key)
1915 {
1916   if (first)
1917   {
1918     DBG_HTML(m_t << "<!-- startFirstParameterType -->\n";)
1919     m_t << "          <td class=\"paramtype\">";
1920   }
1921   else
1922   {
1923     DBG_HTML(m_t << "<!-- startParameterType -->\n";)
1924     m_t << "        <tr>\n";
1925     m_t << "          <td class=\"paramkey\">" << key << "</td>\n";
1926     m_t << "          <td></td>\n";
1927     m_t << "          <td class=\"paramtype\">";
1928   }
1929 }
1930 
endParameterType()1931 void HtmlGenerator::endParameterType()
1932 {
1933   DBG_HTML(m_t << "<!-- endParameterType -->\n";)
1934   m_t << "&#160;</td>\n";
1935 }
1936 
startParameterName(bool)1937 void HtmlGenerator::startParameterName(bool /*oneArgOnly*/)
1938 {
1939   DBG_HTML(m_t << "<!-- startParameterName -->\n";)
1940   m_t << "          <td class=\"paramname\">";
1941 }
1942 
endParameterName(bool last,bool emptyList,bool closeBracket)1943 void HtmlGenerator::endParameterName(bool last,bool emptyList,bool closeBracket)
1944 {
1945   DBG_HTML(m_t << "<!-- endParameterName -->\n";)
1946   if (last)
1947   {
1948     if (emptyList)
1949     {
1950       if (closeBracket) m_t << "</td><td>)";
1951       m_t << "</td>\n";
1952       m_t << "          <td>";
1953     }
1954     else
1955     {
1956       m_t << "&#160;</td>\n";
1957       m_t << "        </tr>\n";
1958       m_t << "        <tr>\n";
1959       m_t << "          <td></td>\n";
1960       m_t << "          <td>";
1961       if (closeBracket) m_t << ")";
1962       m_t << "</td>\n";
1963       m_t << "          <td></td><td>";
1964     }
1965   }
1966   else
1967   {
1968     m_t << "</td>\n";
1969     m_t << "        </tr>\n";
1970   }
1971 }
1972 
endParameterList()1973 void HtmlGenerator::endParameterList()
1974 {
1975   DBG_HTML(m_t << "<!-- endParameterList -->\n";)
1976   m_t << "</td>\n";
1977   m_t << "        </tr>\n";
1978 }
1979 
exceptionEntry(const QCString & prefix,bool closeBracket)1980 void HtmlGenerator::exceptionEntry(const QCString &prefix,bool closeBracket)
1981 {
1982   DBG_HTML(m_t << "<!-- exceptionEntry -->\n";)
1983   m_t << "</td>\n";
1984   m_t << "        </tr>\n";
1985   m_t << "        <tr>\n";
1986   m_t << "          <td align=\"right\">";
1987   // colspan 2 so it gets both parameter type and parameter name columns
1988   if (!prefix.isEmpty())
1989     m_t << prefix << "</td><td>(</td><td colspan=\"2\">";
1990   else if (closeBracket)
1991     m_t << "</td><td>)</td><td></td><td>";
1992   else
1993     m_t << "</td><td></td><td colspan=\"2\">";
1994 }
1995 
endMemberDoc(bool hasArgs)1996 void HtmlGenerator::endMemberDoc(bool hasArgs)
1997 {
1998   DBG_HTML(m_t << "<!-- endMemberDoc -->\n";)
1999   if (!hasArgs)
2000   {
2001     m_t << "        </tr>\n";
2002   }
2003   m_t << "      </table>\n";
2004  // m_t << "</div>\n";
2005 }
2006 
startDotGraph()2007 void HtmlGenerator::startDotGraph()
2008 {
2009   startSectionHeader(m_t,m_relPath,m_sectionCount);
2010 }
2011 
endDotGraph(DotClassGraph & g)2012 void HtmlGenerator::endDotGraph(DotClassGraph &g)
2013 {
2014   bool generateLegend = Config_getBool(GENERATE_LEGEND);
2015   bool umlLook = Config_getBool(UML_LOOK);
2016   endSectionHeader(m_t);
2017   startSectionSummary(m_t,m_sectionCount);
2018   endSectionSummary(m_t);
2019   startSectionContent(m_t,m_sectionCount);
2020 
2021   g.writeGraph(m_t,GOF_BITMAP,EOF_Html,dir(),fileName(),m_relPath,TRUE,TRUE,m_sectionCount);
2022   if (generateLegend && !umlLook)
2023   {
2024     m_t << "<center><span class=\"legend\">[";
2025     startHtmlLink((m_relPath+"graph_legend"+Doxygen::htmlFileExtension));
2026     m_t << theTranslator->trLegend();
2027     endHtmlLink();
2028     m_t << "]</span></center>";
2029   }
2030 
2031   endSectionContent(m_t);
2032   m_sectionCount++;
2033 }
2034 
startInclDepGraph()2035 void HtmlGenerator::startInclDepGraph()
2036 {
2037   startSectionHeader(m_t,m_relPath,m_sectionCount);
2038 }
2039 
endInclDepGraph(DotInclDepGraph & g)2040 void HtmlGenerator::endInclDepGraph(DotInclDepGraph &g)
2041 {
2042   endSectionHeader(m_t);
2043   startSectionSummary(m_t,m_sectionCount);
2044   endSectionSummary(m_t);
2045   startSectionContent(m_t,m_sectionCount);
2046 
2047   g.writeGraph(m_t,GOF_BITMAP,EOF_Html,dir(),fileName(),m_relPath,TRUE,m_sectionCount);
2048 
2049   endSectionContent(m_t);
2050   m_sectionCount++;
2051 }
2052 
startGroupCollaboration()2053 void HtmlGenerator::startGroupCollaboration()
2054 {
2055   startSectionHeader(m_t,m_relPath,m_sectionCount);
2056 }
2057 
endGroupCollaboration(DotGroupCollaboration & g)2058 void HtmlGenerator::endGroupCollaboration(DotGroupCollaboration &g)
2059 {
2060   endSectionHeader(m_t);
2061   startSectionSummary(m_t,m_sectionCount);
2062   endSectionSummary(m_t);
2063   startSectionContent(m_t,m_sectionCount);
2064 
2065   g.writeGraph(m_t,GOF_BITMAP,EOF_Html,dir(),fileName(),m_relPath,TRUE,m_sectionCount);
2066 
2067   endSectionContent(m_t);
2068   m_sectionCount++;
2069 }
2070 
startCallGraph()2071 void HtmlGenerator::startCallGraph()
2072 {
2073   startSectionHeader(m_t,m_relPath,m_sectionCount);
2074 }
2075 
endCallGraph(DotCallGraph & g)2076 void HtmlGenerator::endCallGraph(DotCallGraph &g)
2077 {
2078   endSectionHeader(m_t);
2079   startSectionSummary(m_t,m_sectionCount);
2080   endSectionSummary(m_t);
2081   startSectionContent(m_t,m_sectionCount);
2082 
2083   g.writeGraph(m_t,GOF_BITMAP,EOF_Html,dir(),fileName(),m_relPath,TRUE,m_sectionCount);
2084 
2085   endSectionContent(m_t);
2086   m_sectionCount++;
2087 }
2088 
startDirDepGraph()2089 void HtmlGenerator::startDirDepGraph()
2090 {
2091   startSectionHeader(m_t,m_relPath,m_sectionCount);
2092 }
2093 
endDirDepGraph(DotDirDeps & g)2094 void HtmlGenerator::endDirDepGraph(DotDirDeps &g)
2095 {
2096   endSectionHeader(m_t);
2097   startSectionSummary(m_t,m_sectionCount);
2098   endSectionSummary(m_t);
2099   startSectionContent(m_t,m_sectionCount);
2100 
2101   g.writeGraph(m_t,GOF_BITMAP,EOF_Html,dir(),fileName(),m_relPath,TRUE,m_sectionCount);
2102 
2103   endSectionContent(m_t);
2104   m_sectionCount++;
2105 }
2106 
writeGraphicalHierarchy(DotGfxHierarchyTable & g)2107 void HtmlGenerator::writeGraphicalHierarchy(DotGfxHierarchyTable &g)
2108 {
2109   g.writeGraph(m_t,dir(),fileName());
2110 }
2111 
startMemberGroupHeader(bool)2112 void HtmlGenerator::startMemberGroupHeader(bool)
2113 {
2114   m_t << "<tr><td colspan=\"2\"><div class=\"groupHeader\">";
2115 }
2116 
endMemberGroupHeader()2117 void HtmlGenerator::endMemberGroupHeader()
2118 {
2119   m_t << "</div></td></tr>\n";
2120 }
2121 
startMemberGroupDocs()2122 void HtmlGenerator::startMemberGroupDocs()
2123 {
2124   m_t << "<tr><td colspan=\"2\"><div class=\"groupText\">";
2125 }
2126 
endMemberGroupDocs()2127 void HtmlGenerator::endMemberGroupDocs()
2128 {
2129   m_t << "</div></td></tr>\n";
2130 }
2131 
startMemberGroup()2132 void HtmlGenerator::startMemberGroup()
2133 {
2134 }
2135 
endMemberGroup(bool)2136 void HtmlGenerator::endMemberGroup(bool)
2137 {
2138 }
2139 
startIndent()2140 void HtmlGenerator::startIndent()
2141 {
2142   DBG_HTML(m_t << "<!-- startIndent -->\n";)
2143 
2144   m_t << "<div class=\"memdoc\">\n";
2145 }
2146 
endIndent()2147 void HtmlGenerator::endIndent()
2148 {
2149   DBG_HTML(m_t << "<!-- endIndent -->\n";)
2150   m_t << "\n</div>\n" << "</div>\n";
2151 }
2152 
addIndexItem(const QCString &,const QCString &)2153 void HtmlGenerator::addIndexItem(const QCString &,const QCString &)
2154 {
2155 }
2156 
writeNonBreakableSpace(int n)2157 void HtmlGenerator::writeNonBreakableSpace(int n)
2158 {
2159   int i;
2160   for (i=0;i<n;i++)
2161   {
2162     m_t << "&#160;";
2163   }
2164 }
2165 
startDescTable(const QCString & title)2166 void HtmlGenerator::startDescTable(const QCString &title)
2167 {
2168   m_t << "<table class=\"fieldtable\">\n"
2169       << "<tr><th colspan=\"2\">" << title << "</th></tr>";
2170 }
endDescTable()2171 void HtmlGenerator::endDescTable()
2172 {
2173   m_t << "</table>\n";
2174 }
2175 
startDescTableRow()2176 void HtmlGenerator::startDescTableRow()
2177 {
2178   m_t << "<tr>";
2179 }
2180 
endDescTableRow()2181 void HtmlGenerator::endDescTableRow()
2182 {
2183   m_t << "</tr>\n";
2184 }
2185 
startDescTableTitle()2186 void HtmlGenerator::startDescTableTitle()
2187 {
2188   m_t << "<td class=\"fieldname\">";
2189 }
2190 
endDescTableTitle()2191 void HtmlGenerator::endDescTableTitle()
2192 {
2193   m_t << "&#160;</td>";
2194 }
2195 
startDescTableData()2196 void HtmlGenerator::startDescTableData()
2197 {
2198   m_t << "<td class=\"fielddoc\">";
2199 }
2200 
endDescTableData()2201 void HtmlGenerator::endDescTableData()
2202 {
2203   m_t << "</td>";
2204 }
2205 
startExamples()2206 void HtmlGenerator::startExamples()
2207 {
2208   m_t << "<dl class=\"section examples\"><dt>";
2209   docify(theTranslator->trExamples());
2210   m_t << "</dt>";
2211 }
2212 
endExamples()2213 void HtmlGenerator::endExamples()
2214 {
2215   m_t << "</dl>\n";
2216 }
2217 
startParamList(ParamListTypes,const QCString & title)2218 void HtmlGenerator::startParamList(ParamListTypes,
2219                                 const QCString &title)
2220 {
2221   m_t << "<dl><dt><b>";
2222   docify(title);
2223   m_t << "</b></dt>";
2224 }
2225 
endParamList()2226 void HtmlGenerator::endParamList()
2227 {
2228   m_t << "</dl>";
2229 }
2230 
writeDoc(DocNode * n,const Definition * ctx,const MemberDef *,int id)2231 void HtmlGenerator::writeDoc(DocNode *n,const Definition *ctx,const MemberDef *,int id)
2232 {
2233   m_codeGen.setId(id);
2234   HtmlDocVisitor *visitor = new HtmlDocVisitor(m_t,m_codeGen,ctx);
2235   n->accept(visitor);
2236   delete visitor;
2237 }
2238 
2239 //---------------- helpers for index generation -----------------------------
2240 
startQuickIndexList(TextStream & t,bool compact,bool topLevel=TRUE)2241 static void startQuickIndexList(TextStream &t,bool compact,bool topLevel=TRUE)
2242 {
2243   if (compact)
2244   {
2245     if (topLevel)
2246     {
2247       t << "  <div id=\"navrow1\" class=\"tabs\">\n";
2248     }
2249     else
2250     {
2251       t << "  <div id=\"navrow2\" class=\"tabs2\">\n";
2252     }
2253     t << "    <ul class=\"tablist\">\n";
2254   }
2255   else
2256   {
2257     t << "<ul>";
2258   }
2259 }
2260 
endQuickIndexList(TextStream & t,bool compact)2261 static void endQuickIndexList(TextStream &t,bool compact)
2262 {
2263   if (compact)
2264   {
2265     t << "    </ul>\n";
2266     t << "  </div>\n";
2267   }
2268   else
2269   {
2270     t << "</ul>\n";
2271   }
2272 }
2273 
startQuickIndexItem(TextStream & t,const QCString & l,bool hl,bool,const QCString & relPath)2274 static void startQuickIndexItem(TextStream &t,const QCString &l,
2275                                 bool hl,bool /*compact*/,
2276                                 const QCString &relPath)
2277 {
2278   t << "      <li";
2279   if (hl)
2280   {
2281     t << " class=\"current\"";
2282   }
2283   t << ">";
2284   if (!l.isEmpty()) t << "<a href=\"" << correctURL(l,relPath) << "\">";
2285   t << "<span>";
2286 }
2287 
endQuickIndexItem(TextStream & t,const QCString & l)2288 static void endQuickIndexItem(TextStream &t,const QCString &l)
2289 {
2290   t << "</span>";
2291   if (!l.isEmpty()) t << "</a>";
2292   t << "</li>\n";
2293 }
2294 
quickLinkVisible(LayoutNavEntry::Kind kind)2295 static bool quickLinkVisible(LayoutNavEntry::Kind kind)
2296 {
2297   bool showNamespaces = Config_getBool(SHOW_NAMESPACES);
2298   switch (kind)
2299   {
2300     case LayoutNavEntry::MainPage:           return TRUE;
2301     case LayoutNavEntry::User:               return TRUE;
2302     case LayoutNavEntry::UserGroup:          return TRUE;
2303     case LayoutNavEntry::Pages:              return indexedPages>0;
2304     case LayoutNavEntry::Modules:            return documentedGroups>0;
2305     case LayoutNavEntry::Namespaces:         return documentedNamespaces>0 && showNamespaces;
2306     case LayoutNavEntry::NamespaceList:      return documentedNamespaces>0 && showNamespaces;
2307     case LayoutNavEntry::NamespaceMembers:   return documentedNamespaceMembers[NMHL_All]>0;
2308     case LayoutNavEntry::Concepts:           return documentedConcepts>0;
2309     case LayoutNavEntry::Classes:            return annotatedClasses>0;
2310     case LayoutNavEntry::ClassList:          return annotatedClasses>0;
2311     case LayoutNavEntry::ClassIndex:         return annotatedClasses>0;
2312     case LayoutNavEntry::ClassHierarchy:     return hierarchyClasses>0;
2313     case LayoutNavEntry::ClassMembers:       return documentedClassMembers[CMHL_All]>0;
2314     case LayoutNavEntry::Files:              return documentedFiles>0;
2315     case LayoutNavEntry::FileList:           return documentedFiles>0;
2316     case LayoutNavEntry::FileGlobals:        return documentedFileMembers[FMHL_All]>0;
2317     case LayoutNavEntry::Examples:           return !Doxygen::exampleLinkedMap->empty();
2318     case LayoutNavEntry::Interfaces:         return annotatedInterfaces>0;
2319     case LayoutNavEntry::InterfaceList:      return annotatedInterfaces>0;
2320     case LayoutNavEntry::InterfaceIndex:     return annotatedInterfaces>0;
2321     case LayoutNavEntry::InterfaceHierarchy: return hierarchyInterfaces>0;
2322     case LayoutNavEntry::Structs:            return annotatedStructs>0;
2323     case LayoutNavEntry::StructList:         return annotatedStructs>0;
2324     case LayoutNavEntry::StructIndex:        return annotatedStructs>0;
2325     case LayoutNavEntry::Exceptions:         return annotatedExceptions>0;
2326     case LayoutNavEntry::ExceptionList:      return annotatedExceptions>0;
2327     case LayoutNavEntry::ExceptionIndex:     return annotatedExceptions>0;
2328     case LayoutNavEntry::ExceptionHierarchy: return hierarchyExceptions>0;
2329     case LayoutNavEntry::None:             // should never happen, means not properly initialized
2330       assert(kind != LayoutNavEntry::None);
2331       return FALSE;
2332   }
2333   return FALSE;
2334 }
2335 
renderQuickLinksAsTree(TextStream & t,const QCString & relPath,LayoutNavEntry * root)2336 static void renderQuickLinksAsTree(TextStream &t,const QCString &relPath,LayoutNavEntry *root)
2337 
2338 {
2339   int count=0;
2340   for (const auto &entry : root->children())
2341   {
2342     if (entry->visible() && quickLinkVisible(entry->kind())) count++;
2343   }
2344   if (count>0) // at least one item is visible
2345   {
2346     startQuickIndexList(t,FALSE);
2347     for (const auto &entry : root->children())
2348     {
2349       if (entry->visible() && quickLinkVisible(entry->kind()))
2350       {
2351         QCString url = entry->url();
2352         t << "<li><a href=\"" << relPath << url << "\"><span>";
2353         t << fixSpaces(entry->title());
2354         t << "</span></a>\n";
2355         // recursive into child list
2356         renderQuickLinksAsTree(t,relPath,entry.get());
2357         t << "</li>";
2358       }
2359     }
2360     endQuickIndexList(t,FALSE);
2361   }
2362 }
2363 
2364 
renderQuickLinksAsTabs(TextStream & t,const QCString & relPath,LayoutNavEntry * hlEntry,LayoutNavEntry::Kind kind,bool highlightParent,bool highlightSearch)2365 static void renderQuickLinksAsTabs(TextStream &t,const QCString &relPath,
2366                              LayoutNavEntry *hlEntry,LayoutNavEntry::Kind kind,
2367                              bool highlightParent,bool highlightSearch)
2368 {
2369   if (hlEntry->parent()) // first draw the tabs for the parent of hlEntry
2370   {
2371     renderQuickLinksAsTabs(t,relPath,hlEntry->parent(),kind,highlightParent,highlightSearch);
2372   }
2373   if (hlEntry->parent() && !hlEntry->parent()->children().empty()) // draw tabs for row containing hlEntry
2374   {
2375     bool topLevel = hlEntry->parent()->parent()==0;
2376     int count=0;
2377     for (const auto &entry : hlEntry->parent()->children())
2378     {
2379       if (entry->visible() && quickLinkVisible(entry->kind())) count++;
2380     }
2381     if (count>0) // at least one item is visible
2382     {
2383       startQuickIndexList(t,TRUE,topLevel);
2384       for (const auto &entry : hlEntry->parent()->children())
2385       {
2386         if (entry->visible() && quickLinkVisible(entry->kind()))
2387         {
2388           QCString url = entry->url();
2389           startQuickIndexItem(t,url,
2390               entry.get()==hlEntry  &&
2391               (!entry->children().empty() ||
2392                (entry->kind()==kind && !highlightParent)
2393               ),
2394               TRUE,relPath);
2395           t << fixSpaces(entry->title());
2396           endQuickIndexItem(t,url);
2397         }
2398       }
2399       if (hlEntry->parent()==LayoutDocManager::instance().rootNavEntry()) // first row is special as it contains the search box
2400       {
2401         bool searchEngine      = Config_getBool(SEARCHENGINE);
2402         bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
2403         if (searchEngine)
2404         {
2405           t << "      <li>\n";
2406           if (!serverBasedSearch) // pure client side search
2407           {
2408             writeClientSearchBox(t,relPath);
2409             t << "      </li>\n";
2410           }
2411           else // server based search
2412           {
2413             writeServerSearchBox(t,relPath,highlightSearch);
2414             if (!highlightSearch)
2415             {
2416               t << "      </li>\n";
2417             }
2418           }
2419         }
2420         if (!highlightSearch) // on the search page the index will be ended by the
2421           // page itself
2422         {
2423           endQuickIndexList(t,TRUE);
2424         }
2425       }
2426       else // normal case for other rows than first one
2427       {
2428         endQuickIndexList(t,TRUE);
2429       }
2430     }
2431   }
2432 }
2433 
writeDefaultQuickLinks(TextStream & t,bool compact,HighlightedItem hli,const QCString & file,const QCString & relPath)2434 static void writeDefaultQuickLinks(TextStream &t,bool compact,
2435                                    HighlightedItem hli,
2436                                    const QCString &file,
2437                                    const QCString &relPath)
2438 {
2439   bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH);
2440   bool searchEngine = Config_getBool(SEARCHENGINE);
2441   bool externalSearch = Config_getBool(EXTERNAL_SEARCH);
2442   LayoutNavEntry *root = LayoutDocManager::instance().rootNavEntry();
2443   LayoutNavEntry::Kind kind = (LayoutNavEntry::Kind)-1;
2444   LayoutNavEntry::Kind altKind = (LayoutNavEntry::Kind)-1; // fall back for the old layout file
2445   bool highlightParent=FALSE;
2446   switch (hli) // map HLI enums to LayoutNavEntry::Kind enums
2447   {
2448     case HLI_Main:             kind = LayoutNavEntry::MainPage;         break;
2449     case HLI_Modules:          kind = LayoutNavEntry::Modules;          break;
2450     //case HLI_Directories:      kind = LayoutNavEntry::Dirs;             break;
2451     case HLI_Namespaces:       kind = LayoutNavEntry::NamespaceList;    altKind = LayoutNavEntry::Namespaces;  break;
2452     case HLI_ClassHierarchy:   kind = LayoutNavEntry::ClassHierarchy;   break;
2453     case HLI_InterfaceHierarchy: kind = LayoutNavEntry::InterfaceHierarchy;   break;
2454     case HLI_ExceptionHierarchy: kind = LayoutNavEntry::ExceptionHierarchy;   break;
2455     case HLI_Classes:          kind = LayoutNavEntry::ClassIndex;       altKind = LayoutNavEntry::Classes;     break;
2456     case HLI_Concepts:         kind = LayoutNavEntry::Concepts;         break;
2457     case HLI_Interfaces:       kind = LayoutNavEntry::InterfaceIndex;   altKind = LayoutNavEntry::Interfaces;  break;
2458     case HLI_Structs:          kind = LayoutNavEntry::StructIndex;      altKind = LayoutNavEntry::Structs;     break;
2459     case HLI_Exceptions:       kind = LayoutNavEntry::ExceptionIndex;   altKind = LayoutNavEntry::Exceptions;  break;
2460     case HLI_AnnotatedClasses: kind = LayoutNavEntry::ClassList;        altKind = LayoutNavEntry::Classes;     break;
2461     case HLI_AnnotatedInterfaces: kind = LayoutNavEntry::InterfaceList; altKind = LayoutNavEntry::Interfaces;  break;
2462     case HLI_AnnotatedStructs: kind = LayoutNavEntry::StructList;       altKind = LayoutNavEntry::Structs;     break;
2463     case HLI_AnnotatedExceptions: kind = LayoutNavEntry::ExceptionList; altKind = LayoutNavEntry::Exceptions;  break;
2464     case HLI_Files:            kind = LayoutNavEntry::FileList;         altKind = LayoutNavEntry::Files;       break;
2465     case HLI_NamespaceMembers: kind = LayoutNavEntry::NamespaceMembers; break;
2466     case HLI_Functions:        kind = LayoutNavEntry::ClassMembers;     break;
2467     case HLI_Globals:          kind = LayoutNavEntry::FileGlobals;      break;
2468     case HLI_Pages:            kind = LayoutNavEntry::Pages;            break;
2469     case HLI_Examples:         kind = LayoutNavEntry::Examples;         break;
2470     case HLI_UserGroup:        kind = LayoutNavEntry::UserGroup;        break;
2471     case HLI_ClassVisible:     kind = LayoutNavEntry::ClassList;        altKind = LayoutNavEntry::Classes;
2472                                highlightParent = TRUE; break;
2473     case HLI_ConceptVisible:   kind = LayoutNavEntry::Concepts;
2474                                highlightParent = TRUE; break;
2475     case HLI_InterfaceVisible: kind = LayoutNavEntry::InterfaceList;    altKind = LayoutNavEntry::Interfaces;
2476                                highlightParent = TRUE; break;
2477     case HLI_StructVisible:    kind = LayoutNavEntry::StructList;       altKind = LayoutNavEntry::Structs;
2478                                highlightParent = TRUE; break;
2479     case HLI_ExceptionVisible: kind = LayoutNavEntry::ExceptionList;    altKind = LayoutNavEntry::Exceptions;
2480                                highlightParent = TRUE; break;
2481     case HLI_NamespaceVisible: kind = LayoutNavEntry::NamespaceList;    altKind = LayoutNavEntry::Namespaces;
2482                                highlightParent = TRUE; break;
2483     case HLI_FileVisible:      kind = LayoutNavEntry::FileList;         altKind = LayoutNavEntry::Files;
2484                                highlightParent = TRUE; break;
2485     case HLI_None:   break;
2486     case HLI_Search: break;
2487   }
2488 
2489   if (compact && Config_getBool(HTML_DYNAMIC_MENUS))
2490   {
2491     QCString searchPage;
2492     if (externalSearch)
2493     {
2494       searchPage = "search" + Doxygen::htmlFileExtension;
2495     }
2496     else
2497     {
2498       searchPage = "search.php";
2499     }
2500     t << "<script type=\"text/javascript\" src=\"" << relPath << "menudata.js\"></script>\n";
2501     t << "<script type=\"text/javascript\" src=\"" << relPath << "menu.js\"></script>\n";
2502     t << "<script type=\"text/javascript\">\n";
2503     t << "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */\n";
2504     t << "$(function() {\n";
2505     t << "  initMenu('" << relPath << "',"
2506       << (searchEngine?"true":"false") << ","
2507       << (serverBasedSearch?"true":"false") << ",'"
2508       << searchPage << "','"
2509       << theTranslator->trSearch() << "');\n";
2510     if (Config_getBool(SEARCHENGINE))
2511     {
2512       if (!serverBasedSearch)
2513       {
2514         t << "  $(document).ready(function() { init_search(); });\n";
2515       }
2516       else
2517       {
2518         t << "  $(document).ready(function() {\n"
2519           << "    if ($('.searchresults').length > 0) { searchBox.DOMSearchField().focus(); }\n"
2520           << "  });\n";
2521       }
2522     }
2523     t << "});\n";
2524     t << "/* @license-end */\n";
2525     t << "</script>\n";
2526     t << "<div id=\"main-nav\"></div>\n";
2527   }
2528   else if (compact) // && !Config_getBool(HTML_DYNAMIC_MENUS)
2529   {
2530     // find highlighted index item
2531     LayoutNavEntry *hlEntry = root->find(kind,kind==LayoutNavEntry::UserGroup ? file : QCString());
2532     if (!hlEntry && altKind!=(LayoutNavEntry::Kind)-1) { hlEntry=root->find(altKind); kind=altKind; }
2533     if (!hlEntry) // highlighted item not found in the index! -> just show the level 1 index...
2534     {
2535       highlightParent=TRUE;
2536       hlEntry = root->children().front().get();
2537       if (hlEntry==0)
2538       {
2539         return; // argl, empty index!
2540       }
2541     }
2542     if (kind==LayoutNavEntry::UserGroup)
2543     {
2544       LayoutNavEntry *e = hlEntry->children().front().get();
2545       if (e)
2546       {
2547         hlEntry = e;
2548       }
2549     }
2550     renderQuickLinksAsTabs(t,relPath,hlEntry,kind,highlightParent,hli==HLI_Search);
2551   }
2552   else
2553   {
2554     renderQuickLinksAsTree(t,relPath,root);
2555   }
2556 }
2557 
endQuickIndices()2558 void HtmlGenerator::endQuickIndices()
2559 {
2560   m_t << "</div><!-- top -->\n";
2561 }
2562 
writeSplitBarAsString(const QCString & name,const QCString & relpath)2563 QCString HtmlGenerator::writeSplitBarAsString(const QCString &name,const QCString &relpath)
2564 {
2565   bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
2566   QCString result;
2567   // write split bar
2568   if (generateTreeView)
2569   {
2570     if (!Config_getBool(DISABLE_INDEX) || !Config_getBool(FULL_SIDEBAR))
2571     {
2572       result += QCString(
2573         "<div id=\"side-nav\" class=\"ui-resizable side-nav-resizable\">\n");
2574     }
2575     result+= QCString(
2576      "  <div id=\"nav-tree\">\n"
2577      "    <div id=\"nav-tree-contents\">\n"
2578      "      <div id=\"nav-sync\" class=\"sync\"></div>\n"
2579      "    </div>\n"
2580      "  </div>\n"
2581      "  <div id=\"splitbar\" style=\"-moz-user-select:none;\" \n"
2582      "       class=\"ui-resizable-handle\">\n"
2583      "  </div>\n"
2584      "</div>\n"
2585      "<script type=\"text/javascript\">\n"
2586      "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */\n"
2587      "$(document).ready(function(){initNavTree('") +
2588      QCString(addHtmlExtensionIfMissing(name)) +
2589      QCString("','") + relpath +
2590      QCString("'); initResizable(); });\n"
2591      "/* @license-end */\n"
2592      "</script>\n"
2593      "<div id=\"doc-content\">\n");
2594   }
2595   return result;
2596 }
2597 
writeSplitBar(const QCString & name)2598 void HtmlGenerator::writeSplitBar(const QCString &name)
2599 {
2600   m_t << writeSplitBarAsString(name,m_relPath);
2601 }
2602 
writeNavigationPath(const QCString & s)2603 void HtmlGenerator::writeNavigationPath(const QCString &s)
2604 {
2605   m_t << substitute(s,"$relpath^",m_relPath);
2606 }
2607 
startContents()2608 void HtmlGenerator::startContents()
2609 {
2610   m_t << "<div class=\"contents\">\n";
2611 }
2612 
endContents()2613 void HtmlGenerator::endContents()
2614 {
2615   m_t << "</div><!-- contents -->\n";
2616 }
2617 
startPageDoc(const QCString & pageTitle)2618 void HtmlGenerator::startPageDoc(const QCString &pageTitle)
2619 {
2620   m_t << "<div>";
2621 }
2622 
endPageDoc()2623 void HtmlGenerator::endPageDoc()
2624 {
2625   m_t << "</div><!-- PageDoc -->\n";
2626 }
2627 
writeQuickLinks(bool compact,HighlightedItem hli,const QCString & file)2628 void HtmlGenerator::writeQuickLinks(bool compact,HighlightedItem hli,const QCString &file)
2629 {
2630   writeDefaultQuickLinks(m_t,compact,hli,file,m_relPath);
2631 }
2632 
2633 // PHP based search script
writeSearchPage()2634 void HtmlGenerator::writeSearchPage()
2635 {
2636   bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
2637   bool disableIndex = Config_getBool(DISABLE_INDEX);
2638   QCString projectName = Config_getString(PROJECT_NAME);
2639   QCString htmlOutput = Config_getString(HTML_OUTPUT);
2640 
2641   // OPENSEARCH_PROVIDER {
2642   QCString configFileName = htmlOutput+"/search_config.php";
2643   std::ofstream f(configFileName.str(),std::ofstream::out | std::ofstream::binary);
2644   if (f.is_open())
2645   {
2646     TextStream t(&f);
2647     t << "<?php\n\n";
2648     t << "$config = array(\n";
2649     t << "  'PROJECT_NAME' => \"" << convertToHtml(projectName) << "\",\n";
2650     t << "  'GENERATE_TREEVIEW' => " << (generateTreeView?"true":"false") << ",\n";
2651     t << "  'DISABLE_INDEX' => " << (disableIndex?"true":"false") << ",\n";
2652     t << ");\n\n";
2653     t << "$translator = array(\n";
2654     t << "  'search_results_title' => \"" << theTranslator->trSearchResultsTitle() << "\",\n";
2655     t << "  'search_results' => array(\n";
2656     t << "    0 => \"" << theTranslator->trSearchResults(0) << "\",\n";
2657     t << "    1 => \"" << theTranslator->trSearchResults(1) << "\",\n";
2658     t << "    2 => \"" << substitute(theTranslator->trSearchResults(2), "$", "\\$") << "\",\n";
2659     t << "  ),\n";
2660     t << "  'search_matches' => \"" << theTranslator->trSearchMatches() << "\",\n";
2661     t << "  'search' => \"" << theTranslator->trSearch() << "\",\n";
2662     t << "  'split_bar' => \"" << substitute(substitute(writeSplitBarAsString("search",""), "\"","\\\""), "\n","\\n") << "\",\n";
2663     t << "  'logo' => \"" << substitute(substitute(writeLogoAsString(""), "\"","\\\""), "\n","\\n") << "\",\n";
2664     t << ");\n\n";
2665     t << "?>\n";
2666   }
2667   f.close();
2668 
2669   ResourceMgr::instance().copyResource("search_functions.php",htmlOutput);
2670   ResourceMgr::instance().copyResource("search_opensearch.php",htmlOutput);
2671   // OPENSEARCH_PROVIDER }
2672 
2673   QCString fileName = htmlOutput+"/search.php";
2674   f.open(fileName.str(),std::ofstream::out | std::ofstream::binary);
2675   if (f.is_open())
2676   {
2677     TextStream t(&f);
2678     t << substituteHtmlKeywords(g_header,"Search","");
2679 
2680     t << "<!-- " << theTranslator->trGeneratedBy() << " Doxygen "
2681       << getDoxygenVersion() << " -->\n";
2682     t << "<script type=\"text/javascript\">\n";
2683 		t << "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */\n";
2684 		t << "var searchBox = new SearchBox(\"searchBox\", \""
2685       << "search\",'" << theTranslator->trSearch() << "','" << Doxygen::htmlFileExtension << "');\n";
2686 		t << "/* @license-end */\n";
2687     t << "</script>\n";
2688     if (!Config_getBool(DISABLE_INDEX))
2689     {
2690       writeDefaultQuickLinks(t,TRUE,HLI_Search,QCString(),QCString());
2691     }
2692     else
2693     {
2694       t << "</div>\n";
2695     }
2696 
2697     t << "<?php\n";
2698     t << "require_once \"search_functions.php\";\n";
2699     t << "main();\n";
2700     t << "?>\n";
2701 
2702     // Write empty navigation path, to make footer connect properly
2703     if (generateTreeView)
2704     {
2705       t << "</div><!-- doc-content -->\n";
2706     }
2707 
2708     writePageFooter(t,"Search","","");
2709   }
2710   f.close();
2711 
2712   QCString scriptName = htmlOutput+"/search/search.js";
2713   f.open(scriptName.str(),std::ofstream::out | std::ofstream::binary);
2714   if (f.is_open())
2715   {
2716     TextStream t(&f);
2717     t << ResourceMgr::instance().getAsString("extsearch.js");
2718   }
2719   else
2720   {
2721      err("Failed to open file '%s' for writing...\n",qPrint(scriptName));
2722   }
2723 }
2724 
writeExternalSearchPage()2725 void HtmlGenerator::writeExternalSearchPage()
2726 {
2727   bool generateTreeView = Config_getBool(GENERATE_TREEVIEW);
2728   QCString dname = Config_getString(HTML_OUTPUT);
2729   QCString fileName = dname+"/search"+Doxygen::htmlFileExtension;
2730   std::ofstream f(fileName.str(),std::ofstream::out | std::ofstream::binary);
2731   if (f.is_open())
2732   {
2733     TextStream t(&f);
2734     t << substituteHtmlKeywords(g_header,"Search","");
2735 
2736     t << "<!-- " << theTranslator->trGeneratedBy() << " Doxygen "
2737       << getDoxygenVersion() << " -->\n";
2738     t << "<script type=\"text/javascript\">\n";
2739 		t << "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */\n";
2740 		t << "var searchBox = new SearchBox(\"searchBox\", \""
2741       << "search\",'" << theTranslator->trSearch() << "','" << Doxygen::htmlFileExtension << "');\n";
2742 		t << "/* @license-end */\n";
2743     t << "</script>\n";
2744     if (!Config_getBool(DISABLE_INDEX))
2745     {
2746       writeDefaultQuickLinks(t,TRUE,HLI_Search,QCString(),QCString());
2747       t << "            <input type=\"text\" id=\"MSearchField\" name=\"query\" value=\"\" size=\"20\" accesskey=\"S\" onfocus=\"searchBox.OnSearchFieldFocus(true)\" onblur=\"searchBox.OnSearchFieldFocus(false)\"/>\n";
2748       t << "            </form>\n";
2749       t << "          </div><div class=\"right\"></div>\n";
2750       t << "        </div>\n";
2751       t << "      </li>\n";
2752       t << "    </ul>\n";
2753       t << "  </div>\n";
2754       t << "</div>\n";
2755     }
2756     else
2757     {
2758       t << "</div>\n";
2759     }
2760     t << writeSplitBarAsString("search","");
2761     t << "<div class=\"header\">\n";
2762     t << "  <div class=\"headertitle\">\n";
2763     t << "    <div class=\"title\">" << theTranslator->trSearchResultsTitle() << "</div>\n";
2764     t << "  </div>\n";
2765     t << "</div>\n";
2766     t << "<div class=\"contents\">\n";
2767 
2768     t << "<div id=\"searchresults\"></div>\n";
2769     t << "</div>\n";
2770 
2771     if (generateTreeView)
2772     {
2773       t << "</div><!-- doc-content -->\n";
2774     }
2775 
2776     writePageFooter(t,"Search","","");
2777 
2778   }
2779   f.close();
2780 
2781   QCString scriptName = dname+"/search/search.js";
2782   f.open(scriptName.str(),std::ofstream::out | std::ofstream::binary);
2783   if (f.is_open())
2784   {
2785     TextStream t(&f);
2786     t << "var searchResultsText=["
2787       << "\"" << theTranslator->trSearchResults(0) << "\","
2788       << "\"" << theTranslator->trSearchResults(1) << "\","
2789       << "\"" << theTranslator->trSearchResults(2) << "\"];\n";
2790     t << "var serverUrl=\"" << Config_getString(SEARCHENGINE_URL) << "\";\n";
2791     t << "var tagMap = {\n";
2792     bool first=TRUE;
2793     // add search mappings
2794     const StringVector &extraSearchMappings = Config_getList(EXTRA_SEARCH_MAPPINGS);
2795     for (const auto &ml : extraSearchMappings)
2796     {
2797       QCString mapLine = ml.c_str();
2798       int eqPos = mapLine.find('=');
2799       if (eqPos!=-1) // tag command contains a destination
2800       {
2801         QCString tagName = mapLine.left(eqPos).stripWhiteSpace();
2802         QCString destName = mapLine.right(mapLine.length()-eqPos-1).stripWhiteSpace();
2803         if (!tagName.isEmpty())
2804         {
2805           if (!first) t << ",\n";
2806           t << "  \"" << tagName << "\": \"" << destName << "\"";
2807           first=FALSE;
2808         }
2809       }
2810     }
2811     if (!first) t << "\n";
2812     t << "};\n\n";
2813     t << ResourceMgr::instance().getAsString("extsearch.js");
2814     t << "\n";
2815     t << "$(document).ready(function() {\n";
2816     t << "  var query = trim(getURLParameter('query'));\n";
2817     t << "  if (query) {\n";
2818     t << "    searchFor(query,0,20);\n";
2819     t << "  } else {\n";
2820     t << "    var results = $('#results');\n";
2821     t << "    results.html('<p>" << theTranslator->trSearchResults(0) << "</p>');\n";
2822     t << "  }\n";
2823     t << "});\n";
2824   }
2825   else
2826   {
2827      err("Failed to open file '%s' for writing...\n",qPrint(scriptName));
2828   }
2829 }
2830 
startConstraintList(const QCString & header)2831 void HtmlGenerator::startConstraintList(const QCString &header)
2832 {
2833   m_t << "<div class=\"typeconstraint\">\n";
2834   m_t << "<dl><dt><b>" << header << "</b></dt><dd>\n";
2835   m_t << "<table border=\"0\" cellspacing=\"2\" cellpadding=\"0\">\n";
2836 }
2837 
startConstraintParam()2838 void HtmlGenerator::startConstraintParam()
2839 {
2840   m_t << "<tr><td valign=\"top\"><em>";
2841 }
2842 
endConstraintParam()2843 void HtmlGenerator::endConstraintParam()
2844 {
2845   m_t << "</em></td>";
2846 }
2847 
startConstraintType()2848 void HtmlGenerator::startConstraintType()
2849 {
2850   m_t << "<td>&#160;:</td><td valign=\"top\"><em>";
2851 }
2852 
endConstraintType()2853 void HtmlGenerator::endConstraintType()
2854 {
2855   m_t << "</em></td>";
2856 }
2857 
startConstraintDocs()2858 void HtmlGenerator::startConstraintDocs()
2859 {
2860   m_t << "<td>&#160;";
2861 }
2862 
endConstraintDocs()2863 void HtmlGenerator::endConstraintDocs()
2864 {
2865   m_t << "</td></tr>\n";
2866 }
2867 
endConstraintList()2868 void HtmlGenerator::endConstraintList()
2869 {
2870   m_t << "</table>\n";
2871   m_t << "</dd>\n";
2872   m_t << "</dl>\n";
2873   m_t << "</div>\n";
2874 }
2875 
lineBreak(const QCString & style)2876 void HtmlGenerator::lineBreak(const QCString &style)
2877 {
2878   if (!style.isEmpty())
2879   {
2880     m_t << "<br class=\"" << style << "\" />\n";
2881   }
2882   else
2883   {
2884     m_t << "<br />\n";
2885   }
2886 }
2887 
startHeaderSection()2888 void HtmlGenerator::startHeaderSection()
2889 {
2890   m_t << "<div class=\"header\">\n";
2891 }
2892 
startTitleHead(const QCString &)2893 void HtmlGenerator::startTitleHead(const QCString &)
2894 {
2895   m_t << "  <div class=\"headertitle\">";
2896   startTitle();
2897 }
2898 
endTitleHead(const QCString &,const QCString &)2899 void HtmlGenerator::endTitleHead(const QCString &,const QCString &)
2900 {
2901   endTitle();
2902   m_t << "</div>\n";
2903 }
2904 
endHeaderSection()2905 void HtmlGenerator::endHeaderSection()
2906 {
2907   m_t << "</div><!--header-->\n";
2908 }
2909 
startInlineHeader()2910 void HtmlGenerator::startInlineHeader()
2911 {
2912   if (m_emptySection)
2913   {
2914     m_t << "<table class=\"memberdecls\">\n";
2915     m_emptySection=FALSE;
2916   }
2917   m_t << "<tr><td colspan=\"2\"><h3>";
2918 }
2919 
endInlineHeader()2920 void HtmlGenerator::endInlineHeader()
2921 {
2922   m_t << "</h3></td></tr>\n";
2923 }
2924 
startMemberDocSimple(bool isEnum)2925 void HtmlGenerator::startMemberDocSimple(bool isEnum)
2926 {
2927   DBG_HTML(m_t << "<!-- startMemberDocSimple -->\n";)
2928   m_t << "<table class=\"fieldtable\">\n";
2929   m_t << "<tr><th colspan=\"" << (isEnum?"2":"3") << "\">";
2930   m_t << (isEnum? theTranslator->trEnumerationValues() :
2931        theTranslator->trCompoundMembers()) << "</th></tr>\n";
2932 }
2933 
endMemberDocSimple(bool)2934 void HtmlGenerator::endMemberDocSimple(bool)
2935 {
2936   DBG_HTML(m_t << "<!-- endMemberDocSimple -->\n";)
2937   m_t << "</table>\n";
2938 }
2939 
startInlineMemberType()2940 void HtmlGenerator::startInlineMemberType()
2941 {
2942   DBG_HTML(m_t << "<!-- startInlineMemberType -->\n";)
2943   m_t << "<tr><td class=\"fieldtype\">\n";
2944 }
2945 
endInlineMemberType()2946 void HtmlGenerator::endInlineMemberType()
2947 {
2948   DBG_HTML(m_t << "<!-- endInlineMemberType -->\n";)
2949   m_t << "</td>\n";
2950 }
2951 
startInlineMemberName()2952 void HtmlGenerator::startInlineMemberName()
2953 {
2954   DBG_HTML(m_t << "<!-- startInlineMemberName -->\n";)
2955   m_t << "<td class=\"fieldname\">\n";
2956 }
2957 
endInlineMemberName()2958 void HtmlGenerator::endInlineMemberName()
2959 {
2960   DBG_HTML(m_t << "<!-- endInlineMemberName -->\n";)
2961   m_t << "</td>\n";
2962 }
2963 
startInlineMemberDoc()2964 void HtmlGenerator::startInlineMemberDoc()
2965 {
2966   DBG_HTML(m_t << "<!-- startInlineMemberDoc -->\n";)
2967   m_t << "<td class=\"fielddoc\">\n";
2968 }
2969 
endInlineMemberDoc()2970 void HtmlGenerator::endInlineMemberDoc()
2971 {
2972   DBG_HTML(m_t << "<!-- endInlineMemberDoc -->\n";)
2973   m_t << "</td></tr>\n";
2974 }
2975 
startLabels()2976 void HtmlGenerator::startLabels()
2977 {
2978   DBG_HTML(m_t << "<!-- startLabels -->\n";)
2979   m_t << "<span class=\"mlabels\">";
2980 }
2981 
writeLabel(const QCString & l,bool)2982 void HtmlGenerator::writeLabel(const QCString &l,bool /*isLast*/)
2983 {
2984   DBG_HTML(m_t << "<!-- writeLabel(" << l << ") -->\n";)
2985   //m_t << "<tt>[" << l << "]</tt>";
2986   //if (!isLast) m_t << ", ";
2987   m_t << "<span class=\"mlabel\">" << l << "</span>";
2988 }
2989 
endLabels()2990 void HtmlGenerator::endLabels()
2991 {
2992   DBG_HTML(m_t << "<!-- endLabels -->\n";)
2993   m_t << "</span>";
2994 }
2995 
writeInheritedSectionTitle(const QCString & id,const QCString & ref,const QCString & file,const QCString & anchor,const QCString & title,const QCString & name)2996 void HtmlGenerator::writeInheritedSectionTitle(
2997                   const QCString &id,    const QCString &ref,
2998                   const QCString &file,  const QCString &anchor,
2999                   const QCString &title, const QCString &name)
3000 {
3001   DBG_HTML(m_t << "<!-- writeInheritedSectionTitle -->\n";)
3002   QCString a = anchor;
3003   if (!a.isEmpty()) a.prepend("#");
3004   QCString classLink = QCString("<a class=\"el\" ");
3005   if (!ref.isEmpty())
3006   {
3007     classLink+= externalLinkTarget();
3008     classLink += " href=\"";
3009     classLink+= externalRef(m_relPath,ref,TRUE);
3010   }
3011   else
3012   {
3013     classLink += "href=\"";
3014     classLink+=m_relPath;
3015   }
3016   classLink=classLink+addHtmlExtensionIfMissing(file)+a;
3017   classLink+=QCString("\">")+convertToHtml(name,FALSE)+"</a>";
3018   m_t << "<tr class=\"inherit_header " << id << "\">"
3019     << "<td colspan=\"2\" onclick=\"javascript:toggleInherit('" << id << "')\">"
3020     << "<img src=\"" << m_relPath << "closed.png\" alt=\"-\"/>&#160;"
3021     << theTranslator->trInheritedFrom(convertToHtml(title,FALSE),classLink)
3022     << "</td></tr>\n";
3023 }
3024 
writeSummaryLink(const QCString & file,const QCString & anchor,const QCString & title,bool first)3025 void HtmlGenerator::writeSummaryLink(const QCString &file,const QCString &anchor,const QCString &title,bool first)
3026 {
3027   if (first)
3028   {
3029     m_t << "  <div class=\"summary\">\n";
3030   }
3031   else
3032   {
3033     m_t << " &#124;\n";
3034   }
3035   m_t << "<a href=\"";
3036   if (!file.isEmpty())
3037   {
3038     m_t << m_relPath << addHtmlExtensionIfMissing(file);
3039   }
3040   else if (!anchor.isEmpty())
3041   {
3042     m_t << "#";
3043     m_t << anchor;
3044   }
3045   m_t << "\">";
3046   m_t << title;
3047   m_t << "</a>";
3048 }
3049 
endMemberDeclaration(const QCString & anchor,const QCString & inheritId)3050 void HtmlGenerator::endMemberDeclaration(const QCString &anchor,const QCString &inheritId)
3051 {
3052   m_t << "<tr class=\"separator:" << anchor;
3053   if (!inheritId.isEmpty())
3054   {
3055     m_t << " inherit " << inheritId;
3056   }
3057   m_t << "\"><td class=\"memSeparator\" colspan=\"2\">&#160;</td></tr>\n";
3058 }
3059 
setCurrentDoc(const Definition * context,const QCString & anchor,bool isSourceFile)3060 void HtmlGenerator::setCurrentDoc(const Definition *context,const QCString &anchor,bool isSourceFile)
3061 {
3062   if (Doxygen::searchIndex)
3063   {
3064     Doxygen::searchIndex->setCurrentDoc(context,anchor,isSourceFile);
3065   }
3066 }
3067 
addWord(const QCString & word,bool hiPriority)3068 void HtmlGenerator::addWord(const QCString &word,bool hiPriority)
3069 {
3070   if (Doxygen::searchIndex)
3071   {
3072     Doxygen::searchIndex->addWord(word,hiPriority);
3073   }
3074 }
3075 
getMathJaxMacros()3076 QCString HtmlGenerator::getMathJaxMacros()
3077 {
3078   return getConvertLatexMacro();
3079 }
3080