1 /******************************************************************************
6 * Copyright (C) 1997-2014 by Dimitri van Heesch.
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation under the terms of the GNU General Public License is hereby
10 * granted. No representations are made about the suitability of this software
11 * for any purpose. It is provided "as is" without express or implied warranty.
12 * See the GNU General Public License for more details.
14 * Documents produced by Doxygen are derivative works derived from the
15 * input used in their production; they are not affected by this license.
20 #include "htmldocvisitor.h"
21 #include "docparser.h"
24 #include "outputgen.h"
29 #include "parserintf.h"
33 #include "vhdldocgen.h"
35 #include "memberdef.h"
36 #include "htmlentity.h"
38 static const int NUM_HTML_LIST_TYPES = 4;
39 static const char types[][NUM_HTML_LIST_TYPES] = {"1", "a", "i", "A"};
41 static QCString convertIndexWordToAnchor(const QString &word)
43 static char hex[] = "0123456789abcdef";
45 const char *str = word.data();
51 if ((c >= 'a' && c <= 'z') || // ALPHA
52 (c >= 'A' && c <= 'A') || // ALPHA
53 (c >= '0' && c <= '9') || // DIGIT
66 enc[1] = hex[(c & 0xf0) >> 4];
67 enc[2] = hex[c & 0xf];
76 static bool mustBeOutsideParagraph(DocNode *n)
81 case DocNode::Kind_HtmlList:
82 case DocNode::Kind_SimpleList:
83 case DocNode::Kind_AutoList:
85 case DocNode::Kind_SimpleSect:
86 case DocNode::Kind_ParamSect:
87 case DocNode::Kind_HtmlDescList:
88 case DocNode::Kind_XRefItem:
90 case DocNode::Kind_HtmlTable:
92 case DocNode::Kind_Section:
93 case DocNode::Kind_HtmlHeader:
95 case DocNode::Kind_Internal:
97 case DocNode::Kind_Include:
98 case DocNode::Kind_Image:
99 case DocNode::Kind_SecRefList:
101 case DocNode::Kind_HorRuler:
102 /* CopyDoc gets paragraph markers from the wrapping DocPara node,
103 * but needs to insert them for all documentation being copied to
104 * preserve formatting.
106 case DocNode::Kind_Copy:
108 case DocNode::Kind_HtmlBlockQuote:
110 case DocNode::Kind_ParBlock:
112 case DocNode::Kind_Verbatim:
114 DocVerbatim *dv = (DocVerbatim*)n;
115 return dv->type()!=DocVerbatim::HtmlOnly || dv->isBlock();
117 case DocNode::Kind_StyleChange:
118 return ((DocStyleChange*)n)->style()==DocStyleChange::Preformatted ||
119 ((DocStyleChange*)n)->style()==DocStyleChange::Div ||
120 ((DocStyleChange*)n)->style()==DocStyleChange::Center;
121 case DocNode::Kind_Formula:
122 return !((DocFormula*)n)->isInline();
129 static QString htmlAttribsToString(const HtmlAttribList &attribs)
132 HtmlAttribListIterator li(attribs);
134 for (li.toFirst();(att=li.current());++li)
136 if (!att->value.isEmpty()) // ignore attribute without values as they
137 // are not XHTML compliant
141 result+="=\""+convertToXML(att->value)+"\"";
147 //-------------------------------------------------------------------------
149 HtmlDocVisitor::HtmlDocVisitor(FTextStream &t,CodeOutputInterface &ci,
151 : DocVisitor(DocVisitor_Html), m_t(t), m_ci(ci), m_insidePre(FALSE),
152 m_hide(FALSE), m_ctx(ctx)
154 if (ctx) m_langExt=ctx->getDefFileExtension();
157 //--------------------------------------
158 // visitor functions for leaf nodes
159 //--------------------------------------
161 void HtmlDocVisitor::visit(DocWord *w)
163 //printf("word: %s\n",w->word().data());
168 void HtmlDocVisitor::visit(DocLinkedWord *w)
171 //printf("linked word: %s\n",w->word().data());
172 startLink(w->ref(),w->file(),w->relPath(),w->anchor(),w->tooltip());
177 void HtmlDocVisitor::visit(DocWhiteSpace *w)
190 void HtmlDocVisitor::visit(DocSymbol *s)
193 const char *res = HtmlEntityMapper::instance()->html(s->symbol());
200 err("HTML: non supported HTML-entity found: %s\n",HtmlEntityMapper::instance()->html(s->symbol(),TRUE));
204 void HtmlDocVisitor::writeObfuscatedMailAddress(const QCString &url)
206 m_t << "<a href=\"#\" onclick=\"location.href='mai'+'lto:'";
209 for (i=0;i<url.length();)
211 m_t << "+'" << url.mid(i,size) << "'";
213 if (size==3) size=2; else size=3;
215 m_t << "; return false;\">";
218 void HtmlDocVisitor::visit(DocURL *u)
221 if (u->isEmail()) // mail address
223 QCString url = u->url();
224 writeObfuscatedMailAddress(url);
226 for (i=0;i<url.length();)
228 filter(url.mid(i,size));
229 if (i<url.length()-size) m_t << "<span style=\"display: none;\">.nosp@m.</span>";
231 if (size==5) size=4; else size=5;
238 m_t << u->url() << "\">";
244 void HtmlDocVisitor::visit(DocLineBreak *)
250 void HtmlDocVisitor::visit(DocHorRuler *hr)
253 forceEndParagraph(hr);
255 forceStartParagraph(hr);
258 void HtmlDocVisitor::visit(DocStyleChange *s)
263 case DocStyleChange::Bold:
264 if (s->enable()) m_t << "<b" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</b>";
266 case DocStyleChange::Italic:
267 if (s->enable()) m_t << "<em" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</em>";
269 case DocStyleChange::Code:
270 if (s->enable()) m_t << "<code" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</code>";
272 case DocStyleChange::Subscript:
273 if (s->enable()) m_t << "<sub" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</sub>";
275 case DocStyleChange::Superscript:
276 if (s->enable()) m_t << "<sup" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</sup>";
278 case DocStyleChange::Center:
281 forceEndParagraph(s);
282 m_t << "<center" << htmlAttribsToString(s->attribs()) << ">";
287 forceStartParagraph(s);
290 case DocStyleChange::Small:
291 if (s->enable()) m_t << "<small" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</small>";
293 case DocStyleChange::Preformatted:
296 forceEndParagraph(s);
297 m_t << "<pre" << htmlAttribsToString(s->attribs()) << ">";
304 forceStartParagraph(s);
307 case DocStyleChange::Div:
310 forceEndParagraph(s);
311 m_t << "<div" << htmlAttribsToString(s->attribs()) << ">";
316 forceStartParagraph(s);
319 case DocStyleChange::Span:
320 if (s->enable()) m_t << "<span" << htmlAttribsToString(s->attribs()) << ">"; else m_t << "</span>";
327 void HtmlDocVisitor::visit(DocVerbatim *s)
330 QCString lang = m_langExt;
331 if (!s->language().isEmpty()) // explicit language setting
333 lang = s->language();
335 SrcLangExt langExt = getLanguageFromFileName(lang);
338 case DocVerbatim::Code:
339 forceEndParagraph(s);
340 m_t << PREFRAG_START;
341 Doxygen::parserManager->getParser(lang)
351 FALSE, // inlineFragment
353 TRUE, // show line numbers
354 m_ctx // search context
357 forceStartParagraph(s);
359 case DocVerbatim::Verbatim:
360 forceEndParagraph(s);
361 m_t << /*PREFRAG_START <<*/ "<pre class=\"fragment\">";
363 m_t << "</pre>" /*<< PREFRAG_END*/;
364 forceStartParagraph(s);
366 case DocVerbatim::HtmlOnly:
367 if (s->isBlock()) forceEndParagraph(s);
369 if (s->isBlock()) forceStartParagraph(s);
371 case DocVerbatim::ManOnly:
372 case DocVerbatim::LatexOnly:
373 case DocVerbatim::XmlOnly:
374 case DocVerbatim::RtfOnly:
375 case DocVerbatim::DocbookOnly:
379 case DocVerbatim::Dot:
381 static int dotindex = 1;
382 QCString fileName(4096);
384 fileName.sprintf("%s%d%s",
385 (Config_getString("HTML_OUTPUT")+"/inline_dotgraph_").data(),
389 QFile file(fileName);
390 if (!file.open(IO_WriteOnly))
392 err("Could not open file %s for writing\n",fileName.data());
394 file.writeBlock( s->text(), s->text().length() );
397 forceEndParagraph(s);
398 m_t << "<div align=\"center\">" << endl;
399 writeDotFile(fileName,s->relPath(),s->context());
400 m_t << "</div>" << endl;
401 forceStartParagraph(s);
403 if (Config_getBool("DOT_CLEANUP")) file.remove();
406 case DocVerbatim::Msc:
408 forceEndParagraph(s);
410 static int mscindex = 1;
411 QCString baseName(4096);
413 baseName.sprintf("%s%d",
414 (Config_getString("HTML_OUTPUT")+"/inline_mscgraph_").data(),
417 QFile file(baseName+".msc");
418 if (!file.open(IO_WriteOnly))
420 err("Could not open file %s.msc for writing\n",baseName.data());
422 QCString text = "msc {";
426 file.writeBlock( text, text.length() );
429 m_t << "<div align=\"center\">" << endl;
430 writeMscFile(baseName+".msc",s->relPath(),s->context());
431 if (Config_getBool("DOT_CLEANUP")) file.remove();
433 m_t << "</div>" << endl;
434 forceStartParagraph(s);
439 void HtmlDocVisitor::visit(DocAnchor *anc)
442 m_t << "<a class=\"anchor\" id=\"" << anc->anchor() << "\"></a>";
445 void HtmlDocVisitor::visit(DocInclude *inc)
448 SrcLangExt langExt = getLanguageFromFileName(inc->extension());
451 case DocInclude::Include:
452 forceEndParagraph(inc);
453 m_t << PREFRAG_START;
454 Doxygen::parserManager->getParser(inc->extension())
464 TRUE, // inlineFragment
466 FALSE, // show line numbers
467 m_ctx // search context
470 forceStartParagraph(inc);
472 case DocInclude::IncWithLines:
474 forceEndParagraph(inc);
475 m_t << PREFRAG_START;
476 QFileInfo cfi( inc->file() );
477 FileDef fd( cfi.dirPath().utf8(), cfi.fileName().utf8() );
478 Doxygen::parserManager->getParser(inc->extension())
488 FALSE, // inline fragment
490 TRUE, // show line numbers
491 m_ctx // search context
494 forceStartParagraph(inc);
497 case DocInclude::DontInclude:
499 case DocInclude::HtmlInclude:
502 case DocInclude::LatexInclude:
504 case DocInclude::VerbInclude:
505 forceEndParagraph(inc);
506 m_t << /*PREFRAG_START <<*/ "<pre class=\"fragment\">";
508 m_t << "</pre>" /*<< PREFRAG_END*/;
509 forceStartParagraph(inc);
511 case DocInclude::Snippet:
513 forceEndParagraph(inc);
514 m_t << PREFRAG_START;
515 Doxygen::parserManager->getParser(inc->extension())
518 extractBlock(inc->text(),inc->blockId()),
525 TRUE, // inlineFragment
527 TRUE, // show line number
528 m_ctx // search context
531 forceStartParagraph(inc);
537 void HtmlDocVisitor::visit(DocIncOperator *op)
539 //printf("DocIncOperator: type=%d first=%d, last=%d text=`%s'\n",
540 // op->type(),op->isFirst(),op->isLast(),op->text().data());
543 if (!m_hide) m_t << PREFRAG_START;
547 SrcLangExt langExt = getLanguageFromFileName(m_langExt);
548 if (op->type()!=DocIncOperator::Skip)
553 Doxygen::parserManager->getParser(m_langExt)
564 FALSE, // inline fragment
566 TRUE, // show line numbers
567 m_ctx // search context
576 if (!m_hide) m_t << PREFRAG_END;
580 if (!m_hide) m_t << endl;
584 void HtmlDocVisitor::visit(DocFormula *f)
587 bool bDisplay = !f->isInline();
590 forceEndParagraph(f);
591 m_t << "<p class=\"formulaDsp\">" << endl;
594 if (Config_getBool("USE_MATHJAX"))
596 QCString text = f->text();
597 bool closeInline = FALSE;
598 if (!bDisplay && !text.isEmpty() && text.at(0)=='$' &&
599 text.at(text.length()-1)=='$')
602 text = text.mid(1,text.length()-2);
605 m_t << convertToHtml(text);
613 m_t << "<img class=\"formula"
614 << (bDisplay ? "Dsp" : "Inl");
616 filterQuotedCdataAttr(f->text());
618 // TODO: cache image dimensions on formula generation and give height/width
619 // for faster preloading and better rendering of the page
620 m_t << " src=\"" << f->relPath() << f->name() << ".png\"/>";
625 m_t << endl << "</p>" << endl;
626 forceStartParagraph(f);
630 void HtmlDocVisitor::visit(DocIndexEntry *e)
632 QCString anchor = convertIndexWordToAnchor(e->entry());
635 anchor.prepend(e->member()->anchor()+"_");
637 m_t << "<a name=\"" << anchor << "\"></a>";
638 //printf("*** DocIndexEntry: word='%s' scope='%s' member='%s'\n",
639 // e->entry().data(),
640 // e->scope() ? e->scope()->name().data() : "<null>",
641 // e->member() ? e->member()->name().data() : "<null>"
643 Doxygen::indexList->addIndexItem(e->scope(),e->member(),anchor,e->entry());
646 void HtmlDocVisitor::visit(DocSimpleSectSep *)
648 m_t << "</dd>" << endl;
649 m_t << "<dd>" << endl;
652 void HtmlDocVisitor::visit(DocCite *cite)
655 if (!cite->file().isEmpty())
657 startLink(cite->ref(),cite->file(),cite->relPath(),cite->anchor());
663 filter(cite->text());
664 if (!cite->file().isEmpty())
675 //--------------------------------------
676 // visitor functions for compound nodes
677 //--------------------------------------
680 void HtmlDocVisitor::visitPre(DocAutoList *l)
682 //printf("DocAutoList::visitPre\n");
684 forceEndParagraph(l);
688 // Do list type based on depth:
695 m_t << "<ol type=\"" << types[l->depth() % NUM_HTML_LIST_TYPES] << "\">";
701 if (!l->isPreformatted()) m_t << "\n";
704 void HtmlDocVisitor::visitPost(DocAutoList *l)
706 //printf("DocAutoList::visitPost\n");
716 if (!l->isPreformatted()) m_t << "\n";
717 forceStartParagraph(l);
720 void HtmlDocVisitor::visitPre(DocAutoListItem *)
726 void HtmlDocVisitor::visitPost(DocAutoListItem *li)
730 if (!li->isPreformatted()) m_t << "\n";
734 bool isFirstChildNode(T *parent, DocNode *node)
736 return parent->children().getFirst()==node;
740 bool isLastChildNode(T *parent, DocNode *node)
742 return parent->children().getLast()==node;
745 bool isSeparatedParagraph(DocSimpleSect *parent,DocPara *par)
747 QList<DocNode> nodes = parent->children();
748 int i = nodes.findRef(par);
749 if (i==-1) return FALSE;
750 int count = parent->children().count();
751 if (count>1 && i==0) // first node
753 if (nodes.at(i+1)->kind()==DocNode::Kind_SimpleSectSep)
758 else if (count>1 && i==count-1) // last node
760 if (nodes.at(i-1)->kind()==DocNode::Kind_SimpleSectSep)
765 else if (count>2 && i>0 && i<count-1) // intermediate node
767 if (nodes.at(i-1)->kind()==DocNode::Kind_SimpleSectSep &&
768 nodes.at(i+1)->kind()==DocNode::Kind_SimpleSectSep)
776 static int getParagraphContext(DocPara *p,bool &isFirst,bool &isLast)
781 if (p && p->parent())
783 switch (p->parent()->kind())
785 case DocNode::Kind_ParBlock:
786 { // hierarchy: node N -> para -> parblock -> para
787 // adapt return value to kind of N
788 DocNode::Kind kind = DocNode::Kind_Para;
789 if ( p->parent()->parent() && p->parent()->parent()->parent() )
791 kind = p->parent()->parent()->parent()->kind();
793 isFirst=isFirstChildNode((DocParBlock*)p->parent(),p);
794 isLast =isLastChildNode ((DocParBlock*)p->parent(),p);
798 if (kind==DocNode::Kind_HtmlListItem ||
799 kind==DocNode::Kind_SecRefItem)
803 else if (kind==DocNode::Kind_HtmlDescData ||
804 kind==DocNode::Kind_XRefItem ||
805 kind==DocNode::Kind_SimpleSect)
809 else if (kind==DocNode::Kind_HtmlCell ||
810 kind==DocNode::Kind_ParamList)
817 if (kind==DocNode::Kind_HtmlListItem ||
818 kind==DocNode::Kind_SecRefItem)
822 else if (kind==DocNode::Kind_HtmlDescData ||
823 kind==DocNode::Kind_XRefItem ||
824 kind==DocNode::Kind_SimpleSect)
828 else if (kind==DocNode::Kind_HtmlCell ||
829 kind==DocNode::Kind_ParamList)
836 case DocNode::Kind_AutoListItem:
837 isFirst=isFirstChildNode((DocAutoListItem*)p->parent(),p);
838 isLast =isLastChildNode ((DocAutoListItem*)p->parent(),p);
841 case DocNode::Kind_SimpleListItem:
846 case DocNode::Kind_ParamList:
851 case DocNode::Kind_HtmlListItem:
852 isFirst=isFirstChildNode((DocHtmlListItem*)p->parent(),p);
853 isLast =isLastChildNode ((DocHtmlListItem*)p->parent(),p);
857 case DocNode::Kind_SecRefItem:
858 isFirst=isFirstChildNode((DocSecRefItem*)p->parent(),p);
859 isLast =isLastChildNode ((DocSecRefItem*)p->parent(),p);
863 case DocNode::Kind_HtmlDescData:
864 isFirst=isFirstChildNode((DocHtmlDescData*)p->parent(),p);
865 isLast =isLastChildNode ((DocHtmlDescData*)p->parent(),p);
869 case DocNode::Kind_XRefItem:
870 isFirst=isFirstChildNode((DocXRefItem*)p->parent(),p);
871 isLast =isLastChildNode ((DocXRefItem*)p->parent(),p);
875 case DocNode::Kind_SimpleSect:
876 isFirst=isFirstChildNode((DocSimpleSect*)p->parent(),p);
877 isLast =isLastChildNode ((DocSimpleSect*)p->parent(),p);
880 if (isSeparatedParagraph((DocSimpleSect*)p->parent(),p))
881 // if the paragraph is enclosed with separators it will
882 // be included in <dd>..</dd> so avoid addition paragraph
888 case DocNode::Kind_HtmlCell:
889 isFirst=isFirstChildNode((DocHtmlCell*)p->parent(),p);
890 isLast =isLastChildNode ((DocHtmlCell*)p->parent(),p);
897 //printf("para=%p parent()->kind=%d isFirst=%d isLast=%d t=%d\n",
898 // p,p->parent()->kind(),isFirst,isLast,t);
903 void HtmlDocVisitor::visitPre(DocPara *p)
907 //printf("DocPara::visitPre: parent of kind %d ",
908 // p->parent() ? p->parent()->kind() : -1);
910 bool needsTag = FALSE;
911 if (p && p->parent())
913 switch (p->parent()->kind())
915 case DocNode::Kind_Section:
916 case DocNode::Kind_Internal:
917 case DocNode::Kind_HtmlListItem:
918 case DocNode::Kind_HtmlDescData:
919 case DocNode::Kind_HtmlCell:
920 case DocNode::Kind_SimpleListItem:
921 case DocNode::Kind_AutoListItem:
922 case DocNode::Kind_SimpleSect:
923 case DocNode::Kind_XRefItem:
924 case DocNode::Kind_Copy:
925 case DocNode::Kind_HtmlBlockQuote:
926 case DocNode::Kind_ParBlock:
929 case DocNode::Kind_Root:
930 needsTag = !((DocRoot*)p->parent())->singleLine();
937 // if the first element of a paragraph is something that should be outside of
938 // the paragraph (<ul>,<dl>,<table>,..) then that will already started the
939 // paragraph and we don't need to do it here
941 if (p && nodeIndex<p->children().count())
943 while (nodeIndex<p->children().count() &&
944 p->children().at(nodeIndex)->kind()==DocNode::Kind_WhiteSpace)
948 if (nodeIndex<p->children().count())
950 DocNode *n = p->children().at(nodeIndex);
951 if (mustBeOutsideParagraph(n))
958 // check if this paragraph is the first or last child of a <li> or <dd>.
959 // this allows us to mark the tag with a special class so we can
960 // fix the otherwise ugly spacing.
962 static const char *contexts[7] =
964 " class=\"startli\"", // 1
965 " class=\"startdd\"", // 2
966 " class=\"endli\"", // 3
967 " class=\"enddd\"", // 4
968 " class=\"starttd\"", // 5
969 " class=\"endtd\"" // 6
973 t = getParagraphContext(p,isFirst,isLast);
974 //printf("startPara first=%d last=%d\n",isFirst,isLast);
975 if (isFirst && isLast) needsTag=FALSE;
977 //printf(" needsTag=%d\n",needsTag);
978 // write the paragraph tag (if needed)
979 if (needsTag) m_t << "<p" << contexts[t] << ">";
982 void HtmlDocVisitor::visitPost(DocPara *p)
984 bool needsTag = FALSE;
985 if (p && p->parent())
987 switch (p->parent()->kind())
989 case DocNode::Kind_Section:
990 case DocNode::Kind_Internal:
991 case DocNode::Kind_HtmlListItem:
992 case DocNode::Kind_HtmlDescData:
993 case DocNode::Kind_HtmlCell:
994 case DocNode::Kind_SimpleListItem:
995 case DocNode::Kind_AutoListItem:
996 case DocNode::Kind_SimpleSect:
997 case DocNode::Kind_XRefItem:
998 case DocNode::Kind_Copy:
999 case DocNode::Kind_HtmlBlockQuote:
1000 case DocNode::Kind_ParBlock:
1003 case DocNode::Kind_Root:
1004 needsTag = !((DocRoot*)p->parent())->singleLine();
1012 // if the last element of a paragraph is something that should be outside of
1013 // the paragraph (<ul>,<dl>,<table>) then that will already have ended the
1014 // paragraph and we don't need to do it here
1015 int nodeIndex = p->children().count()-1;
1016 if (p && nodeIndex>=0)
1018 while (nodeIndex>=0 && p->children().at(nodeIndex)->kind()==DocNode::Kind_WhiteSpace)
1024 DocNode *n = p->children().at(nodeIndex);
1025 if (mustBeOutsideParagraph(n))
1034 getParagraphContext(p,isFirst,isLast);
1035 //printf("endPara first=%d last=%d\n",isFirst,isLast);
1036 if (isFirst && isLast) needsTag=FALSE;
1038 //printf("DocPara::visitPost needsTag=%d\n",needsTag);
1040 if (needsTag) m_t << "</p>\n";
1044 void HtmlDocVisitor::visitPre(DocRoot *)
1048 void HtmlDocVisitor::visitPost(DocRoot *)
1052 void HtmlDocVisitor::visitPre(DocSimpleSect *s)
1055 forceEndParagraph(s);
1056 m_t << "<dl class=\"section " << s->typeString() << "\"><dt>";
1059 case DocSimpleSect::See:
1060 m_t << theTranslator->trSeeAlso(); break;
1061 case DocSimpleSect::Return:
1062 m_t << theTranslator->trReturns(); break;
1063 case DocSimpleSect::Author:
1064 m_t << theTranslator->trAuthor(TRUE,TRUE); break;
1065 case DocSimpleSect::Authors:
1066 m_t << theTranslator->trAuthor(TRUE,FALSE); break;
1067 case DocSimpleSect::Version:
1068 m_t << theTranslator->trVersion(); break;
1069 case DocSimpleSect::Since:
1070 m_t << theTranslator->trSince(); break;
1071 case DocSimpleSect::Date:
1072 m_t << theTranslator->trDate(); break;
1073 case DocSimpleSect::Note:
1074 m_t << theTranslator->trNote(); break;
1075 case DocSimpleSect::Warning:
1076 m_t << theTranslator->trWarning(); break;
1077 case DocSimpleSect::Pre:
1078 m_t << theTranslator->trPrecondition(); break;
1079 case DocSimpleSect::Post:
1080 m_t << theTranslator->trPostcondition(); break;
1081 case DocSimpleSect::Copyright:
1082 m_t << theTranslator->trCopyright(); break;
1083 case DocSimpleSect::Invar:
1084 m_t << theTranslator->trInvariant(); break;
1085 case DocSimpleSect::Remark:
1086 m_t << theTranslator->trRemarks(); break;
1087 case DocSimpleSect::Attention:
1088 m_t << theTranslator->trAttention(); break;
1089 case DocSimpleSect::User: break;
1090 case DocSimpleSect::Rcs: break;
1091 case DocSimpleSect::Unknown: break;
1094 // special case 1: user defined title
1095 if (s->type()!=DocSimpleSect::User && s->type()!=DocSimpleSect::Rcs)
1101 void HtmlDocVisitor::visitPost(DocSimpleSect *s)
1104 m_t << "</dd></dl>\n";
1105 forceStartParagraph(s);
1108 void HtmlDocVisitor::visitPre(DocTitle *)
1112 void HtmlDocVisitor::visitPost(DocTitle *)
1118 void HtmlDocVisitor::visitPre(DocSimpleList *sl)
1121 forceEndParagraph(sl);
1123 if (!sl->isPreformatted()) m_t << "\n";
1127 void HtmlDocVisitor::visitPost(DocSimpleList *sl)
1131 if (!sl->isPreformatted()) m_t << "\n";
1132 forceStartParagraph(sl);
1135 void HtmlDocVisitor::visitPre(DocSimpleListItem *)
1141 void HtmlDocVisitor::visitPost(DocSimpleListItem *li)
1145 if (!li->isPreformatted()) m_t << "\n";
1148 void HtmlDocVisitor::visitPre(DocSection *s)
1151 forceEndParagraph(s);
1152 m_t << "<h" << s->level() << ">";
1153 m_t << "<a class=\"anchor\" id=\"" << s->anchor();
1154 m_t << "\"></a>" << endl;
1155 filter(convertCharEntitiesToUTF8(s->title().data()));
1156 m_t << "</h" << s->level() << ">\n";
1159 void HtmlDocVisitor::visitPost(DocSection *s)
1161 forceStartParagraph(s);
1164 void HtmlDocVisitor::visitPre(DocHtmlList *s)
1167 forceEndParagraph(s);
1168 if (s->type()==DocHtmlList::Ordered)
1170 m_t << "<ol" << htmlAttribsToString(s->attribs()) << ">\n";
1174 m_t << "<ul" << htmlAttribsToString(s->attribs()) << ">\n";
1178 void HtmlDocVisitor::visitPost(DocHtmlList *s)
1181 if (s->type()==DocHtmlList::Ordered)
1189 if (!s->isPreformatted()) m_t << "\n";
1190 forceStartParagraph(s);
1193 void HtmlDocVisitor::visitPre(DocHtmlListItem *i)
1196 m_t << "<li" << htmlAttribsToString(i->attribs()) << ">";
1197 if (!i->isPreformatted()) m_t << "\n";
1200 void HtmlDocVisitor::visitPost(DocHtmlListItem *)
1206 void HtmlDocVisitor::visitPre(DocHtmlDescList *dl)
1209 forceEndParagraph(dl);
1210 m_t << "<dl" << htmlAttribsToString(dl->attribs()) << ">\n";
1213 void HtmlDocVisitor::visitPost(DocHtmlDescList *dl)
1217 forceStartParagraph(dl);
1220 void HtmlDocVisitor::visitPre(DocHtmlDescTitle *dt)
1223 m_t << "<dt" << htmlAttribsToString(dt->attribs()) << ">";
1226 void HtmlDocVisitor::visitPost(DocHtmlDescTitle *)
1232 void HtmlDocVisitor::visitPre(DocHtmlDescData *dd)
1235 m_t << "<dd" << htmlAttribsToString(dd->attribs()) << ">";
1238 void HtmlDocVisitor::visitPost(DocHtmlDescData *)
1244 void HtmlDocVisitor::visitPre(DocHtmlTable *t)
1248 forceEndParagraph(t);
1250 QString attrs = htmlAttribsToString(t->attribs());
1251 if (attrs.isEmpty())
1253 m_t << "<table class=\"doxtable\">\n";
1257 m_t << "<table " << htmlAttribsToString(t->attribs()) << ">\n";
1261 void HtmlDocVisitor::visitPost(DocHtmlTable *t)
1264 m_t << "</table>\n";
1265 forceStartParagraph(t);
1268 void HtmlDocVisitor::visitPre(DocHtmlRow *tr)
1271 m_t << "<tr" << htmlAttribsToString(tr->attribs()) << ">\n";
1274 void HtmlDocVisitor::visitPost(DocHtmlRow *)
1280 void HtmlDocVisitor::visitPre(DocHtmlCell *c)
1285 m_t << "<th" << htmlAttribsToString(c->attribs()) << ">";
1289 m_t << "<td" << htmlAttribsToString(c->attribs()) << ">";
1293 void HtmlDocVisitor::visitPost(DocHtmlCell *c)
1296 if (c->isHeading()) m_t << "</th>"; else m_t << "</td>";
1299 void HtmlDocVisitor::visitPre(DocHtmlCaption *c)
1302 bool hasAlign = FALSE;
1303 HtmlAttribListIterator li(c->attribs());
1305 for (li.toFirst();(att=li.current());++li)
1307 if (att->name=="align") hasAlign=TRUE;
1309 m_t << "<caption" << htmlAttribsToString(c->attribs());
1310 if (!hasAlign) m_t << " align=\"bottom\"";
1314 void HtmlDocVisitor::visitPost(DocHtmlCaption *)
1317 m_t << "</caption>\n";
1320 void HtmlDocVisitor::visitPre(DocInternal *)
1323 //forceEndParagraph(i);
1324 //m_t << "<p><b>" << theTranslator->trForInternalUseOnly() << "</b></p>" << endl;
1327 void HtmlDocVisitor::visitPost(DocInternal *)
1330 //forceStartParagraph(i);
1333 void HtmlDocVisitor::visitPre(DocHRef *href)
1336 if (href->url().left(7)=="mailto:")
1338 writeObfuscatedMailAddress(href->url().mid(7));
1342 QCString url = correctURL(href->url(),href->relPath());
1343 m_t << "<a href=\"" << convertToXML(url) << "\""
1344 << htmlAttribsToString(href->attribs()) << ">";
1348 void HtmlDocVisitor::visitPost(DocHRef *)
1354 void HtmlDocVisitor::visitPre(DocHtmlHeader *header)
1357 forceEndParagraph(header);
1358 m_t << "<h" << header->level()
1359 << htmlAttribsToString(header->attribs()) << ">";
1362 void HtmlDocVisitor::visitPost(DocHtmlHeader *header)
1365 m_t << "</h" << header->level() << ">\n";
1366 forceStartParagraph(header);
1369 void HtmlDocVisitor::visitPre(DocImage *img)
1371 if (img->type()==DocImage::Html)
1373 forceEndParagraph(img);
1375 QString baseName=img->name();
1377 if ((i=baseName.findRev('/'))!=-1 || (i=baseName.findRev('\\'))!=-1)
1379 baseName=baseName.right(baseName.length()-i-1);
1381 m_t << "<div class=\"image\">" << endl;
1382 QCString url = img->url();
1385 m_t << "<img src=\"" << img->relPath() << img->name() << "\" alt=\""
1386 << baseName << "\"" << htmlAttribsToString(img->attribs())
1391 m_t << "<img src=\"" << correctURL(url,img->relPath()) << "\" "
1392 << htmlAttribsToString(img->attribs())
1395 if (img->hasCaption())
1397 m_t << "<div class=\"caption\">" << endl;
1400 else // other format -> skip
1407 void HtmlDocVisitor::visitPost(DocImage *img)
1409 if (img->type()==DocImage::Html)
1412 if (img->hasCaption())
1416 m_t << "</div>" << endl;
1417 forceStartParagraph(img);
1419 else // other format
1425 void HtmlDocVisitor::visitPre(DocDotFile *df)
1428 m_t << "<div class=\"dotgraph\">" << endl;
1429 writeDotFile(df->file(),df->relPath(),df->context());
1430 if (df->hasCaption())
1432 m_t << "<div class=\"caption\">" << endl;
1436 void HtmlDocVisitor::visitPost(DocDotFile *df)
1439 if (df->hasCaption())
1441 m_t << "</div>" << endl;
1443 m_t << "</div>" << endl;
1446 void HtmlDocVisitor::visitPre(DocMscFile *df)
1449 m_t << "<div class=\"mscgraph\">" << endl;
1450 writeMscFile(df->file(),df->relPath(),df->context());
1451 if (df->hasCaption())
1453 m_t << "<div class=\"caption\">" << endl;
1456 void HtmlDocVisitor::visitPost(DocMscFile *df)
1459 if (df->hasCaption())
1461 m_t << "</div>" << endl;
1463 m_t << "</div>" << endl;
1466 void HtmlDocVisitor::visitPre(DocDiaFile *df)
1469 m_t << "<div class=\"diagraph\">" << endl;
1470 writeDiaFile(df->file(),df->relPath(),df->context());
1471 if (df->hasCaption())
1473 m_t << "<div class=\"caption\">" << endl;
1476 void HtmlDocVisitor::visitPost(DocDiaFile *df)
1479 if (df->hasCaption())
1481 m_t << "</div>" << endl;
1483 m_t << "</div>" << endl;
1486 void HtmlDocVisitor::visitPre(DocLink *lnk)
1489 startLink(lnk->ref(),lnk->file(),lnk->relPath(),lnk->anchor());
1492 void HtmlDocVisitor::visitPost(DocLink *)
1498 void HtmlDocVisitor::visitPre(DocRef *ref)
1501 if (!ref->file().isEmpty())
1503 // when ref->isSubPage()==TRUE we use ref->file() for HTML and
1504 // ref->anchor() for LaTeX/RTF
1505 startLink(ref->ref(),ref->file(),ref->relPath(),ref->isSubPage() ? QCString() : ref->anchor());
1507 if (!ref->hasLinkText()) filter(ref->targetTitle());
1510 void HtmlDocVisitor::visitPost(DocRef *ref)
1513 if (!ref->file().isEmpty()) endLink();
1517 void HtmlDocVisitor::visitPre(DocSecRefItem *ref)
1520 QString refName=ref->file();
1521 if (refName.right(Doxygen::htmlFileExtension.length())!=
1522 QString(Doxygen::htmlFileExtension))
1524 refName+=Doxygen::htmlFileExtension;
1526 m_t << "<li><a href=\"" << refName << "#" << ref->anchor() << "\">";
1530 void HtmlDocVisitor::visitPost(DocSecRefItem *)
1533 m_t << "</a></li>\n";
1536 void HtmlDocVisitor::visitPre(DocSecRefList *s)
1539 forceEndParagraph(s);
1540 m_t << "<div class=\"multicol\">" << endl;
1541 m_t << "<ul>" << endl;
1544 void HtmlDocVisitor::visitPost(DocSecRefList *s)
1547 m_t << "</ul>" << endl;
1548 m_t << "</div>" << endl;
1549 forceStartParagraph(s);
1552 //void HtmlDocVisitor::visitPre(DocLanguage *l)
1554 // QString langId = Config_getEnum("OUTPUT_LANGUAGE");
1555 // if (l->id().lower()!=langId.lower())
1562 //void HtmlDocVisitor::visitPost(DocLanguage *l)
1564 // QString langId = Config_getEnum("OUTPUT_LANGUAGE");
1565 // if (l->id().lower()!=langId.lower())
1571 void HtmlDocVisitor::visitPre(DocParamSect *s)
1574 forceEndParagraph(s);
1579 case DocParamSect::Param:
1580 heading=theTranslator->trParameters();
1583 case DocParamSect::RetVal:
1584 heading=theTranslator->trReturnValues();
1587 case DocParamSect::Exception:
1588 heading=theTranslator->trExceptions();
1589 className="exception";
1591 case DocParamSect::TemplateParam:
1592 heading=theTranslator->trTemplateParameters();
1593 className="tparams";
1598 m_t << "<dl class=\"" << className << "\"><dt>";
1600 m_t << "</dt><dd>" << endl;
1601 m_t << " <table class=\"" << className << "\">" << endl;
1604 void HtmlDocVisitor::visitPost(DocParamSect *s)
1607 m_t << " </table>" << endl;
1608 m_t << " </dd>" << endl;
1609 m_t << "</dl>" << endl;
1610 forceStartParagraph(s);
1613 void HtmlDocVisitor::visitPre(DocParamList *pl)
1615 //printf("DocParamList::visitPre\n");
1618 DocParamSect *sect = 0;
1619 if (pl->parent()->kind()==DocNode::Kind_ParamSect)
1621 sect=(DocParamSect*)pl->parent();
1623 if (sect && sect->hasInOutSpecifier())
1625 m_t << "<td class=\"paramdir\">";
1626 if (pl->direction()!=DocParamSect::Unspecified)
1629 if (pl->direction()==DocParamSect::In)
1633 else if (pl->direction()==DocParamSect::Out)
1637 else if (pl->direction()==DocParamSect::InOut)
1645 if (sect && sect->hasTypeSpecifier())
1647 m_t << "<td class=\"paramtype\">";
1648 QListIterator<DocNode> li(pl->paramTypes());
1651 for (li.toFirst();(type=li.current());++li)
1653 if (!first) m_t << " | "; else first=FALSE;
1654 if (type->kind()==DocNode::Kind_Word)
1656 visit((DocWord*)type);
1658 else if (type->kind()==DocNode::Kind_LinkedWord)
1660 visit((DocLinkedWord*)type);
1665 m_t << "<td class=\"paramname\">";
1666 //QStrListIterator li(pl->parameters());
1668 QListIterator<DocNode> li(pl->parameters());
1671 for (li.toFirst();(param=li.current());++li)
1673 if (!first) m_t << ","; else first=FALSE;
1674 if (param->kind()==DocNode::Kind_Word)
1676 visit((DocWord*)param);
1678 else if (param->kind()==DocNode::Kind_LinkedWord)
1680 visit((DocLinkedWord*)param);
1686 void HtmlDocVisitor::visitPost(DocParamList *)
1688 //printf("DocParamList::visitPost\n");
1690 m_t << "</td></tr>" << endl;
1693 void HtmlDocVisitor::visitPre(DocXRefItem *x)
1696 if (x->title().isEmpty()) return;
1698 forceEndParagraph(x);
1699 bool anonymousEnum = x->file()=="@";
1702 m_t << "<dl class=\"" << x->key() << "\"><dt><b><a class=\"el\" href=\""
1703 << x->relPath() << x->file() << Doxygen::htmlFileExtension
1704 << "#" << x->anchor() << "\">";
1708 m_t << "<dl class=\"" << x->key() << "\"><dt><b>";
1712 if (!anonymousEnum) m_t << "</a>";
1713 m_t << "</b></dt><dd>";
1716 void HtmlDocVisitor::visitPost(DocXRefItem *x)
1719 if (x->title().isEmpty()) return;
1720 m_t << "</dd></dl>" << endl;
1721 forceStartParagraph(x);
1724 void HtmlDocVisitor::visitPre(DocInternalRef *ref)
1727 startLink(0,ref->file(),ref->relPath(),ref->anchor());
1730 void HtmlDocVisitor::visitPost(DocInternalRef *)
1737 void HtmlDocVisitor::visitPre(DocCopy *)
1741 void HtmlDocVisitor::visitPost(DocCopy *)
1745 void HtmlDocVisitor::visitPre(DocText *)
1749 void HtmlDocVisitor::visitPost(DocText *)
1753 void HtmlDocVisitor::visitPre(DocHtmlBlockQuote *b)
1756 forceEndParagraph(b);
1758 QString attrs = htmlAttribsToString(b->attribs());
1759 if (attrs.isEmpty())
1761 m_t << "<blockquote class=\"doxtable\">\n";
1765 m_t << "<blockquote " << htmlAttribsToString(b->attribs()) << ">\n";
1769 void HtmlDocVisitor::visitPost(DocHtmlBlockQuote *b)
1772 m_t << "</blockquote>" << endl;
1773 forceStartParagraph(b);
1776 void HtmlDocVisitor::visitPre(DocVhdlFlow *vf)
1779 if (VhdlDocGen::getFlowMember()) // use VHDL flow chart creator
1781 forceEndParagraph(vf);
1782 QCString fname=FlowChart::convertNameToFileName();
1784 m_t << "flowchart: " ; // TODO: translate me
1785 m_t << "<a href=\"";
1786 m_t << fname.data();
1788 m_t << VhdlDocGen::getFlowMember()->name().data();
1790 if (vf->hasCaption())
1797 void HtmlDocVisitor::visitPost(DocVhdlFlow *vf)
1800 if (VhdlDocGen::getFlowMember()) // use VHDL flow chart creator
1803 forceStartParagraph(vf);
1807 void HtmlDocVisitor::visitPre(DocParBlock *)
1812 void HtmlDocVisitor::visitPost(DocParBlock *)
1819 void HtmlDocVisitor::filter(const char *str)
1829 case '<': m_t << "<"; break;
1830 case '>': m_t << ">"; break;
1831 case '&': m_t << "&"; break;
1837 /// Escape basic entities to produce a valid CDATA attribute value,
1838 /// assume that the outer quoting will be using the double quote "
1839 void HtmlDocVisitor::filterQuotedCdataAttr(const char* str)
1849 case '&': m_t << "&"; break;
1850 case '"': m_t << """; break;
1851 // For SGML compliance, and given the SGML declaration for HTML syntax,
1852 // it's enough to replace these two, provided that the declaration
1853 // for the HTML version we generate (and as supported by the browser)
1854 // specifies that all the other symbols used in rawVal are
1855 // within the right character class (i.e., they're not
1856 // some multinational weird characters not in the BASESET).
1857 // We assume that 1) the browser will support whatever is remaining
1858 // in the formula and 2) the TeX formulae are generally governed
1859 // by even stricter character restrictions so it should be enough.
1861 // On some incompliant browsers, additional translation of
1862 // '>' and '<' into ">" and "<", respectively, might be needed;
1863 // but I'm unaware of particular modern (last 4 years) versions
1864 // with such problems, so let's not do it for performance.
1865 // Also, some brousers will (wrongly) not process the entity references
1866 // inside the attribute value and show the &...; form instead,
1867 // so we won't create entites unless necessary to minimize clutter there.
1874 void HtmlDocVisitor::startLink(const QCString &ref,const QCString &file,
1875 const QCString &relPath,const QCString &anchor,
1876 const QCString &tooltip)
1878 //printf("HtmlDocVisitor: file=%s anchor=%s\n",file.data(),anchor.data());
1879 if (!ref.isEmpty()) // link to entity imported via tag file
1881 m_t << "<a class=\"elRef\" ";
1882 m_t << externalLinkTarget() << externalRef(relPath,ref,FALSE);
1886 m_t << "<a class=\"el\" ";
1889 m_t << externalRef(relPath,ref,TRUE);
1890 if (!file.isEmpty()) m_t << file << Doxygen::htmlFileExtension;
1891 if (!anchor.isEmpty()) m_t << "#" << anchor;
1893 if (!tooltip.isEmpty()) m_t << " title=\"" << substitute(tooltip,"\"",""") << "\"";
1897 void HtmlDocVisitor::endLink()
1902 void HtmlDocVisitor::pushEnabled()
1904 m_enabled.push(new bool(m_hide));
1907 void HtmlDocVisitor::popEnabled()
1909 bool *v=m_enabled.pop();
1915 void HtmlDocVisitor::writeDotFile(const QCString &fn,const QCString &relPath,
1916 const QCString &context)
1918 QCString baseName=fn;
1920 if ((i=baseName.findRev('/'))!=-1)
1922 baseName=baseName.right(baseName.length()-i-1);
1924 if ((i=baseName.find('.'))!=-1) // strip extension
1926 baseName=baseName.left(i);
1928 baseName.prepend("dot_");
1929 QCString outDir = Config_getString("HTML_OUTPUT");
1930 writeDotGraphFromFile(fn,outDir,baseName,BITMAP);
1931 writeDotImageMapFromFile(m_t,fn,outDir,relPath,baseName,context);
1934 void HtmlDocVisitor::writeMscFile(const QCString &fileName,
1935 const QCString &relPath,
1936 const QCString &context)
1938 QCString baseName=fileName;
1940 if ((i=baseName.findRev('/'))!=-1) // strip path
1942 baseName=baseName.right(baseName.length()-i-1);
1944 if ((i=baseName.find('.'))!=-1) // strip extension
1946 baseName=baseName.left(i);
1948 baseName.prepend("msc_");
1949 QCString outDir = Config_getString("HTML_OUTPUT");
1950 QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
1951 MscOutputFormat mscFormat = MSC_BITMAP;
1952 if ("svg" == imgExt)
1953 mscFormat = MSC_SVG;
1954 writeMscGraphFromFile(fileName,outDir,baseName,mscFormat);
1955 writeMscImageMapFromFile(m_t,fileName,outDir,relPath,baseName,context,mscFormat);
1958 void HtmlDocVisitor::writeDiaFile(const QCString &fileName,
1959 const QCString &relPath,
1962 QCString baseName=fileName;
1964 if ((i=baseName.findRev('/'))!=-1) // strip path
1966 baseName=baseName.right(baseName.length()-i-1);
1968 if ((i=baseName.find('.'))!=-1) // strip extension
1970 baseName=baseName.left(i);
1972 baseName.prepend("dia_");
1973 QCString outDir = Config_getString("HTML_OUTPUT");
1974 writeDiaGraphFromFile(fileName,outDir,baseName,DIA_BITMAP);
1976 m_t << "<img src=\"" << relPath << baseName << ".png" << "\" />" << endl;
1979 /** Used for items found inside a paragraph, which due to XHTML restrictions
1980 * have to be outside of the paragraph. This method will forcefully end
1981 * the current paragraph and forceStartParagraph() will restart it.
1983 void HtmlDocVisitor::forceEndParagraph(DocNode *n)
1985 //printf("forceEndParagraph(%p) %d\n",n,n->kind());
1986 if (n->parent() && n->parent()->kind()==DocNode::Kind_Para)
1988 DocPara *para = (DocPara*)n->parent();
1989 int nodeIndex = para->children().findRef(n);
1991 if (nodeIndex<0) return; // first node
1992 while (nodeIndex>=0 &&
1993 para->children().at(nodeIndex)->kind()==DocNode::Kind_WhiteSpace
2000 DocNode *n = para->children().at(nodeIndex);
2001 //printf("n=%p kind=%d outside=%d\n",n,n->kind(),mustBeOutsideParagraph(n));
2002 if (mustBeOutsideParagraph(n)) return;
2007 getParagraphContext(para,isFirst,isLast);
2008 //printf("forceEnd first=%d last=%d\n",isFirst,isLast);
2009 if (isFirst && isLast) return;
2015 /** Used for items found inside a paragraph, which due to XHTML restrictions
2016 * have to be outside of the paragraph. This method will forcefully start
2017 * the paragraph, that was previously ended by forceEndParagraph().
2019 void HtmlDocVisitor::forceStartParagraph(DocNode *n)
2021 //printf("forceStartParagraph(%p) %d\n",n,n->kind());
2022 if (n->parent() && n->parent()->kind()==DocNode::Kind_Para) // if we are inside a paragraph
2024 DocPara *para = (DocPara*)n->parent();
2025 int nodeIndex = para->children().findRef(n);
2026 int numNodes = para->children().count();
2028 if (nodeIndex==numNodes) return; // last node
2029 while (nodeIndex<numNodes &&
2030 para->children().at(nodeIndex)->kind()==DocNode::Kind_WhiteSpace
2035 if (nodeIndex<numNodes)
2037 DocNode *n = para->children().at(nodeIndex);
2038 if (mustBeOutsideParagraph(n)) return;
2042 return; // only whitespace at the end!
2047 getParagraphContext(para,isFirst,isLast);
2048 //printf("forceStart first=%d last=%d\n",isFirst,isLast);
2049 if (isFirst && isLast) return;