From 883989fced827524354297009fde396ca6264a31 Mon Sep 17 00:00:00 2001
From: Dimitri van Heesch <dimitri@stack.nl>
Date: Tue, 18 Aug 2015 19:40:03 +0200
Subject: Fixes to support nested tables again

---
 src/ftvhelp.cpp             |   1 +
 src/htmldocvisitor.cpp      |  18 ++----
 src/latexdocvisitor.cpp     | 136 +++++++++++++++++++++++++++-----------------
 src/latexdocvisitor.h       |  84 ++++++++++++++++++++++++---
 templates/html/doxygen.css  |   4 ++
 templates/latex/doxygen.sty |   8 ++-
 6 files changed, 180 insertions(+), 71 deletions(-)

diff --git a/src/ftvhelp.cpp b/src/ftvhelp.cpp
index e2774a9..a70e243 100644
--- a/src/ftvhelp.cpp
+++ b/src/ftvhelp.cpp
@@ -171,6 +171,7 @@ void FTVHelp::decContentsDepth()
 
 /*! Add a list item to the contents file.
  *  \param isDir TRUE if the item is a directory, FALSE if it is a text
+ *  \param name The name of the item.
  *  \param ref  the URL of to the item.
  *  \param file the file containing the definition of the item
  *  \param anchor the anchor within the file.
diff --git a/src/htmldocvisitor.cpp b/src/htmldocvisitor.cpp
index 296a957..2e9795e 100644
--- a/src/htmldocvisitor.cpp
+++ b/src/htmldocvisitor.cpp
@@ -1301,6 +1301,11 @@ void HtmlDocVisitor::visitPre(DocHtmlTable *t)
 
   forceEndParagraph(t);
 
+  if (t->hasCaption())
+  {
+    m_t << "<a class=\"anchor\" id=\"" << t->caption()->anchor() << "\"></a>\n";
+  }
+
   QString attrs = htmlAttribsToString(t->attribs());
   if (attrs.isEmpty())
   {
@@ -1353,21 +1358,11 @@ void HtmlDocVisitor::visitPost(DocHtmlCell *c)
 void HtmlDocVisitor::visitPre(DocHtmlCaption *c)
 {
   if (m_hide) return;
-  if (c->hasCaptionId())
-  {
-    m_t << "<a class=\"anchor\" id=\"" << c->anchor() << "\"></a>\n";
-  }
   bool hasAlign      = FALSE;
   HtmlAttribListIterator li(c->attribs());
   HtmlAttrib *att;
   QCString id;
-  for (li.toFirst();(att=li.current());++li)
-  {
-    if (att->name=="align") hasAlign=TRUE;
-  }
-  m_t << "<caption" << htmlAttribsToString(c->attribs());
-  if (!hasAlign) m_t << " align=\"bottom\"";
-  m_t << ">";
+  m_t << "<caption" << htmlAttribsToString(c->attribs()) << ">";
 }
 
 void HtmlDocVisitor::visitPost(DocHtmlCaption *) 
@@ -1813,7 +1808,6 @@ void HtmlDocVisitor::visitPre(DocHtmlBlockQuote *b)
 {
   if (m_hide) return;
   forceEndParagraph(b);
-
   QString attrs = htmlAttribsToString(b->attribs());
   if (attrs.isEmpty())
   {
diff --git a/src/latexdocvisitor.cpp b/src/latexdocvisitor.cpp
index 22b3b32..5e7278c 100644
--- a/src/latexdocvisitor.cpp
+++ b/src/latexdocvisitor.cpp
@@ -172,10 +172,9 @@ LatexDocVisitor::LatexDocVisitor(FTextStream &t,CodeOutputInterface &ci,
                                  const char *langExt,bool insideTabbing) 
   : DocVisitor(DocVisitor_Latex), m_t(t), m_ci(ci), m_insidePre(FALSE), 
     m_insideItem(FALSE), m_hide(FALSE), m_hideCaption(FALSE), m_insideTabbing(insideTabbing),
-    m_insideTable(FALSE), m_langExt(langExt), m_currentColumn(0), 
-    m_inRowspan(FALSE), m_inColspan(FALSE), m_firstRow(FALSE)
+    m_langExt(langExt)
 {
-  m_rowSpans.setAutoDelete(TRUE);
+  m_tableStateStack.setAutoDelete(TRUE);
 }
 
   //--------------------------------------
@@ -890,7 +889,7 @@ void LatexDocVisitor::visitPost(DocHtmlDescData *)
 {
 }
 
-static const char *getTableName(const DocNode *n)
+static bool tableIsNested(const DocNode *n)
 {
   bool isNested=FALSE;
   while (n && !isNested)
@@ -898,14 +897,39 @@ static const char *getTableName(const DocNode *n)
     isNested = n->kind()==DocNode::Kind_HtmlTable || n->kind()==DocNode::Kind_ParamSect;
     n  = n->parent();
   }
-  return isNested ? "TabularNC" : "TabularC";
+  return isNested;
+}
+
+static void writeStartTableCommand(FTextStream &t,const DocNode *n,int cols)
+{
+  if (tableIsNested(n))
+  {
+    t << "\\begin{tabularx}{\\linewidth}{|*{" << cols << "}{>{\\raggedright\\arraybackslash}X|}}";
+  }
+  else
+  {
+    t << "\\tabulinesep=1mm\n\\begin{longtabu} spread 0pt [c]{*" << cols << "{|X[-1]}|}\n";
+  }
+  //return isNested ? "TabularNC" : "TabularC";
+}
+
+static void writeEndTableCommand(FTextStream &t,const DocNode *n)
+{
+  if (tableIsNested(n))
+  {
+    t << "\\end{tabularx}\n";
+  }
+  else
+  {
+    t << "\\end{longtabu}\n";
+  }
+  //return isNested ? "TabularNC" : "TabularC";
 }
 
 void LatexDocVisitor::visitPre(DocHtmlTable *t)
 {
-  m_rowSpans.clear();
-  m_insideTable=TRUE;
   if (m_hide) return;
+  pushTableState();
   if (t->hasCaption())
   {
     DocHtmlCaption *c = t->caption();
@@ -918,7 +942,7 @@ void LatexDocVisitor::visitPre(DocHtmlTable *t)
     m_t << endl;
   }
 
-  m_t << "\\begin{" << getTableName(t->parent()) << "}{" << t->numColumns() << "}\n";
+  writeStartTableCommand(m_t,t->parent(),t->numColumns());
 
   if (t->hasCaption())
   {
@@ -930,7 +954,7 @@ void LatexDocVisitor::visitPre(DocHtmlTable *t)
     m_t << "\\\\\n";
   }
 
-  m_numCols = t->numColumns();
+  setNumCols(t->numColumns());
   m_t << "\\hline\n";
 
   // check if first row is a heading and then render the row already here
@@ -939,17 +963,17 @@ void LatexDocVisitor::visitPre(DocHtmlTable *t)
   DocHtmlRow *firstRow = t->firstRow();
   if (firstRow && firstRow->isHeading())
   {
-    m_firstRow=TRUE;
+    setFirstRow(TRUE);
     firstRow->accept(this);
-    m_firstRow=FALSE;
+    setFirstRow(FALSE);
   }
 }
 
 void LatexDocVisitor::visitPost(DocHtmlTable *t)
 {
-  m_insideTable=FALSE;
   if (m_hide) return;
-  m_t << "\\end{" << getTableName(t->parent()) << "}\n";
+  writeEndTableCommand(m_t,t->parent());
+  popTableState();
 }
 
 void LatexDocVisitor::visitPre(DocHtmlCaption *c)
@@ -965,7 +989,7 @@ void LatexDocVisitor::visitPost(DocHtmlCaption *c)
 
 void LatexDocVisitor::visitPre(DocHtmlRow *r)
 {
-  m_currentColumn = 0;
+  setCurrentColumn(0);
   if (r->isHeading()) m_t << "\\rowcolor{\\tableheadbgcolor}";
 }
 
@@ -973,15 +997,15 @@ void LatexDocVisitor::visitPost(DocHtmlRow *row)
 {
   if (m_hide) return;
 
-  int c=m_currentColumn;
-  while (c<=m_numCols) // end of row while inside a row span?
+  int c=currentColumn();
+  while (c<=numCols()) // end of row while inside a row span?
   {
     uint i;
-    for (i=0;i<m_rowSpans.count();i++)
+    for (i=0;i<rowSpans().count();i++)
     {
-      ActiveRowSpan *span = m_rowSpans.at(i);
-      //printf("  founc row span: column=%d rs=%d cs=%d rowIdx=%d cell->rowIdx=%d\n",
-      //    span->column, span->rowSpan,span->colSpan,row->rowIndex(),span->cell->rowIndex());
+      ActiveRowSpan *span = rowSpans().at(i);
+      //printf("  found row span: column=%d rs=%d cs=%d rowIdx=%d cell->rowIdx=%d i=%d c=%d\n",
+      //    span->column, span->rowSpan,span->colSpan,row->rowIndex(),span->cell->rowIndex(),i,c);
       if (span->rowSpan>0 && span->column==c &&  // we are at a cell in a row span
           row->rowIndex()>span->cell->rowIndex() // but not the row that started the span
          )
@@ -991,9 +1015,9 @@ void LatexDocVisitor::visitPost(DocHtmlRow *row)
         {
           m_t << "\\multicolumn{" << span->colSpan << "}{";
           m_t << "p{(\\linewidth-\\tabcolsep*" 
-            << m_numCols << "-\\arrayrulewidth*"
+            << numCols() << "-\\arrayrulewidth*"
             << row->visibleCells() << ")*" 
-            << span->colSpan <<"/"<< m_numCols << "}|}{}";
+            << span->colSpan <<"/"<< numCols() << "}|}{}";
         }
         else // solitary row span
         {
@@ -1008,9 +1032,9 @@ void LatexDocVisitor::visitPost(DocHtmlRow *row)
   
   int col = 1;
   uint i;
-  for (i=0;i<m_rowSpans.count();i++)
+  for (i=0;i<rowSpans().count();i++)
   {
-    ActiveRowSpan *span = m_rowSpans.at(i);
+    ActiveRowSpan *span = rowSpans().at(i);
     if (span->rowSpan>0) span->rowSpan--;
     if (span->rowSpan<=0)
     {
@@ -1027,16 +1051,16 @@ void LatexDocVisitor::visitPost(DocHtmlRow *row)
     }
   }
 
-  if (col <= m_numCols)
+  if (col <= numCols())
   {
-    m_t << "\\cline{" << col << "-" << m_numCols << "}";
+    m_t << "\\cline{" << col << "-" << numCols() << "}";
   }
 
   m_t << "\n";
 
   if (row->isHeading() && row->rowIndex()==1)
   {
-    if (m_firstRow)
+    if (firstRow())
     {
       m_t << "\\endfirsthead" << endl;
       m_t << "\\hline" << endl;
@@ -1059,32 +1083,32 @@ void LatexDocVisitor::visitPre(DocHtmlCell *c)
   {
     row = (DocHtmlRow*)c->parent();
   }
-  
-  m_currentColumn++;
+
+  setCurrentColumn(currentColumn()+1);
 
   //Skip columns that span from above.
   uint i;
-  for (i=0;i<m_rowSpans.count();i++)
+  for (i=0;i<rowSpans().count();i++)
   {
-    ActiveRowSpan *span = m_rowSpans.at(i);
-    if (span->rowSpan>0 && span->column==m_currentColumn)
+    ActiveRowSpan *span = rowSpans().at(i);
+    if (span->rowSpan>0 && span->column==currentColumn())
     {
       if (row && span->colSpan>1)
       {
         m_t << "\\multicolumn{" << span->colSpan << "}{";
-        if (m_currentColumn /*c->columnIndex()*/==1) // add extra | for first column
+        if (currentColumn() /*c->columnIndex()*/==1) // add extra | for first column
         {
           m_t << "|";
         }
         m_t << "p{(\\linewidth-\\tabcolsep*" 
