Orb/Doxygen/src/htmldocvisitor.cpp
changeset 3 d8fccb2cd802
parent 0 42188c7ea2d9
child 4 468f4c8d3d5b
equal deleted inserted replaced
2:932c358ece3e 3:d8fccb2cd802
       
     1 /******************************************************************************
       
     2  *
       
     3  * 
       
     4  *
       
     5  *
       
     6  * Copyright (C) 1997-2008 by Dimitri van Heesch.
       
     7  *
       
     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.
       
    13  *
       
    14  * Documents produced by Doxygen are derivative works derived from the
       
    15  * input used in their production; they are not affected by this license.
       
    16  *
       
    17  */
       
    18 
       
    19 #include <qdir.h>
       
    20 #include "htmldocvisitor.h"
       
    21 #include "docparser.h"
       
    22 #include "language.h"
       
    23 #include "doxygen.h"
       
    24 #include "outputgen.h"
       
    25 #include "dot.h"
       
    26 #include "message.h"
       
    27 #include "config.h"
       
    28 #include "htmlgen.h"
       
    29 #include "parserintf.h"
       
    30 #include "msc.h"
       
    31 #include "util.h"
       
    32 
       
    33 
       
    34 static const int NUM_HTML_LIST_TYPES = 4;
       
    35 static const char types[][NUM_HTML_LIST_TYPES] = {"1", "a", "i", "A"};
       
    36 
       
    37 static QCString convertIndexWordToAnchor(const QString &word)
       
    38 {
       
    39   static char hex[] = "0123456789abcdef";
       
    40   uint i;
       
    41   QCString result;
       
    42   for (i=0;i<word.length();i++)
       
    43   {
       
    44     int c = word.at(i);
       
    45     if (isId(c))
       
    46     {
       
    47       result+=c;
       
    48     }
       
    49     else if (isspace(c))
       
    50     {
       
    51       result+="_";
       
    52     }
       
    53     else
       
    54     {
       
    55       char cs[3];
       
    56       cs[0]=hex[c>>4];
       
    57       cs[1]=hex[c&0xf];
       
    58       cs[2]=0;
       
    59       result+=cs;
       
    60     }
       
    61   }
       
    62   return result;
       
    63 }
       
    64 
       
    65 static bool mustBeOutsideParagraph(DocNode *n)
       
    66 {
       
    67   switch (n->kind())
       
    68   {
       
    69           /* <ul> */
       
    70         case DocNode::Kind_HtmlList:
       
    71         case DocNode::Kind_SimpleList:
       
    72         case DocNode::Kind_AutoList:
       
    73           /* <dl> */
       
    74         case DocNode::Kind_SimpleSect:
       
    75         case DocNode::Kind_ParamSect:
       
    76         case DocNode::Kind_HtmlDescList:
       
    77         case DocNode::Kind_XRefItem:
       
    78           /* <table> */
       
    79         case DocNode::Kind_HtmlTable:
       
    80           /* <h?> */
       
    81         case DocNode::Kind_Section:
       
    82         case DocNode::Kind_HtmlHeader:
       
    83           /* <div> */
       
    84         case DocNode::Kind_Verbatim:
       
    85         case DocNode::Kind_Include:
       
    86         case DocNode::Kind_Image:
       
    87         case DocNode::Kind_SecRefList:
       
    88           /* <hr> */
       
    89         case DocNode::Kind_HorRuler:
       
    90           return TRUE;
       
    91         case DocNode::Kind_StyleChange:
       
    92           return ((DocStyleChange*)n)->style()==DocStyleChange::Preformatted ||
       
    93                  ((DocStyleChange*)n)->style()==DocStyleChange::Div ||
       
    94                  ((DocStyleChange*)n)->style()==DocStyleChange::Center;
       
    95         case DocNode::Kind_Formula:
       
    96           return !((DocFormula*)n)->isInline();
       
    97         default:
       
    98           break;
       
    99   }
       
   100   return FALSE;
       
   101 }
       
   102 
       
   103 
       
   104 
       
   105 static QString htmlAttribsToString(const HtmlAttribList &attribs)
       
   106 {
       
   107   QString result;
       
   108   HtmlAttribListIterator li(attribs);
       
   109   HtmlAttrib *att;
       
   110   for (li.toFirst();(att=li.current());++li)
       
   111   {
       
   112     if (!att->value.isEmpty())  // ignore attribute without values as they
       
   113                                 // are not XHTML compliant
       
   114     {
       
   115       result+=" ";
       
   116       result+=att->name;
       
   117       result+="=\""+convertToXML(att->value)+"\"";
       
   118     }
       
   119   }
       
   120   return result;
       
   121 }
       
   122 
       
   123 //-------------------------------------------------------------------------
       
   124 
       
   125 HtmlDocVisitor::HtmlDocVisitor(QTextStream &t,CodeOutputInterface &ci,
       
   126                                const char *langExt) 
       
   127   : DocVisitor(DocVisitor_Html), m_t(t), m_ci(ci), m_insidePre(FALSE), 
       
   128                                  m_hide(FALSE), m_langExt(langExt)
       
   129 {
       
   130 }
       
   131 
       
   132   //--------------------------------------
       
   133   // visitor functions for leaf nodes
       
   134   //--------------------------------------
       
   135 
       
   136 void HtmlDocVisitor::visit(DocWord *w)
       
   137 {
       
   138   if (m_hide) return;
       
   139   filter(w->word());
       
   140 }
       
   141 
       
   142 void HtmlDocVisitor::visit(DocLinkedWord *w)
       
   143 {
       
   144   if (m_hide) return;
       
   145   startLink(w->ref(),w->file(),w->relPath(),w->anchor(),w->tooltip());
       
   146   filter(w->word());
       
   147   endLink();
       
   148 }
       
   149 
       
   150 void HtmlDocVisitor::visit(DocWhiteSpace *w)
       
   151 {
       
   152   if (m_hide) return;
       
   153   if (m_insidePre)
       
   154   {
       
   155     m_t << w->chars();
       
   156   }
       
   157   else
       
   158   {
       
   159     m_t << " ";
       
   160   }
       
   161 }
       
   162 
       
   163 void HtmlDocVisitor::visit(DocSymbol *s)
       
   164 {
       
   165   if (m_hide) return;
       
   166   switch(s->symbol())
       
   167   {
       
   168     case DocSymbol::BSlash:  m_t << "\\"; break;
       
   169     case DocSymbol::At:      m_t << "@"; break;
       
   170     case DocSymbol::Less:    m_t << "&lt;"; break;
       
   171     case DocSymbol::Greater: m_t << "&gt;"; break;
       
   172     case DocSymbol::Amp:     m_t << "&amp;"; break;
       
   173     case DocSymbol::Dollar:  m_t << "$"; break;
       
   174     case DocSymbol::Hash:    m_t << "#"; break;
       
   175     case DocSymbol::Percent: m_t << "%"; break;
       
   176     case DocSymbol::Copy:    m_t << "&copy;"; break;
       
   177     case DocSymbol::Tm:      m_t << "&trade;"; break;
       
   178     case DocSymbol::Reg:     m_t << "&reg;"; break;
       
   179     case DocSymbol::Apos:    m_t << "'"; break;
       
   180     case DocSymbol::Quot:    m_t << "\""; break;
       
   181     case DocSymbol::Lsquo:   m_t << "&lsquo;"; break;
       
   182     case DocSymbol::Rsquo:   m_t << "&rsquo;"; break;
       
   183     case DocSymbol::Ldquo:   m_t << "&ldquo;"; break;
       
   184     case DocSymbol::Rdquo:   m_t << "&rdquo;"; break;
       
   185     case DocSymbol::Ndash:   m_t << "&ndash;"; break;
       
   186     case DocSymbol::Mdash:   m_t << "&mdash;"; break;
       
   187     case DocSymbol::Uml:     m_t << "&" << s->letter() << "uml;"; break;
       
   188     case DocSymbol::Acute:   m_t << "&" << s->letter() << "acute;"; break;
       
   189     case DocSymbol::Grave:   m_t << "&" << s->letter() << "grave;"; break;
       
   190     case DocSymbol::Circ:    m_t << "&" << s->letter() << "circ;"; break;
       
   191     case DocSymbol::Slash:   m_t << "&" << s->letter() << "slash;"; break;
       
   192     case DocSymbol::Tilde:   m_t << "&" << s->letter() << "tilde;"; break;
       
   193     case DocSymbol::Szlig:   m_t << "&szlig;"; break;
       
   194     case DocSymbol::Cedil:   m_t << "&" << s->letter() << "cedil;"; break;
       
   195     case DocSymbol::Ring:    m_t << "&" << s->letter() << "ring;"; break;
       
   196     case DocSymbol::Nbsp:    m_t << "&nbsp;"; break;
       
   197     case DocSymbol::AElig:   m_t << "&AElig;"; break;
       
   198     case DocSymbol::Aelig:   m_t << "&aelig;"; break;
       
   199     default:
       
   200                              err("Error: unknown symbol found\n");
       
   201   }
       
   202 }
       
   203 
       
   204 void HtmlDocVisitor::visit(DocURL *u)
       
   205 {
       
   206   if (m_hide) return;
       
   207   m_t << "<a href=\"";
       
   208   if (u->isEmail()) m_t << "mailto:";
       
   209   m_t << u->url() << "\">";
       
   210   filter(u->url());
       
   211   m_t << "</a>";
       
   212 }
       
   213 
       
   214 void HtmlDocVisitor::visit(DocLineBreak *)
       
   215 {
       
   216   if (m_hide) return;
       
   217   m_t << "<br/>\n";
       
   218 }
       
   219 
       
   220 void HtmlDocVisitor::visit(DocHorRuler *)
       
   221 {
       
   222   if (m_hide) return;
       
   223   m_t << "<hr/>\n";
       
   224 }
       
   225 
       
   226 void HtmlDocVisitor::visit(DocStyleChange *s)
       
   227 {
       
   228   if (m_hide) return;
       
   229   switch (s->style())
       
   230   {
       
   231     case DocStyleChange::Bold:
       
   232       if (s->enable()) m_t << "<b" << htmlAttribsToString(s->attribs()) << ">";      else m_t << "</b>";
       
   233       break;
       
   234     case DocStyleChange::Italic:
       
   235       if (s->enable()) m_t << "<em" << htmlAttribsToString(s->attribs()) << ">";     else m_t << "</em>";
       
   236       break;
       
   237     case DocStyleChange::Code:
       
   238       if (s->enable()) m_t << "<code" << htmlAttribsToString(s->attribs()) << ">";   else m_t << "</code>";
       
   239       break;
       
   240     case DocStyleChange::Subscript:
       
   241       if (s->enable()) m_t << "<sub" << htmlAttribsToString(s->attribs()) << ">";    else m_t << "</sub>";
       
   242       break;
       
   243     case DocStyleChange::Superscript:
       
   244       if (s->enable()) m_t << "<sup" << htmlAttribsToString(s->attribs()) << ">";    else m_t << "</sup>";
       
   245       break;
       
   246     case DocStyleChange::Center:
       
   247       if (s->enable()) 
       
   248       {
       
   249         forceEndParagraph(s);
       
   250         m_t << "<center" << htmlAttribsToString(s->attribs()) << ">"; 
       
   251       }
       
   252       else 
       
   253       {
       
   254         m_t << "</center>";
       
   255         forceStartParagraph(s);
       
   256       }
       
   257       break;
       
   258     case DocStyleChange::Small:
       
   259       if (s->enable()) m_t << "<small" << htmlAttribsToString(s->attribs()) << ">";  else m_t << "</small>";
       
   260       break;
       
   261     case DocStyleChange::Preformatted:
       
   262       if (s->enable())
       
   263       {
       
   264         forceEndParagraph(s);
       
   265         m_t << "<pre" << htmlAttribsToString(s->attribs()) << ">";
       
   266         m_insidePre=TRUE;
       
   267       }
       
   268       else
       
   269       {
       
   270         m_insidePre=FALSE;
       
   271         m_t << "</pre>";
       
   272         forceStartParagraph(s);
       
   273       }
       
   274       break;
       
   275     case DocStyleChange::Div:
       
   276       if (s->enable()) 
       
   277       {
       
   278         forceEndParagraph(s);
       
   279         m_t << "<div" << htmlAttribsToString(s->attribs()) << ">";  
       
   280       }
       
   281       else 
       
   282       {
       
   283         m_t << "</div>";
       
   284         forceStartParagraph(s);
       
   285       }
       
   286       break;
       
   287     case DocStyleChange::Span:
       
   288       if (s->enable()) m_t << "<span" << htmlAttribsToString(s->attribs()) << ">";  else m_t << "</span>";
       
   289       break;
       
   290 
       
   291   }
       
   292 }
       
   293 
       
   294 
       
   295 void HtmlDocVisitor::visit(DocVerbatim *s)
       
   296 {
       
   297   if (m_hide) return;
       
   298   switch(s->type())
       
   299   {
       
   300     case DocVerbatim::Code: // fall though
       
   301       forceEndParagraph(s);
       
   302       m_t << PREFRAG_START;
       
   303       Doxygen::parserManager->getParser(m_langExt)
       
   304                             ->parseCode(m_ci,s->context(),s->text().latin1(),
       
   305                                         s->isExample(),s->exampleFile());
       
   306       m_t << PREFRAG_END;
       
   307       forceStartParagraph(s);
       
   308       break;
       
   309     case DocVerbatim::Verbatim: 
       
   310       forceEndParagraph(s);
       
   311       m_t << PREFRAG_START;
       
   312       filter(s->text());
       
   313       m_t << PREFRAG_END;
       
   314       forceStartParagraph(s);
       
   315       break;
       
   316     case DocVerbatim::HtmlOnly: 
       
   317       m_t << s->text(); 
       
   318       break;
       
   319     case DocVerbatim::ManOnly: 
       
   320     case DocVerbatim::LatexOnly: 
       
   321     case DocVerbatim::XmlOnly: 
       
   322       /* nothing */ 
       
   323       break;
       
   324 
       
   325     case DocVerbatim::Dot:
       
   326       {
       
   327         static int dotindex = 1;
       
   328         QCString fileName(4096);
       
   329 
       
   330         fileName.sprintf("%s%d%s", 
       
   331             (Config_getString("HTML_OUTPUT")+"/inline_dotgraph_").data(), 
       
   332             dotindex++,
       
   333             ".dot"
       
   334            );
       
   335         QFile file(fileName);
       
   336         if (!file.open(IO_WriteOnly))
       
   337         {
       
   338           err("Could not open file %s for writing\n",fileName.data());
       
   339         }
       
   340         file.writeBlock( s->text(), s->text().length() );
       
   341         file.close();
       
   342 
       
   343         forceEndParagraph(s);
       
   344         m_t << "<div align=\"center\">" << endl;
       
   345         writeDotFile(fileName,s->relPath(),s->context());
       
   346         m_t << "</div>" << endl;
       
   347         forceStartParagraph(s);
       
   348 
       
   349         if (Config_getBool("DOT_CLEANUP")) file.remove();
       
   350       }
       
   351       break;
       
   352     case DocVerbatim::Msc:
       
   353       {
       
   354         static int mscindex = 1;
       
   355         QCString baseName(4096);
       
   356 
       
   357         baseName.sprintf("%s%d", 
       
   358             (Config_getString("HTML_OUTPUT")+"/inline_mscgraph_").data(), 
       
   359             mscindex++
       
   360            );
       
   361         QFile file(baseName+".msc");
       
   362         if (!file.open(IO_WriteOnly))
       
   363         {
       
   364           err("Could not open file %s.msc for writing\n",baseName.data());
       
   365         }
       
   366         QCString text = "msc {";
       
   367         text+=s->text();
       
   368         text+="}";
       
   369         file.writeBlock( text, text.length() );
       
   370         file.close();
       
   371 
       
   372         forceEndParagraph(s);
       
   373         m_t << "<div align=\"center\">" << endl;
       
   374         writeMscFile(baseName,s->relPath(),s->context());
       
   375         m_t << "</div>" << endl;
       
   376         forceStartParagraph(s);
       
   377 
       
   378         if (Config_getBool("DOT_CLEANUP")) file.remove();
       
   379       }
       
   380       break;
       
   381   }
       
   382 }
       
   383 
       
   384 void HtmlDocVisitor::visit(DocAnchor *anc)
       
   385 {
       
   386   if (m_hide) return;
       
   387   m_t << "<a class=\"anchor\" id=\"" << anc->anchor() << "\"></a>";
       
   388 }
       
   389 
       
   390 void HtmlDocVisitor::visit(DocInclude *inc)
       
   391 {
       
   392   if (m_hide) return;
       
   393   switch(inc->type())
       
   394   {
       
   395     case DocInclude::Include: 
       
   396       forceEndParagraph(inc);
       
   397       m_t << PREFRAG_START;
       
   398       Doxygen::parserManager->getParser(inc->extension())
       
   399                             ->parseCode(m_ci,                 
       
   400                                         inc->context(),
       
   401                                         inc->text().latin1(),
       
   402                                         inc->isExample(),
       
   403                                         inc->exampleFile(),
       
   404                                         0,   // fd
       
   405                                         -1,  // startLine
       
   406                                         -1,  // endLine
       
   407                                         TRUE // inlineFragment
       
   408                                        );
       
   409       m_t << PREFRAG_END;
       
   410       forceStartParagraph(inc);
       
   411       break;
       
   412     case DocInclude::IncWithLines:
       
   413       { 
       
   414          forceEndParagraph(inc);
       
   415          m_t << PREFRAG_START;
       
   416          QFileInfo cfi( inc->file() );
       
   417          FileDef fd( cfi.dirPath(), cfi.fileName() );
       
   418          Doxygen::parserManager->getParser(inc->extension())
       
   419                                ->parseCode(m_ci,
       
   420                                            inc->context(),
       
   421                                            inc->text().latin1(),
       
   422                                            inc->isExample(),
       
   423                                            inc->exampleFile(), &fd);
       
   424          m_t << PREFRAG_END;
       
   425          forceStartParagraph(inc);
       
   426       }
       
   427       break;
       
   428     case DocInclude::DontInclude: 
       
   429       break;
       
   430     case DocInclude::HtmlInclude: 
       
   431       m_t << inc->text(); 
       
   432       break;
       
   433     case DocInclude::VerbInclude: 
       
   434       forceEndParagraph(inc);
       
   435       m_t << PREFRAG_START;
       
   436       filter(inc->text());
       
   437       m_t << PREFRAG_END;
       
   438       forceStartParagraph(inc);
       
   439       break;
       
   440   }
       
   441 }
       
   442 
       
   443 void HtmlDocVisitor::visit(DocIncOperator *op)
       
   444 {
       
   445   //printf("DocIncOperator: type=%d first=%d, last=%d text=`%s'\n",
       
   446   //    op->type(),op->isFirst(),op->isLast(),op->text().data());
       
   447   if (op->isFirst()) 
       
   448   {
       
   449     if (!m_hide) m_t << PREFRAG_START;
       
   450     pushEnabled();
       
   451     m_hide=TRUE;
       
   452   }
       
   453   if (op->type()!=DocIncOperator::Skip) 
       
   454   {
       
   455     popEnabled();
       
   456     if (!m_hide) 
       
   457     {
       
   458       Doxygen::parserManager->getParser(m_langExt)
       
   459                             ->parseCode(m_ci,op->context(),
       
   460                                 op->text().latin1(),op->isExample(),
       
   461                                 op->exampleFile());
       
   462     }
       
   463     pushEnabled();
       
   464     m_hide=TRUE;
       
   465   }
       
   466   if (op->isLast())  
       
   467   {
       
   468     popEnabled();
       
   469     if (!m_hide) m_t << PREFRAG_END;
       
   470   }
       
   471   else
       
   472   {
       
   473     if (!m_hide) m_t << endl;
       
   474   }
       
   475 }
       
   476 
       
   477 void HtmlDocVisitor::visit(DocFormula *f)
       
   478 {
       
   479   if (m_hide) return;
       
   480   bool bDisplay = !f->isInline();
       
   481   if (bDisplay) 
       
   482   {
       
   483     forceEndParagraph(f);
       
   484     m_t << "<p class=\"formulaDsp\">" << endl;
       
   485   }
       
   486   m_t << "<img class=\"formula" 
       
   487       << (bDisplay ? "Dsp" : "Inl");
       
   488   m_t << "\" alt=\"";
       
   489   filterQuotedCdataAttr(f->text());
       
   490   m_t << "\"";
       
   491   /// @todo cache image dimensions on formula generation and give height/width
       
   492   /// for faster preloading and better rendering of the page
       
   493   m_t << " src=\"" << f->relPath() << f->name() << ".png\"/>";
       
   494   if (bDisplay)
       
   495   {
       
   496     m_t << endl << "</p>" << endl;
       
   497     forceStartParagraph(f);
       
   498   }
       
   499 }
       
   500 
       
   501 void HtmlDocVisitor::visit(DocIndexEntry *e)
       
   502 {
       
   503   QCString anchor = convertIndexWordToAnchor(e->entry());
       
   504   if (e->member()) 
       
   505   {
       
   506     anchor.prepend(e->member()->anchor()+"_");
       
   507   }
       
   508   m_t << "<a name=\"" << anchor << "\"></a>";
       
   509   //printf("*** DocIndexEntry: word='%s' scope='%s' member='%s'\n",
       
   510   //       e->entry().data(),
       
   511   //       e->scope()  ? e->scope()->name().data()  : "<null>",
       
   512   //       e->member() ? e->member()->name().data() : "<null>"
       
   513   //      );
       
   514   Doxygen::indexList.addIndexItem(e->scope(),e->member(),anchor,e->entry());
       
   515 }
       
   516 
       
   517 void HtmlDocVisitor::visit(DocSimpleSectSep *)
       
   518 {
       
   519   m_t << "</dd>" << endl;
       
   520   m_t << "<dd>" << endl;
       
   521 }
       
   522 
       
   523 //--------------------------------------
       
   524 // visitor functions for compound nodes
       
   525 //--------------------------------------
       
   526 
       
   527 
       
   528 void HtmlDocVisitor::visitPre(DocAutoList *l)
       
   529 {
       
   530   if (m_hide) return;
       
   531   forceEndParagraph(l);
       
   532   if (l->isEnumList())
       
   533   {
       
   534     //
       
   535     // Do list type based on depth:
       
   536     // 1.
       
   537     //   a.
       
   538     //     i.
       
   539     //       A. 
       
   540     //         1. (repeat)...
       
   541     //
       
   542     m_t << "<ol type=\"" << types[l->depth() % NUM_HTML_LIST_TYPES] << "\">";
       
   543   }
       
   544   else
       
   545   {
       
   546     m_t << "<ul>";
       
   547   }
       
   548   if (!l->isPreformatted()) m_t << "\n";
       
   549 }
       
   550 
       
   551 void HtmlDocVisitor::visitPost(DocAutoList *l)
       
   552 {
       
   553   if (m_hide) return;
       
   554   if (l->isEnumList())
       
   555   {
       
   556     m_t << "</ol>";
       
   557   }
       
   558   else
       
   559   {
       
   560     m_t << "</ul>";
       
   561   }
       
   562   if (!l->isPreformatted()) m_t << "\n";
       
   563   forceStartParagraph(l);
       
   564 }
       
   565 
       
   566 void HtmlDocVisitor::visitPre(DocAutoListItem *)
       
   567 {
       
   568   if (m_hide) return;
       
   569   m_t << "<li>";
       
   570 }
       
   571 
       
   572 void HtmlDocVisitor::visitPost(DocAutoListItem *li) 
       
   573 {
       
   574   if (m_hide) return;
       
   575   m_t << "</li>";
       
   576   if (!li->isPreformatted()) m_t << "\n";
       
   577 }
       
   578 
       
   579 template<class T> 
       
   580 bool isFirstChildNode(T *parent, DocNode *node)
       
   581 {
       
   582    return parent->children().getFirst()==node;
       
   583 }
       
   584 
       
   585 template<class T> 
       
   586 bool isLastChildNode(T *parent, DocNode *node)
       
   587 {
       
   588    return parent->children().getLast()==node;
       
   589 }
       
   590 
       
   591 bool isSeparatedParagraph(DocSimpleSect *parent,DocPara *par)
       
   592 {
       
   593   QList<DocNode> nodes = parent->children();
       
   594   int i = nodes.findRef(par);
       
   595   if (i==-1) return FALSE;
       
   596   int count = parent->children().count();
       
   597   if (count>1 && i==0)
       
   598   {
       
   599     if (nodes.at(i+1)->kind()==DocNode::Kind_SimpleSectSep)
       
   600     {
       
   601       return TRUE;
       
   602     }
       
   603   }
       
   604   else if (count>1 && i==count-1)
       
   605   {
       
   606     if (nodes.at(i-1)->kind()==DocNode::Kind_SimpleSectSep)
       
   607     {
       
   608       return TRUE;
       
   609     }
       
   610   }
       
   611   else if (count>2 && i>0 && i<count-1)
       
   612   {
       
   613     if (nodes.at(i-1)->kind()==DocNode::Kind_SimpleSectSep &&
       
   614         nodes.at(i+1)->kind()==DocNode::Kind_SimpleSectSep)
       
   615     {
       
   616       return TRUE;
       
   617     }
       
   618   }
       
   619   return FALSE;
       
   620 }
       
   621 
       
   622 static int getParagraphContext(DocPara *p,bool &isFirst,bool &isLast)
       
   623 {
       
   624   int t=0;
       
   625   isFirst=FALSE;
       
   626   isLast=FALSE;
       
   627   if (p && p->parent())
       
   628   {
       
   629     switch (p->parent()->kind()) 
       
   630     {
       
   631       case DocNode::Kind_AutoListItem:
       
   632         isFirst=TRUE;
       
   633         isLast =TRUE;
       
   634         if (isFirst) t=1;
       
   635         if (isLast)  t=3;
       
   636         break;
       
   637       case DocNode::Kind_SimpleListItem:
       
   638         isFirst=TRUE;
       
   639         isLast =TRUE;
       
   640         if (isFirst) t=1;
       
   641         if (isLast)  t=3;
       
   642         break;
       
   643       case DocNode::Kind_HtmlListItem:
       
   644         isFirst=isFirstChildNode((DocHtmlListItem*)p->parent(),p);
       
   645         isLast =isLastChildNode ((DocHtmlListItem*)p->parent(),p);
       
   646         if (isFirst) t=1;
       
   647         if (isLast)  t=3;
       
   648         break;
       
   649       case DocNode::Kind_SecRefItem:
       
   650         isFirst=isFirstChildNode((DocSecRefItem*)p->parent(),p);
       
   651         isLast =isLastChildNode ((DocSecRefItem*)p->parent(),p);
       
   652         if (isFirst) t=1;
       
   653         if (isLast)  t=3;
       
   654         break;
       
   655       case DocNode::Kind_HtmlDescData:
       
   656         isFirst=isFirstChildNode((DocHtmlDescData*)p->parent(),p);
       
   657         isLast =isLastChildNode ((DocHtmlDescData*)p->parent(),p);
       
   658         if (isFirst) t=2;
       
   659         if (isLast)  t=4;
       
   660         break;
       
   661       case DocNode::Kind_XRefItem:
       
   662         isFirst=isFirstChildNode((DocXRefItem*)p->parent(),p);
       
   663         isLast =isLastChildNode ((DocXRefItem*)p->parent(),p);
       
   664         if (isFirst) t=2;
       
   665         if (isLast)  t=4;
       
   666         break;
       
   667       case DocNode::Kind_HtmlCell:
       
   668         isFirst=isFirstChildNode((DocHtmlCell*)p->parent(),p);
       
   669         isLast =isLastChildNode ((DocHtmlCell*)p->parent(),p);
       
   670         if (isFirst) t=5;
       
   671         if (isLast)  t=6;
       
   672         break;
       
   673       case DocNode::Kind_SimpleSect:
       
   674         isFirst=isFirstChildNode((DocSimpleSect*)p->parent(),p);
       
   675         isLast =isLastChildNode ((DocSimpleSect*)p->parent(),p);
       
   676         if (isFirst) t=2;
       
   677         if (isLast)  t=4;
       
   678         if (isSeparatedParagraph((DocSimpleSect*)p->parent(),p))
       
   679           // if the paragraph is enclosed with separators it will
       
   680           // be included in <dd>..</dd> so avoid addition paragraph
       
   681           // markers
       
   682         {
       
   683           isFirst=isLast=TRUE;
       
   684         }
       
   685         break;
       
   686       default:
       
   687         break;
       
   688     }
       
   689     //printf("para=%p parent()->kind=%d isFirst=%d isLast=%d t=%d\n",
       
   690     //    p,p->parent()->kind(),isFirst,isLast,t);
       
   691   }
       
   692   return t;
       
   693 }
       
   694 
       
   695 void HtmlDocVisitor::visitPre(DocPara *p) 
       
   696 {
       
   697   if (m_hide) return;
       
   698 
       
   699   //printf("Processing docpara with parent of kind %d\n",
       
   700   //       p->parent() ? p->parent()->kind() : -1);
       
   701 
       
   702   bool needsTag = FALSE;
       
   703   if (p && p->parent()) 
       
   704   {
       
   705     switch (p->parent()->kind()) 
       
   706     {
       
   707       case DocNode::Kind_Section:
       
   708       case DocNode::Kind_HtmlListItem:
       
   709       case DocNode::Kind_HtmlDescData:
       
   710       case DocNode::Kind_HtmlCell:
       
   711       case DocNode::Kind_SimpleListItem:
       
   712       case DocNode::Kind_AutoListItem:
       
   713       case DocNode::Kind_SimpleSect:
       
   714       case DocNode::Kind_XRefItem:
       
   715         needsTag = TRUE;
       
   716         break;
       
   717       case DocNode::Kind_Root:
       
   718         needsTag = !((DocRoot*)p->parent())->singleLine();
       
   719         break;
       
   720       default:
       
   721         needsTag = FALSE;
       
   722     }
       
   723   }
       
   724 
       
   725   // if the first element of a paragraph is something that should be outside of
       
   726   // the paragraph (<ul>,<dl>,<table>,..) then that will already started the 
       
   727   // paragraph and we don't need to do it here
       
   728   uint nodeIndex = 0;
       
   729   if (p && nodeIndex<p->children().count())
       
   730   {
       
   731     while (nodeIndex<p->children().count() && 
       
   732            p->children().at(nodeIndex)->kind()==DocNode::Kind_WhiteSpace)
       
   733     {
       
   734       nodeIndex++;
       
   735     }
       
   736     if (nodeIndex<p->children().count())
       
   737     {
       
   738       DocNode *n = p->children().at(nodeIndex);
       
   739       if (mustBeOutsideParagraph(n))
       
   740       {
       
   741         needsTag = FALSE;
       
   742       }
       
   743     }
       
   744   }
       
   745 
       
   746   // check if this paragraph is the first or last child of a <li> or <dd>.
       
   747   // this allows us to mark the tag with a special class so we can
       
   748   // fix the otherwise ugly spacing.
       
   749   int t;
       
   750   static const char *contexts[7] = 
       
   751   { "",                     // 0
       
   752     " class=\"startli\"",   // 1
       
   753     " class=\"startdd\"",   // 2
       
   754     " class=\"endli\"",     // 3
       
   755     " class=\"enddd\"",     // 4
       
   756     " class=\"starttd\"",   // 5
       
   757     " class=\"endtd\""      // 6
       
   758   };
       
   759   bool isFirst;
       
   760   bool isLast;
       
   761   t = getParagraphContext(p,isFirst,isLast);
       
   762   //printf("startPara first=%d last=%d\n",isFirst,isLast);
       
   763   if (isFirst && isLast) needsTag=FALSE;
       
   764 
       
   765   // write the paragraph tag (if needed)
       
   766   if (needsTag) m_t << "<p" << contexts[t] << ">";
       
   767 }
       
   768 
       
   769 void HtmlDocVisitor::visitPost(DocPara *p)
       
   770 {
       
   771 //  if (m_hide) return;
       
   772 //  if (!p->isLast() &&            // omit <p> for last paragraph
       
   773 //      !(p->parent() &&           // and for parameter sections
       
   774 //        p->parent()->kind()==DocNode::Kind_ParamSect
       
   775 //       ) 
       
   776 //     ) 
       
   777 //  {
       
   778 //    m_t << "<p>\n";
       
   779 //  }
       
   780 
       
   781   bool needsTag = FALSE;
       
   782   if (p && p->parent()) 
       
   783   {
       
   784     switch (p->parent()->kind()) 
       
   785     {
       
   786       case DocNode::Kind_Section:
       
   787       case DocNode::Kind_HtmlListItem:
       
   788       case DocNode::Kind_HtmlDescData:
       
   789       case DocNode::Kind_HtmlCell:
       
   790       case DocNode::Kind_SimpleListItem:
       
   791       case DocNode::Kind_AutoListItem:
       
   792       case DocNode::Kind_SimpleSect:
       
   793       case DocNode::Kind_XRefItem:
       
   794         needsTag = TRUE;
       
   795         break;
       
   796       case DocNode::Kind_Root:
       
   797         needsTag = !((DocRoot*)p->parent())->singleLine();
       
   798         break;
       
   799       default:
       
   800         needsTag = FALSE;
       
   801     }
       
   802   }
       
   803 
       
   804   QCString context;
       
   805   // if the last element of a paragraph is something that should be outside of
       
   806   // the paragraph (<ul>,<dl>,<table>) then that will already have ended the 
       
   807   // paragraph and we don't need to do it here
       
   808   int nodeIndex = p->children().count()-1;
       
   809   if (p && nodeIndex>=0)
       
   810   {
       
   811     while (nodeIndex>=0 && p->children().at(nodeIndex)->kind()==DocNode::Kind_WhiteSpace)
       
   812     {
       
   813       nodeIndex--;
       
   814     }
       
   815     if (nodeIndex>=0)
       
   816     {
       
   817       DocNode *n = p->children().at(nodeIndex);
       
   818       if (mustBeOutsideParagraph(n))
       
   819       {
       
   820         needsTag = FALSE;
       
   821       }
       
   822     }
       
   823   }
       
   824 
       
   825   bool isFirst;
       
   826   bool isLast;
       
   827   getParagraphContext(p,isFirst,isLast);
       
   828   //printf("endPara first=%d last=%d\n",isFirst,isLast);
       
   829   if (isFirst && isLast) needsTag=FALSE;
       
   830 
       
   831   if (needsTag) m_t << "</p>\n";
       
   832 
       
   833 }
       
   834 
       
   835 void HtmlDocVisitor::visitPre(DocRoot *)
       
   836 {
       
   837 }
       
   838 
       
   839 void HtmlDocVisitor::visitPost(DocRoot *)
       
   840 {
       
   841 }
       
   842 
       
   843 void HtmlDocVisitor::visitPre(DocSimpleSect *s)
       
   844 {
       
   845   if (m_hide) return;
       
   846   forceEndParagraph(s);
       
   847   m_t << "<dl class=\"" << s->typeString() << "\"><dt><b>";
       
   848   switch(s->type())
       
   849   {
       
   850     case DocSimpleSect::See: 
       
   851       m_t << theTranslator->trSeeAlso(); break;
       
   852     case DocSimpleSect::Return: 
       
   853       m_t << theTranslator->trReturns(); break;
       
   854     case DocSimpleSect::Author: 
       
   855       m_t << theTranslator->trAuthor(TRUE,TRUE); break;
       
   856     case DocSimpleSect::Authors: 
       
   857       m_t << theTranslator->trAuthor(TRUE,FALSE); break;
       
   858     case DocSimpleSect::Version: 
       
   859       m_t << theTranslator->trVersion(); break;
       
   860     case DocSimpleSect::Since: 
       
   861       m_t << theTranslator->trSince(); break;
       
   862     case DocSimpleSect::Date: 
       
   863       m_t << theTranslator->trDate(); break;
       
   864     case DocSimpleSect::Note: 
       
   865       m_t << theTranslator->trNote(); break;
       
   866     case DocSimpleSect::Warning:
       
   867       m_t << theTranslator->trWarning(); break;
       
   868     case DocSimpleSect::Pre:
       
   869       m_t << theTranslator->trPrecondition(); break;
       
   870     case DocSimpleSect::Post:
       
   871       m_t << theTranslator->trPostcondition(); break;
       
   872     case DocSimpleSect::Invar:
       
   873       m_t << theTranslator->trInvariant(); break;
       
   874     case DocSimpleSect::Remark:
       
   875       m_t << theTranslator->trRemarks(); break;
       
   876     case DocSimpleSect::Attention:
       
   877       m_t << theTranslator->trAttention(); break;
       
   878     case DocSimpleSect::User: break;
       
   879     case DocSimpleSect::Rcs: break;
       
   880     case DocSimpleSect::Unknown:  break;
       
   881   }
       
   882 
       
   883   // special case 1: user defined title
       
   884   if (s->type()!=DocSimpleSect::User && s->type()!=DocSimpleSect::Rcs)
       
   885   {
       
   886     m_t << ":</b></dt><dd>";
       
   887   }
       
   888 }
       
   889 
       
   890 void HtmlDocVisitor::visitPost(DocSimpleSect *s)
       
   891 {
       
   892   if (m_hide) return;
       
   893   m_t << "</dd></dl>\n";
       
   894   forceStartParagraph(s);
       
   895 }
       
   896 
       
   897 void HtmlDocVisitor::visitPre(DocTitle *)
       
   898 {
       
   899 }
       
   900 
       
   901 void HtmlDocVisitor::visitPost(DocTitle *)
       
   902 {
       
   903   if (m_hide) return;
       
   904   m_t << "</b></dt><dd>";
       
   905 }
       
   906 
       
   907 void HtmlDocVisitor::visitPre(DocSimpleList *sl)
       
   908 {
       
   909   if (m_hide) return;
       
   910   forceEndParagraph(sl);
       
   911   m_t << "<ul>";
       
   912   if (!sl->isPreformatted()) m_t << "\n";
       
   913 
       
   914 }
       
   915 
       
   916 void HtmlDocVisitor::visitPost(DocSimpleList *sl)
       
   917 {
       
   918   if (m_hide) return;
       
   919   m_t << "</ul>";
       
   920   if (!sl->isPreformatted()) m_t << "\n";
       
   921   forceStartParagraph(sl);
       
   922 }
       
   923 
       
   924 void HtmlDocVisitor::visitPre(DocSimpleListItem *)
       
   925 {
       
   926   if (m_hide) return;
       
   927   m_t << "<li>";
       
   928 }
       
   929 
       
   930 void HtmlDocVisitor::visitPost(DocSimpleListItem *li) 
       
   931 {
       
   932   if (m_hide) return;
       
   933   m_t << "</li>";
       
   934   if (!li->isPreformatted()) m_t << "\n";
       
   935 }
       
   936 
       
   937 void HtmlDocVisitor::visitPre(DocSection *s)
       
   938 {
       
   939   if (m_hide) return;
       
   940   forceEndParagraph(s);
       
   941   m_t << "<h" << s->level()+1 << ">";
       
   942   m_t << "<a class=\"anchor\" id=\"" << s->anchor();
       
   943   m_t << "\">" << endl;
       
   944   filter(convertCharEntitiesToUTF8(s->title().data()));
       
   945   m_t << "</a></h" << s->level()+1 << ">\n";
       
   946 }
       
   947 
       
   948 void HtmlDocVisitor::visitPost(DocSection *s) 
       
   949 {
       
   950   forceStartParagraph(s);
       
   951 }
       
   952 
       
   953 void HtmlDocVisitor::visitPre(DocHtmlList *s)
       
   954 {
       
   955   if (m_hide) return;
       
   956   forceEndParagraph(s);
       
   957   if (s->type()==DocHtmlList::Ordered) 
       
   958   {
       
   959     m_t << "<ol" << htmlAttribsToString(s->attribs()) << ">\n"; 
       
   960   }
       
   961   else 
       
   962   {
       
   963     m_t << "<ul" << htmlAttribsToString(s->attribs()) << ">\n";
       
   964   }
       
   965 }
       
   966 
       
   967 void HtmlDocVisitor::visitPost(DocHtmlList *s) 
       
   968 {
       
   969   if (m_hide) return;
       
   970   if (s->type()==DocHtmlList::Ordered) 
       
   971   {
       
   972     m_t << "</ol>"; 
       
   973   }
       
   974   else
       
   975   { 
       
   976     m_t << "</ul>";
       
   977   }
       
   978   if (!s->isPreformatted()) m_t << "\n";
       
   979   forceStartParagraph(s);
       
   980 }
       
   981 
       
   982 void HtmlDocVisitor::visitPre(DocHtmlListItem *i)
       
   983 {
       
   984   if (m_hide) return;
       
   985   m_t << "<li" << htmlAttribsToString(i->attribs()) << ">";
       
   986   if (!i->isPreformatted()) m_t << "\n";
       
   987 }
       
   988 
       
   989 void HtmlDocVisitor::visitPost(DocHtmlListItem *) 
       
   990 {
       
   991   if (m_hide) return;
       
   992   m_t << "</li>\n";
       
   993 }
       
   994 
       
   995 void HtmlDocVisitor::visitPre(DocHtmlDescList *dl)
       
   996 {
       
   997   if (m_hide) return;
       
   998   forceEndParagraph(dl);
       
   999   m_t << "<dl" << htmlAttribsToString(dl->attribs()) << ">\n";
       
  1000 }
       
  1001 
       
  1002 void HtmlDocVisitor::visitPost(DocHtmlDescList *dl) 
       
  1003 {
       
  1004   if (m_hide) return;
       
  1005   m_t << "</dl>\n";
       
  1006   forceStartParagraph(dl);
       
  1007 }
       
  1008 
       
  1009 void HtmlDocVisitor::visitPre(DocHtmlDescTitle *dt)
       
  1010 {
       
  1011   if (m_hide) return;
       
  1012   m_t << "<dt" << htmlAttribsToString(dt->attribs()) << ">";
       
  1013 }
       
  1014 
       
  1015 void HtmlDocVisitor::visitPost(DocHtmlDescTitle *) 
       
  1016 {
       
  1017   if (m_hide) return;
       
  1018   m_t << "</dt>\n";
       
  1019 }
       
  1020 
       
  1021 void HtmlDocVisitor::visitPre(DocHtmlDescData *dd)
       
  1022 {
       
  1023   if (m_hide) return;
       
  1024   m_t << "<dd" << htmlAttribsToString(dd->attribs()) << ">";
       
  1025 }
       
  1026 
       
  1027 void HtmlDocVisitor::visitPost(DocHtmlDescData *) 
       
  1028 {
       
  1029   if (m_hide) return;
       
  1030   m_t << "</dd>\n";
       
  1031 }
       
  1032 
       
  1033 void HtmlDocVisitor::visitPre(DocHtmlTable *t)
       
  1034 {
       
  1035   if (m_hide) return;
       
  1036   //bool hasBorder      = FALSE;
       
  1037   //bool hasCellSpacing = FALSE;
       
  1038   //bool hasCellPadding = FALSE;
       
  1039 
       
  1040   forceEndParagraph(t);
       
  1041 
       
  1042   //HtmlAttribListIterator li(t->attribs());
       
  1043   //HtmlAttrib *att;
       
  1044   //for (li.toFirst();(att=li.current());++li)
       
  1045   //{
       
  1046   // if      (att->name=="border")      hasBorder=TRUE;
       
  1047   //  else if (att->name=="cellspacing") hasCellSpacing=TRUE;
       
  1048   //  else if (att->name=="cellpadding") hasCellPadding=TRUE;
       
  1049   //}
       
  1050   m_t << "<table class=\"doxtable\"" << htmlAttribsToString(t->attribs());
       
  1051   //if (!hasBorder)      m_t << " border=\"1\"";
       
  1052   //if (!hasCellSpacing) m_t << " cellspacing=\"3\"";
       
  1053   //if (!hasCellPadding) m_t << " cellpadding=\"3\"";
       
  1054   m_t << ">\n";
       
  1055 }
       
  1056 
       
  1057 void HtmlDocVisitor::visitPost(DocHtmlTable *t) 
       
  1058 {
       
  1059   if (m_hide) return;
       
  1060   m_t << "</table>\n";
       
  1061   forceStartParagraph(t);
       
  1062 }
       
  1063 
       
  1064 void HtmlDocVisitor::visitPre(DocHtmlRow *tr)
       
  1065 {
       
  1066   if (m_hide) return;
       
  1067   m_t << "<tr" << htmlAttribsToString(tr->attribs()) << ">\n";
       
  1068 }
       
  1069 
       
  1070 void HtmlDocVisitor::visitPost(DocHtmlRow *) 
       
  1071 {
       
  1072   if (m_hide) return;
       
  1073   m_t << "</tr>\n";
       
  1074 }
       
  1075 
       
  1076 void HtmlDocVisitor::visitPre(DocHtmlCell *c)
       
  1077 {
       
  1078   if (m_hide) return;
       
  1079   if (c->isHeading()) 
       
  1080   {
       
  1081     m_t << "<th" << htmlAttribsToString(c->attribs()) << ">"; 
       
  1082   }
       
  1083   else 
       
  1084   {
       
  1085     m_t << "<td" << htmlAttribsToString(c->attribs()) << ">";
       
  1086   }
       
  1087 }
       
  1088 
       
  1089 void HtmlDocVisitor::visitPost(DocHtmlCell *c) 
       
  1090 {
       
  1091   if (m_hide) return;
       
  1092   if (c->isHeading()) m_t << "</th>"; else m_t << "</td>";
       
  1093 }
       
  1094 
       
  1095 void HtmlDocVisitor::visitPre(DocHtmlCaption *c)
       
  1096 {
       
  1097   if (m_hide) return;
       
  1098   bool hasAlign      = FALSE;
       
  1099   HtmlAttribListIterator li(c->attribs());
       
  1100   HtmlAttrib *att;
       
  1101   for (li.toFirst();(att=li.current());++li)
       
  1102   {
       
  1103     if (att->name=="align") hasAlign=TRUE;
       
  1104   }
       
  1105   m_t << "<caption" << htmlAttribsToString(c->attribs());
       
  1106   if (!hasAlign) m_t << " align=\"bottom\"";
       
  1107   m_t << ">";
       
  1108 }
       
  1109 
       
  1110 void HtmlDocVisitor::visitPost(DocHtmlCaption *) 
       
  1111 {
       
  1112   if (m_hide) return;
       
  1113   m_t << "</caption>\n";
       
  1114 }
       
  1115 
       
  1116 void HtmlDocVisitor::visitPre(DocInternal *)
       
  1117 {
       
  1118   if (m_hide) return;
       
  1119   m_t << "<p><b>" << theTranslator->trForInternalUseOnly() << "</b></p>" << endl;
       
  1120   m_t << "<p>" << endl;
       
  1121 }
       
  1122 
       
  1123 void HtmlDocVisitor::visitPost(DocInternal *) 
       
  1124 {
       
  1125   if (m_hide) return;
       
  1126   m_t << "</p>" << endl;
       
  1127 }
       
  1128 
       
  1129 void HtmlDocVisitor::visitPre(DocHRef *href)
       
  1130 {
       
  1131   if (m_hide) return;
       
  1132   m_t << "<a href=\"" << convertToXML(href->url())  << "\""
       
  1133       << htmlAttribsToString(href->attribs()) << ">";
       
  1134 }
       
  1135 
       
  1136 void HtmlDocVisitor::visitPost(DocHRef *) 
       
  1137 {
       
  1138   if (m_hide) return;
       
  1139   m_t << "</a>";
       
  1140 }
       
  1141 
       
  1142 void HtmlDocVisitor::visitPre(DocHtmlHeader *header)
       
  1143 {
       
  1144   if (m_hide) return;
       
  1145   forceEndParagraph(header);
       
  1146   m_t << "<h" << header->level() 
       
  1147       << htmlAttribsToString(header->attribs()) << ">";
       
  1148 }
       
  1149 
       
  1150 void HtmlDocVisitor::visitPost(DocHtmlHeader *header) 
       
  1151 {
       
  1152   if (m_hide) return;
       
  1153   m_t << "</h" << header->level() << ">\n";
       
  1154   forceStartParagraph(header);
       
  1155 }
       
  1156 
       
  1157 void HtmlDocVisitor::visitPre(DocImage *img)
       
  1158 {
       
  1159   if (img->type()==DocImage::Html)
       
  1160   {
       
  1161     forceEndParagraph(img);
       
  1162     if (m_hide) return;
       
  1163     QString baseName=img->name();
       
  1164     int i;
       
  1165     if ((i=baseName.findRev('/'))!=-1 || (i=baseName.findRev('\\'))!=-1)
       
  1166     {
       
  1167       baseName=baseName.right(baseName.length()-i-1);
       
  1168     }
       
  1169     m_t << "<div align=\"center\">" << endl;
       
  1170     m_t << "<img src=\"" << img->relPath() << img->name() << "\" alt=\"" 
       
  1171       << baseName << "\"" << "/>" << endl;
       
  1172     if (img->hasCaption())
       
  1173     {
       
  1174       m_t << "<p><strong>";
       
  1175     }
       
  1176   }
       
  1177   else // other format -> skip
       
  1178   {
       
  1179     pushEnabled();
       
  1180     m_hide=TRUE;
       
  1181   }
       
  1182 }
       
  1183 
       
  1184 void HtmlDocVisitor::visitPost(DocImage *img) 
       
  1185 {
       
  1186   if (img->type()==DocImage::Html)
       
  1187   {
       
  1188     if (m_hide) return;
       
  1189     if (img->hasCaption())
       
  1190     {
       
  1191       m_t << "</strong></p>";
       
  1192     }
       
  1193     m_t << "</div>" << endl;
       
  1194     forceStartParagraph(img);
       
  1195   }
       
  1196   else // other format
       
  1197   {
       
  1198     popEnabled();
       
  1199   }
       
  1200 }
       
  1201 
       
  1202 void HtmlDocVisitor::visitPre(DocDotFile *df)
       
  1203 {
       
  1204   if (m_hide) return;
       
  1205   writeDotFile(df->file(),df->relPath(),df->context());
       
  1206   m_t << "<div align=\"center\">" << endl;
       
  1207   if (df->hasCaption())
       
  1208   { 
       
  1209     m_t << "<p><strong>";
       
  1210   }
       
  1211 }
       
  1212 
       
  1213 void HtmlDocVisitor::visitPost(DocDotFile *df) 
       
  1214 {
       
  1215   if (m_hide) return;
       
  1216   if (df->hasCaption())
       
  1217   {
       
  1218     m_t << "</strong></p>" << endl;
       
  1219   }
       
  1220   m_t << "</div>" << endl;
       
  1221 }
       
  1222 
       
  1223 void HtmlDocVisitor::visitPre(DocLink *lnk)
       
  1224 {
       
  1225   if (m_hide) return;
       
  1226   startLink(lnk->ref(),lnk->file(),lnk->relPath(),lnk->anchor());
       
  1227 }
       
  1228 
       
  1229 void HtmlDocVisitor::visitPost(DocLink *) 
       
  1230 {
       
  1231   if (m_hide) return;
       
  1232   endLink();
       
  1233 }
       
  1234 
       
  1235 void HtmlDocVisitor::visitPre(DocRef *ref)
       
  1236 {
       
  1237   if (m_hide) return;
       
  1238   if (!ref->file().isEmpty()) 
       
  1239   {
       
  1240     startLink(ref->ref(),ref->file(),ref->relPath(),ref->anchor());
       
  1241   }
       
  1242   if (!ref->hasLinkText()) filter(ref->targetTitle());
       
  1243 }
       
  1244 
       
  1245 void HtmlDocVisitor::visitPost(DocRef *ref) 
       
  1246 {
       
  1247   if (m_hide) return;
       
  1248   if (!ref->file().isEmpty()) endLink();
       
  1249   //m_t << " ";
       
  1250 }
       
  1251 
       
  1252 void HtmlDocVisitor::visitPre(DocSecRefItem *ref)
       
  1253 {
       
  1254   if (m_hide) return;
       
  1255   QString refName=ref->file();
       
  1256   if (refName.right(Doxygen::htmlFileExtension.length())!=
       
  1257       QString(Doxygen::htmlFileExtension))
       
  1258   {
       
  1259     refName+=Doxygen::htmlFileExtension;
       
  1260   }
       
  1261   m_t << "<li><a href=\"" << refName << "#" << ref->anchor() << "\">";
       
  1262 
       
  1263 }
       
  1264 
       
  1265 void HtmlDocVisitor::visitPost(DocSecRefItem *) 
       
  1266 {
       
  1267   if (m_hide) return;
       
  1268   m_t << "</a></li>\n";
       
  1269 }
       
  1270 
       
  1271 void HtmlDocVisitor::visitPre(DocSecRefList *s)
       
  1272 {
       
  1273   if (m_hide) return;
       
  1274   forceEndParagraph(s);
       
  1275   m_t << "<div class=\"multicol\">" << endl;
       
  1276   m_t << "<ul>" << endl;
       
  1277 }
       
  1278 
       
  1279 void HtmlDocVisitor::visitPost(DocSecRefList *s) 
       
  1280 {
       
  1281   if (m_hide) return;
       
  1282   m_t << "</ul>" << endl;
       
  1283   m_t << "</div>" << endl;
       
  1284   forceStartParagraph(s);
       
  1285 }
       
  1286 
       
  1287 //void HtmlDocVisitor::visitPre(DocLanguage *l)
       
  1288 //{
       
  1289 //  QString langId = Config_getEnum("OUTPUT_LANGUAGE");
       
  1290 //  if (l->id().lower()!=langId.lower())
       
  1291 //  {
       
  1292 //    pushEnabled();
       
  1293 //    m_hide = TRUE;
       
  1294 //  }
       
  1295 //}
       
  1296 //
       
  1297 //void HtmlDocVisitor::visitPost(DocLanguage *l) 
       
  1298 //{
       
  1299 //  QString langId = Config_getEnum("OUTPUT_LANGUAGE");
       
  1300 //  if (l->id().lower()!=langId.lower())
       
  1301 //  {
       
  1302 //    popEnabled();
       
  1303 //  }
       
  1304 //}
       
  1305 
       
  1306 void HtmlDocVisitor::visitPre(DocParamSect *s)
       
  1307 {
       
  1308   if (m_hide) return;
       
  1309   forceEndParagraph(s);
       
  1310   m_t << "<dl><dt><b>";
       
  1311   switch(s->type())
       
  1312   {
       
  1313     case DocParamSect::Param: 
       
  1314       m_t << theTranslator->trParameters(); break;
       
  1315     case DocParamSect::RetVal: 
       
  1316       m_t << theTranslator->trReturnValues(); break;
       
  1317     case DocParamSect::Exception: 
       
  1318       m_t << theTranslator->trExceptions(); break;
       
  1319     case DocParamSect::TemplateParam: 
       
  1320       /* TODO: add this 
       
  1321       m_t << theTranslator->trTemplateParam(); break;
       
  1322       */
       
  1323       m_t << "Template Parameters"; break;
       
  1324     default:
       
  1325       ASSERT(0);
       
  1326   }
       
  1327   m_t << ":";
       
  1328   m_t << "</b></dt><dd>" << endl;
       
  1329   m_t << "  <table border=\"0\" cellspacing=\"2\" cellpadding=\"0\">" << endl;
       
  1330 }
       
  1331 
       
  1332 void HtmlDocVisitor::visitPost(DocParamSect *s)
       
  1333 {
       
  1334   if (m_hide) return;
       
  1335   m_t << "  </table>" << endl;
       
  1336   m_t << "  </dd>" << endl;
       
  1337   m_t << "</dl>" << endl;
       
  1338   forceStartParagraph(s);
       
  1339 }
       
  1340 
       
  1341 void HtmlDocVisitor::visitPre(DocParamList *pl)
       
  1342 {
       
  1343   if (m_hide) return;
       
  1344   m_t << "    <tr><td valign=\"top\">";
       
  1345   if (pl->direction()!=DocParamSect::Unspecified)
       
  1346   {
       
  1347     m_t << "<tt>[";
       
  1348     if (pl->direction()==DocParamSect::In)
       
  1349     {
       
  1350       m_t << "in";
       
  1351     }
       
  1352     else if (pl->direction()==DocParamSect::Out)
       
  1353     {
       
  1354       m_t << "out";
       
  1355     }
       
  1356     else if (pl->direction()==DocParamSect::InOut)
       
  1357     {
       
  1358       m_t << "in,out";
       
  1359     }
       
  1360     m_t << "]</tt>&nbsp;";
       
  1361   }
       
  1362   m_t << "</td><td valign=\"top\"><em>";
       
  1363   //QStrListIterator li(pl->parameters());
       
  1364   //const char *s;
       
  1365   QListIterator<DocNode> li(pl->parameters());
       
  1366   DocNode *param;
       
  1367   bool first=TRUE;
       
  1368   for (li.toFirst();(param=li.current());++li)
       
  1369   {
       
  1370     if (!first) m_t << ","; else first=FALSE;
       
  1371     if (param->kind()==DocNode::Kind_Word)
       
  1372     {
       
  1373       visit((DocWord*)param); 
       
  1374     }
       
  1375     else if (param->kind()==DocNode::Kind_LinkedWord)
       
  1376     {
       
  1377       visit((DocLinkedWord*)param); 
       
  1378     }
       
  1379   }
       
  1380   m_t << "</em>&nbsp;</td><td>";
       
  1381 }
       
  1382 
       
  1383 void HtmlDocVisitor::visitPost(DocParamList *)
       
  1384 {
       
  1385   if (m_hide) return;
       
  1386   m_t << "</td></tr>" << endl;
       
  1387 }
       
  1388 
       
  1389 void HtmlDocVisitor::visitPre(DocXRefItem *x)
       
  1390 {
       
  1391   if (m_hide) return;
       
  1392   forceEndParagraph(x);
       
  1393   bool anonymousEnum = x->file()=="@";
       
  1394   if (!anonymousEnum)
       
  1395   {
       
  1396     m_t << "<dl class=\"" << x->key() << "\"><dt><b><a class=\"el\" href=\"" 
       
  1397         << x->relPath() << x->file() << Doxygen::htmlFileExtension 
       
  1398         << "#" << x->anchor() << "\">";
       
  1399   }
       
  1400   else 
       
  1401   {
       
  1402     m_t << "<dl class=\"" << x->key() << "\"><dt><b>";
       
  1403   }
       
  1404   filter(x->title());
       
  1405   m_t << ":";
       
  1406   if (!anonymousEnum) m_t << "</a>";
       
  1407   m_t << "</b></dt><dd>";
       
  1408 }
       
  1409 
       
  1410 void HtmlDocVisitor::visitPost(DocXRefItem *x)
       
  1411 {
       
  1412   if (m_hide) return;
       
  1413   m_t << "</dd></dl>" << endl;
       
  1414   forceStartParagraph(x);
       
  1415 }
       
  1416 
       
  1417 void HtmlDocVisitor::visitPre(DocInternalRef *ref)
       
  1418 {
       
  1419   if (m_hide) return;
       
  1420   startLink(0,ref->file(),ref->relPath(),ref->anchor());
       
  1421 }
       
  1422 
       
  1423 void HtmlDocVisitor::visitPost(DocInternalRef *) 
       
  1424 {
       
  1425   if (m_hide) return;
       
  1426   endLink();
       
  1427   m_t << " ";
       
  1428 }
       
  1429 
       
  1430 void HtmlDocVisitor::visitPre(DocCopy *)
       
  1431 {
       
  1432 }
       
  1433 
       
  1434 void HtmlDocVisitor::visitPost(DocCopy *)
       
  1435 {
       
  1436 }
       
  1437 
       
  1438 void HtmlDocVisitor::visitPre(DocText *)
       
  1439 {
       
  1440 }
       
  1441 
       
  1442 void HtmlDocVisitor::visitPost(DocText *)
       
  1443 {
       
  1444 }
       
  1445 
       
  1446 void HtmlDocVisitor::filter(const char *str)
       
  1447 { 
       
  1448   if (str==0) return;
       
  1449   const char *p=str;
       
  1450   char c;
       
  1451   while (*p)
       
  1452   {
       
  1453     c=*p++;
       
  1454     switch(c)
       
  1455     {
       
  1456       case '<':  m_t << "&lt;"; break;
       
  1457       case '>':  m_t << "&gt;"; break;
       
  1458       case '&':  m_t << "&amp;"; break;
       
  1459       default:   m_t << c;
       
  1460     }
       
  1461   }
       
  1462 }
       
  1463 
       
  1464 /// Escape basic entities to produce a valid CDATA attribute value,
       
  1465 /// assume that the outer quoting will be using the double quote &quot;
       
  1466 void HtmlDocVisitor::filterQuotedCdataAttr(const char* str)
       
  1467 {
       
  1468   if (str==0) return;
       
  1469   const char *p=str;
       
  1470   char c;
       
  1471   while (*p)
       
  1472   {
       
  1473     c=*p++;
       
  1474     switch(c)
       
  1475     {
       
  1476       case '&':  m_t << "&amp;"; break;
       
  1477       case '"':  m_t << "&quot;"; break;
       
  1478        // For SGML compliance, and given the SGML declaration for HTML syntax,
       
  1479        // it's enough to replace these two, provided that the declaration
       
  1480        // for the HTML version we generate (and as supported by the browser)
       
  1481        // specifies that all the other symbols used in rawVal are
       
  1482        // within the right charachter class (i.e., they're not
       
  1483        // some multinational weird charachters not in the BASESET).
       
  1484        // We assume that 1) the browser will support whatever is remaining
       
  1485        // in the formula and 2) the TeX formulae are generally governed
       
  1486        // by even stricter charachter restrictions so it should be enough.
       
  1487        //
       
  1488        // On some incompliant browsers, additional translation of
       
  1489        // '>' and '<' into "&gt;" and "&lt;", respectively, might be needed;
       
  1490        // but I'm unaware of particular modern (last 4 years) versions
       
  1491        // with such problems, so let's not do it for performance.
       
  1492        // Also, some brousers will (wrongly) not process the entity references
       
  1493        // inside the attribute value and show the &...; form instead,  
       
  1494        // so we won't create entites unless necessary to minimize clutter there.
       
  1495        // --vassilii 
       
  1496       default:   m_t << c;
       
  1497     }
       
  1498   }
       
  1499 }
       
  1500 
       
  1501 void HtmlDocVisitor::startLink(const QString &ref,const QString &file,
       
  1502                                const QString &relPath,const QString &anchor,
       
  1503                                const QString &tooltip)
       
  1504 {
       
  1505   QCString *dest;
       
  1506   if (!ref.isEmpty()) // link to entity imported via tag file
       
  1507   {
       
  1508     m_t << "<a class=\"elRef\" ";
       
  1509     m_t << "doxygen=\"" << ref << ":";
       
  1510     if ((dest=Doxygen::tagDestinationDict[ref])) m_t << *dest << "/";
       
  1511     m_t << "\" ";
       
  1512   }
       
  1513   else // local link
       
  1514   {
       
  1515     m_t << "<a class=\"el\" ";
       
  1516   }
       
  1517   m_t << "href=\"";
       
  1518   if (!ref.isEmpty())
       
  1519   {
       
  1520     if ((dest=Doxygen::tagDestinationDict[ref])) m_t << *dest << "/";
       
  1521   }
       
  1522   else
       
  1523   {
       
  1524     m_t << relPath;
       
  1525   }
       
  1526   if (!file.isEmpty()) m_t << file << Doxygen::htmlFileExtension;
       
  1527   if (!anchor.isEmpty()) m_t << "#" << anchor;
       
  1528   m_t << "\"";
       
  1529   if (!tooltip.isEmpty()) m_t << " title=\"" << tooltip << "\"";
       
  1530   m_t << ">";
       
  1531 }
       
  1532 
       
  1533 void HtmlDocVisitor::endLink()
       
  1534 {
       
  1535   m_t << "</a>";
       
  1536 }
       
  1537 
       
  1538 void HtmlDocVisitor::pushEnabled()
       
  1539 {
       
  1540   m_enabled.push(new bool(m_hide));
       
  1541 }
       
  1542 
       
  1543 void HtmlDocVisitor::popEnabled()
       
  1544 {
       
  1545   bool *v=m_enabled.pop();
       
  1546   ASSERT(v!=0);
       
  1547   m_hide = *v;
       
  1548   delete v;
       
  1549 }
       
  1550 
       
  1551 void HtmlDocVisitor::writeDotFile(const QString &fileName,const QString &relPath,
       
  1552                                   const QString &context)
       
  1553 {
       
  1554   QString baseName=fileName;
       
  1555   int i;
       
  1556   if ((i=baseName.findRev('/'))!=-1)
       
  1557   {
       
  1558     baseName=baseName.right(baseName.length()-i-1);
       
  1559   }
       
  1560   QString outDir = Config_getString("HTML_OUTPUT");
       
  1561   writeDotGraphFromFile(fileName,outDir,baseName,BITMAP);
       
  1562   QString mapName = baseName+".map";
       
  1563   QString mapFile = fileName+".map";
       
  1564   m_t << "<img src=\"" << relPath << baseName << "." 
       
  1565     << Config_getEnum("DOT_IMAGE_FORMAT") << "\" alt=\""
       
  1566     << baseName << "\" border=\"0\" usemap=\"#" << mapName << "\">" << endl;
       
  1567   QString imap = getDotImageMapFromFile(fileName,outDir,relPath.data(),context);
       
  1568   m_t << "<map name=\"" << mapName << "\" id=\"" << mapName << "\">" << imap << "</map>" << endl;
       
  1569 }
       
  1570 
       
  1571 void HtmlDocVisitor::writeMscFile(const QString &fileName,const QString &relPath,
       
  1572                                   const QString &context)
       
  1573 {
       
  1574   QString baseName=fileName;
       
  1575   int i;
       
  1576   if ((i=baseName.findRev('/'))!=-1)
       
  1577   {
       
  1578     baseName=baseName.right(baseName.length()-i-1);
       
  1579   }
       
  1580   QString outDir = Config_getString("HTML_OUTPUT");
       
  1581   writeMscGraphFromFile(fileName,outDir,baseName,MSC_BITMAP);
       
  1582   QString mapName = baseName+".map";
       
  1583   QString mapFile = fileName+".map";
       
  1584   m_t << "<img src=\"" << relPath << baseName << ".png\" alt=\""
       
  1585     << baseName << "\" border=\"0\" usemap=\"#" << mapName << "\">" << endl;
       
  1586   QString imap = getMscImageMapFromFile(fileName,outDir,relPath.data(),context);
       
  1587   m_t << "<map name=\"" << mapName << "\" id=\"" << mapName << "\">" << imap << "</map>" << endl;
       
  1588 }
       
  1589 
       
  1590 /** Used for items found inside a paragraph, which due to XHTML restrictions
       
  1591  *  have to be outside of the paragraph. This method will forcefully end
       
  1592  *  the current paragraph and forceStartParagraph() will restart it.
       
  1593  */
       
  1594 void HtmlDocVisitor::forceEndParagraph(DocNode *n)
       
  1595 {
       
  1596   //printf("forceEndParagraph(%p) %d\n",n,n->kind());
       
  1597   if (n->parent() && n->parent()->kind()==DocNode::Kind_Para)
       
  1598   {
       
  1599     DocPara *para = (DocPara*)n->parent();
       
  1600     int nodeIndex = para->children().findRef(n);
       
  1601     nodeIndex--;
       
  1602     if (nodeIndex<0) return; // first node
       
  1603     while (nodeIndex>=0 && 
       
  1604            para->children().at(nodeIndex)->kind()==DocNode::Kind_WhiteSpace
       
  1605           )
       
  1606     {
       
  1607       nodeIndex--;
       
  1608     }
       
  1609     if (nodeIndex>=0)
       
  1610     {
       
  1611       DocNode *n = para->children().at(nodeIndex);
       
  1612       //printf("n=%p kind=%d outside=%d\n",n,n->kind(),mustBeOutsideParagraph(n));
       
  1613       if (mustBeOutsideParagraph(n)) return;
       
  1614     }
       
  1615 
       
  1616     bool isFirst;
       
  1617     bool isLast;
       
  1618     getParagraphContext(para,isFirst,isLast);
       
  1619     //printf("forceEnd first=%d last=%d\n",isFirst,isLast);
       
  1620     if (isFirst && isLast) return;
       
  1621 
       
  1622     m_t << "</p>" << endl;
       
  1623   }
       
  1624 }
       
  1625 
       
  1626 /** Used for items found inside a paragraph, which due to XHTML restrictions
       
  1627  *  have to be outside of the paragraph. This method will forcefully start
       
  1628  *  the paragraph, that was previously ended by forceEndParagraph().
       
  1629  */
       
  1630 void HtmlDocVisitor::forceStartParagraph(DocNode *n)
       
  1631 {
       
  1632   //printf("forceStartParagraph(%p) %d\n",n,n->kind());
       
  1633   if (n->parent() && n->parent()->kind()==DocNode::Kind_Para) // if we are inside a paragraph
       
  1634   {
       
  1635     DocPara *para = (DocPara*)n->parent();
       
  1636     int nodeIndex = para->children().findRef(n);
       
  1637     int numNodes  = para->children().count();
       
  1638     nodeIndex++;
       
  1639     if (nodeIndex==numNodes) return; // last node
       
  1640     while (nodeIndex<numNodes && 
       
  1641            para->children().at(nodeIndex)->kind()==DocNode::Kind_WhiteSpace
       
  1642           )
       
  1643     {
       
  1644       nodeIndex++;
       
  1645     }
       
  1646     if (nodeIndex<numNodes)
       
  1647     {
       
  1648       DocNode *n = para->children().at(nodeIndex);
       
  1649       if (mustBeOutsideParagraph(n)) return;
       
  1650     }
       
  1651     else
       
  1652     {
       
  1653       return; // only whitespace at the end!
       
  1654     }
       
  1655 
       
  1656     bool isFirst;
       
  1657     bool isLast;
       
  1658     getParagraphContext(para,isFirst,isLast);
       
  1659     //printf("forceStart first=%d last=%d\n",isFirst,isLast);
       
  1660     if (isFirst && isLast) return;
       
  1661 
       
  1662     m_t << "<p>";
       
  1663   }
       
  1664 }
       
  1665