-            << m_numCols << "-\\arrayrulewidth*"
+            << numCols() << "-\\arrayrulewidth*"
             << row->visibleCells() << ")*" 
-            << span->colSpan <<"/"<< m_numCols << "}|}{}";
-        m_currentColumn+=span->colSpan;
+            << span->colSpan <<"/"<< numCols() << "}|}{}";
+        setCurrentColumn(currentColumn()+span->colSpan);
       }
       else
       {
-        m_currentColumn++;
+        setCurrentColumn(currentColumn()+1);
       }
       m_t << "&";
     }
@@ -1093,23 +1117,26 @@ void LatexDocVisitor::visitPre(DocHtmlCell *c)
   int cs = c->colSpan();
   if (cs>1 && row)
   {
-    m_inColspan = TRUE;
+    setInColSpan(TRUE);
     m_t << "\\multicolumn{" << cs << "}{";
     if (c->columnIndex()==1) // add extra | for first column
     {
       m_t << "|";
     }
     m_t << "p{(\\linewidth-\\tabcolsep*" 
-        << m_numCols << "-\\arrayrulewidth*"
+        << numCols() << "-\\arrayrulewidth*"
         << row->visibleCells() << ")*" 
-        << cs <<"/"<< m_numCols << "}|}{";
+        << cs <<"/"<< numCols() << "}|}{";
     if (c->isHeading()) m_t << "\\cellcolor{\\tableheadbgcolor}";
   }
   int rs = c->rowSpan();
   if (rs>0)
   {
-    m_inRowspan = TRUE;
-    m_rowSpans.append(new ActiveRowSpan(c,rs,cs,m_currentColumn));
+    setInRowSpan(TRUE);
+    //printf("adding row span: cell={r=%d c=%d rs=%d cs=%d} curCol=%d\n",
+    //                       c->rowIndex(),c->columnIndex(),c->rowSpan(),c->colSpan(),
+    //                       currentColumn());
+    addRowSpan(new ActiveRowSpan(c,rs,cs,currentColumn()));
     m_t << "\\multirow{" << rs << "}{\\linewidth}{";
   }
   int a = c->alignment();
@@ -1127,7 +1154,7 @@ void LatexDocVisitor::visitPre(DocHtmlCell *c)
   }
   if (cs>1)
   {
-    m_currentColumn+=cs-1;
+    setCurrentColumn(currentColumn()+cs-1);
   }
 }
 
@@ -1138,14 +1165,14 @@ void LatexDocVisitor::visitPost(DocHtmlCell *c)
   {
     m_t << "}";
   }
-  if (m_inRowspan)
+  if (inRowSpan())
   {
-    m_inRowspan = FALSE;
+    setInRowSpan(FALSE);
     m_t << "}";
   }
-  if (m_inColspan)
+  if (inColSpan())
   {
-    m_inColspan = FALSE;
+    setInColSpan(FALSE);
     m_t << "}";
   }
   if (!c->isLast()) m_t << "&";
@@ -1603,15 +1630,22 @@ void LatexDocVisitor::filter(const char *str)
 void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const QCString &anchor,bool refToTable)
 {
   static bool pdfHyperLinks = Config_getBool("PDF_HYPERLINKS");
-  if (ref.isEmpty() && pdfHyperLinks) // internal PDF link 
+  if (ref.isEmpty() && pdfHyperLinks) // internal PDF link
   {
-    m_t << "\\hyperlink{";
+    if (refToTable)
+    {
+      m_t << "\\doxytablelink{";
+    }
+    else
+    {
+      m_t << "\\hyperlink{";
+    }
     if (!file.isEmpty()) m_t << stripPath(file);
     if (!file.isEmpty() && !anchor.isEmpty()) m_t << "_";
     if (!anchor.isEmpty()) m_t << anchor;
     m_t << "}{";
   }
-  else if (refToTable)
+  else if (ref.isEmpty() && refToTable)
   {
     m_t << "\\doxytableref{";
   }
@@ -1620,7 +1654,7 @@ void LatexDocVisitor::startLink(const QCString &ref,const QCString &file,const Q
     m_t << "\\doxyref{";
   }
   else // external link
-  { 
+  {
     m_t << "{\\bf ";
   }
 }
diff --git a/src/latexdocvisitor.h b/src/latexdocvisitor.h
index de797ae..02df1ef 100644
--- a/src/latexdocvisitor.h
+++ b/src/latexdocvisitor.h
@@ -192,15 +192,85 @@ class LatexDocVisitor : public DocVisitor
     bool m_hide;
     bool m_hideCaption;
     bool m_insideTabbing;
-    bool m_insideTable;
-    int  m_numCols;
     QStack<bool> m_enabled;
     QCString m_langExt;
-    RowSpanList m_rowSpans;
-    int m_currentColumn;
-    bool m_inRowspan;
-    bool m_inColspan;
-    bool m_firstRow;
+
+    struct TableState
+    {
+      TableState() : numCols(0), currentColumn(0), inRowSpan(FALSE), 
+                     inColSpan(FALSE), firstRow(FALSE)
+      { rowSpans.setAutoDelete(TRUE); }
+      RowSpanList rowSpans;
+      int  numCols;
+      int currentColumn;
+      bool inRowSpan;
+      bool inColSpan;
+      bool firstRow;
+    };
+    QStack<TableState> m_tableStateStack; // needed for nested tables
+    RowSpanList m_emptyRowSpanList;
+
+    void pushTableState()
+    {
+      m_tableStateStack.push(new TableState);
+    }
+    void popTableState()
+    {
+      delete m_tableStateStack.pop();
+    }
+    int currentColumn() const
+    {
+      return !m_tableStateStack.isEmpty() ? m_tableStateStack.top()->currentColumn : 0;
+    }
+    void setCurrentColumn(int col)
+    {
+      if (!m_tableStateStack.isEmpty()) m_tableStateStack.top()->currentColumn = col;
+    }
+    int numCols() const
+    {
+      return !m_tableStateStack.isEmpty() ? m_tableStateStack.top()->numCols : 0;
+    }
+    void setNumCols(int num)
+    {
+      if (!m_tableStateStack.isEmpty()) m_tableStateStack.top()->numCols = num;
+    }
+    bool inRowSpan() const
+    {
+      return !m_tableStateStack.isEmpty() ? m_tableStateStack.top()->inRowSpan : FALSE;
+    }
+    void setInRowSpan(bool b)
+    {
+      if (!m_tableStateStack.isEmpty()) m_tableStateStack.top()->inRowSpan = b;
+    }
+    bool inColSpan() const
+    {
+      return !m_tableStateStack.isEmpty() ? m_tableStateStack.top()->inColSpan : FALSE;
+    }
+    void setInColSpan(bool b)
+    {
+      if (!m_tableStateStack.isEmpty()) m_tableStateStack.top()->inColSpan = b;
+    }
+    bool firstRow() const
+    {
+      return !m_tableStateStack.isEmpty() ? m_tableStateStack.top()->firstRow : FALSE;
+    }
+    void setFirstRow(bool b)
+    {
+      if (!m_tableStateStack.isEmpty()) m_tableStateStack.top()->firstRow = b;
+    }
+    const RowSpanList &rowSpans()
+    {
+      return !m_tableStateStack.isEmpty() ? m_tableStateStack.top()->rowSpans : m_emptyRowSpanList;
+    }
+    void addRowSpan(ActiveRowSpan *span)
+    {
+      if (!m_tableStateStack.isEmpty()) m_tableStateStack.top()->rowSpans.append(span);
+    }
+    bool insideTable() const
+    {
+      return !m_tableStateStack.isEmpty();
+    }
+
 };
 
 #endif
diff --git a/templates/html/doxygen.css b/templates/html/doxygen.css
index e41b0d7..8383f5a 100644
--- a/templates/html/doxygen.css
+++ b/templates/html/doxygen.css
@@ -832,6 +832,10 @@ address {
 	color: ##33;
 }
 
+table.doxtable caption {
+	caption-side: top;
+}
+
 table.doxtable {
 	border-collapse:collapse;
         margin-top: 4px;
diff --git a/templates/latex/doxygen.sty b/templates/latex/doxygen.sty
index 64fb0f0..803b662 100644
--- a/templates/latex/doxygen.sty
+++ b/templates/latex/doxygen.sty
@@ -11,6 +11,7 @@
 \RequirePackage[table]{xcolor}
 \RequirePackage{longtable}
 \RequirePackage{tabu}
+\RequirePackage{tabularx}
 
 %---------- Internal commands used in this style file ----------------
 
@@ -421,7 +422,7 @@
 \newcommand{\PBS}[1]{\let\temp=\\#1\let\\=\temp}%
 \newenvironment{TabularC}[1]%
 {\tabulinesep=1mm
-\begin{longtabu} spread 0pt [l]{*#1{|X[-1]}|}}%
+\begin{longtabu} spread 0pt [c]{*#1{|X[-1]}|}}%
 {\end{longtabu}\par}%
 
 \newenvironment{TabularNC}[1]%
@@ -444,6 +445,11 @@
   \textbf{#1} (\textnormal{#2}\,\pageref{#3})%
 }
 
+% Used to link to a table when hyperlinks are turned on
+\newcommand{\doxytablelink}[2]{%
+  \ref{#1}%
+}
+
 % Used to link to a table when hyperlinks are turned off
 \newcommand{\doxytableref}[3]{%
   \ref{#3}%
-- 
cgit v0.12

