Commit fe818bf8 authored by Dimitri van Heesch's avatar Dimitri van Heesch

Added graphical hierarchy support to template engine

parent 3ebc4315
This diff is collapsed.
......@@ -51,6 +51,8 @@ struct MemberInfo;
class MemberGroup;
class MemberGroupSDict;
class MemberGroupList;
class DotNode;
class DotGfxHierarchyTable;
//----------------------------------------------------
......@@ -419,6 +421,26 @@ class ClassIndexContext : public RefCountedContext, public TemplateStructIntf
//----------------------------------------------------
class InheritanceGraphContext : public RefCountedContext, public TemplateStructIntf
{
public:
static InheritanceGraphContext *alloc(DotGfxHierarchyTable *hierarchy,DotNode *n,int id)
{ return new InheritanceGraphContext(hierarchy,n,id); }
// TemplateStructIntf methods
virtual TemplateVariant get(const char *name) const;
virtual int addRef() { return RefCountedContext::addRef(); }
virtual int release() { return RefCountedContext::release(); }
private:
InheritanceGraphContext(DotGfxHierarchyTable *hierarchy,DotNode *n,int id);
~InheritanceGraphContext();
class Private;
Private *p;
};
//----------------------------------------------------
class ClassInheritanceNodeContext : public RefCountedContext, public TemplateStructIntf
{
public:
......@@ -485,8 +507,8 @@ class NestingNodeContext : public RefCountedContext, public TemplateStructIntf
{
public:
static NestingNodeContext *alloc(const NestingNodeContext *parent,Definition *def,
int index,int level,bool addClasses)
{ return new NestingNodeContext(parent,def,index,level,addClasses); }
int index,int level,bool addClasses,bool inherit,bool hideSuper)
{ return new NestingNodeContext(parent,def,index,level,addClasses,inherit,hideSuper); }
QCString id() const;
......@@ -497,7 +519,7 @@ class NestingNodeContext : public RefCountedContext, public TemplateStructIntf
private:
NestingNodeContext(const NestingNodeContext *parent,
Definition *,int index,int level,bool addClasses);
Definition *,int index,int level,bool addClasses,bool inherit,bool hideSuper);
~NestingNodeContext();
class Private;
Private *p;
......@@ -527,6 +549,8 @@ class NestingContext : public RefCountedContext, public TemplateListIntf
void addPages(const PageSDict &pages,bool rootOnly);
void addModules(const GroupSDict &modules);
void addModules(const GroupList &modules);
void addClassHierarchy(const ClassSDict &clDict,bool rootOnly);
void addDerivedClasses(const BaseClassList *bcl,bool hideSuper);
private:
NestingContext(const NestingNodeContext *parent,int level);
......
......@@ -772,18 +772,18 @@ static bool checkDeliverables(const QCString &file1,
//--------------------------------------------------------------------
/** Class representing a list of DotNode objects. */
class DotNodeList : public QList<DotNode>
inline int DotNode::findParent( DotNode *n )
{
public:
DotNodeList() : QList<DotNode>() {}
~DotNodeList() {}
private:
int compareValues(const DotNode *n1,const DotNode *n2) const
{
return qstricmp(n1->m_label,n2->m_label);
}
};
if ( !m_parents ) return -1;
return m_parents->find(n);
}
//--------------------------------------------------------------------
int DotNodeList::compareValues(const DotNode *n1,const DotNode *n2) const
{
return qstricmp(n1->m_label,n2->m_label);
}
//--------------------------------------------------------------------
......@@ -1908,7 +1908,7 @@ void DotNode::write(FTextStream &t,
bool reNumber
)
{
//printf("DotNode::write(%d) name=%s this=%p written=%d\n",distance,m_label.data(),this,m_written);
//printf("DotNode::write(%d) name=%s this=%p written=%d visible=%d\n",m_distance,m_label.data(),this,m_written,m_visible);
if (m_written) return; // node already written to the output
if (!m_visible) return; // node is not visible
writeBox(t,gt,format,m_truncated==Truncated,reNumber);
......@@ -2261,12 +2261,106 @@ const DotNode *DotNode::findDocNode() const
int DotGfxHierarchyTable::m_curNodeNumber;
void DotGfxHierarchyTable::createGraph(DotNode *n,FTextStream &out,
const char *path,const char *fileName,int id) const
{
QDir d(path);
QCString baseName;
QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
baseName.sprintf("inherit_graph_%d",id);
QCString imgName = baseName+"."+ imgExt;
QCString mapName = baseName+".map";
QCString absImgName = QCString(d.absPath().data())+"/"+imgName;
QCString absMapName = QCString(d.absPath().data())+"/"+mapName;
QCString absBaseName = QCString(d.absPath().data())+"/"+baseName;
QListIterator<DotNode> dnli2(*m_rootNodes);
DotNode *node;
// compute md5 checksum of the graph were are about to generate
QGString theGraph;
FTextStream md5stream(&theGraph);
writeGraphHeader(md5stream,theTranslator->trGraphicalHierarchy());
md5stream << " rankdir=\"LR\";" << endl;
for (dnli2.toFirst();(node=dnli2.current());++dnli2)
{
if (node->m_subgraphId==n->m_subgraphId)
{
node->clearWriteFlag();
}
}
for (dnli2.toFirst();(node=dnli2.current());++dnli2)
{
if (node->m_subgraphId==n->m_subgraphId)
{
node->write(md5stream,DotNode::Hierarchy,GOF_BITMAP,FALSE,TRUE,TRUE,TRUE);
}
}
writeGraphFooter(md5stream);
resetReNumbering();
uchar md5_sig[16];
QCString sigStr(33);
MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
MD5SigToString(md5_sig,sigStr.data(),33);
bool regenerate=FALSE;
if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
!checkDeliverables(absImgName,absMapName))
{
regenerate=TRUE;
// image was new or has changed
QCString dotName=absBaseName+".dot";
QFile f(dotName);
if (!f.open(IO_WriteOnly)) return;
FTextStream t(&f);
t << theGraph;
f.close();
resetReNumbering();
DotRunner *dotRun = new DotRunner(dotName,d.absPath().data(),TRUE,absImgName);
dotRun->addJob(imgExt,absImgName);
dotRun->addJob(MAP_CMD,absMapName);
DotManager::instance()->addRun(dotRun);
}
else
{
removeDotGraph(absBaseName+".dot");
}
Doxygen::indexList->addImageFile(imgName);
// write image and map in a table row
QCString mapLabel = escapeCharsInString(n->m_label,FALSE);
if (imgExt=="svg") // vector graphics
{
if (regenerate || !writeSVGFigureLink(out,QCString(),baseName,absImgName))
{
if (regenerate)
{
DotManager::instance()->addSVGConversion(absImgName,QCString(),
FALSE,QCString(),FALSE,0);
}
int mapId = DotManager::instance()->addSVGObject(fileName,baseName,
absImgName,QCString());
out << "<!-- SVG " << mapId << " -->" << endl;
}
}
else // normal bitmap
{
out << "<img src=\"" << imgName << "\" border=\"0\" alt=\"\" usemap=\"#"
<< mapLabel << "\"/>" << endl;
if (regenerate || !insertMapFile(out,absMapName,QCString(),mapLabel))
{
int mapId = DotManager::instance()->addMap(fileName,absMapName,QCString(),
FALSE,QCString(),mapLabel);
out << "<!-- MAP " << mapId << " -->" << endl;
}
}
}
void DotGfxHierarchyTable::writeGraph(FTextStream &out,
const char *path,const char *fileName) const
{
//printf("DotGfxHierarchyTable::writeGraph(%s)\n",name);
//printf("m_rootNodes=%p count=%d\n",m_rootNodes,m_rootNodes->count());
if (m_rootSubgraphs->count()==0) return;
QDir d(path);
......@@ -2284,97 +2378,8 @@ void DotGfxHierarchyTable::writeGraph(FTextStream &out,
int count=0;
for (dnli.toFirst();(n=dnli.current());++dnli)
{
QCString baseName;
QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
baseName.sprintf("inherit_graph_%d",count++);
//baseName = convertNameToFile(baseName);
QCString imgName = baseName+"."+ imgExt;
QCString mapName = baseName+".map";
QCString absImgName = QCString(d.absPath().data())+"/"+imgName;
QCString absMapName = QCString(d.absPath().data())+"/"+mapName;
QCString absBaseName = QCString(d.absPath().data())+"/"+baseName;
QListIterator<DotNode> dnli2(*m_rootNodes);
DotNode *node;
// compute md5 checksum of the graph were are about to generate
QGString theGraph;
FTextStream md5stream(&theGraph);
writeGraphHeader(md5stream,theTranslator->trGraphicalHierarchy());
md5stream << " rankdir=\"LR\";" << endl;
for (dnli2.toFirst();(node=dnli2.current());++dnli2)
{
if (node->m_subgraphId==n->m_subgraphId)
{
node->clearWriteFlag();
}
}
for (dnli2.toFirst();(node=dnli2.current());++dnli2)
{
if (node->m_subgraphId==n->m_subgraphId)
{
node->write(md5stream,DotNode::Hierarchy,GOF_BITMAP,FALSE,TRUE,TRUE,TRUE);
}
}
writeGraphFooter(md5stream);
resetReNumbering();
uchar md5_sig[16];
QCString sigStr(33);
MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
MD5SigToString(md5_sig,sigStr.data(),33);
bool regenerate=FALSE;
if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
!checkDeliverables(absImgName,absMapName))
{
regenerate=TRUE;
// image was new or has changed
QCString dotName=absBaseName+".dot";
QFile f(dotName);
if (!f.open(IO_WriteOnly)) return;
FTextStream t(&f);
t << theGraph;
f.close();
resetReNumbering();
DotRunner *dotRun = new DotRunner(dotName,d.absPath().data(),TRUE,absImgName);
dotRun->addJob(imgExt,absImgName);
dotRun->addJob(MAP_CMD,absMapName);
DotManager::instance()->addRun(dotRun);
}
else
{
removeDotGraph(absBaseName+".dot");
}
Doxygen::indexList->addImageFile(imgName);
// write image and map in a table row
QCString mapLabel = escapeCharsInString(n->m_label,FALSE);
out << "<tr><td>";
if (imgExt=="svg") // vector graphics
{
if (regenerate || !writeSVGFigureLink(out,QCString(),baseName,absImgName))
{
if (regenerate)
{
DotManager::instance()->addSVGConversion(absImgName,QCString(),
FALSE,QCString(),FALSE,0);
}
int mapId = DotManager::instance()->addSVGObject(fileName,baseName,
absImgName,QCString());
out << "<!-- SVG " << mapId << " -->" << endl;
}
}
else // normal bitmap
{
out << "<img src=\"" << imgName << "\" border=\"0\" alt=\"\" usemap=\"#"
<< mapLabel << "\"/>" << endl;
if (regenerate || !insertMapFile(out,absMapName,QCString(),mapLabel))
{
int mapId = DotManager::instance()->addMap(fileName,absMapName,QCString(),
FALSE,QCString(),mapLabel);
out << "<!-- MAP " << mapId << " -->" << endl;
}
}
createGraph(n,out,path,fileName,count++);
out << "</td></tr>" << endl;
}
out << "</table>" << endl;
......
......@@ -122,6 +122,7 @@ class DotNode
friend class DotNodeList;
friend class DotCallGraph;
friend class DotGroupCollaboration;
friend class DotInheritanceGraph;
friend QCString computeMd5Signature(
DotNode *root, GraphType gt,
......@@ -133,12 +134,15 @@ class DotNode
);
};
inline int DotNode::findParent( DotNode *n )
/** Class representing a list of DotNode objects. */
class DotNodeList : public QList<DotNode>
{
if( !m_parents )
return -1;
return m_parents->find(n);
}
public:
DotNodeList() : QList<DotNode>() {}
~DotNodeList() {}
private:
int compareValues(const DotNode *n1,const DotNode *n2) const;
};
/** Represents a graphical class hierarchy */
class DotGfxHierarchyTable
......@@ -147,6 +151,8 @@ class DotGfxHierarchyTable
DotGfxHierarchyTable();
~DotGfxHierarchyTable();
void writeGraph(FTextStream &t,const char *path, const char *fileName) const;
void createGraph(DotNode *rootNode,FTextStream &t,const char *path,const char *fileName,int id) const;
const DotNodeList *subGraphs() const { return m_rootSubgraphs; }
private:
void addHierarchy(DotNode *n,ClassDef *cd,bool hide);
......
......@@ -1976,10 +1976,21 @@ class ExpressionParser
ExprAst *parseLiteral()
{
TRACE(("{parseLiteral(%s)\n",m_curToken.id.data()));
ExprAst *lit = new ExprAstLiteral(m_curToken.id);
ExprAst *expr = new ExprAstLiteral(m_curToken.id);
getNextToken();
if (expr)
{
while (m_curToken.type==ExprToken::Operator &&
m_curToken.op==Operator::Filter)
{
getNextToken();
ExprAstFilter *filter = parseFilter();
if (!filter) break;
expr = new ExprAstFilterAppl(expr,filter);
}
}
TRACE(("}parseLiteral()\n"));
return lit;
return expr;
}
ExprAst *parseIdentifierOptionalArgs()
......@@ -3550,6 +3561,7 @@ class TemplateNodeCreate : public TemplateNodeCreator<TemplateNodeCreate>
{
outputFile.prepend(ci->outputDirectory()+"/");
}
//printf("NoteCreate(%s)\n",outputFile.data());
QFile f(outputFile);
if (f.open(IO_WriteOnly))
{
......@@ -3622,9 +3634,11 @@ class TemplateNodeTree : public TemplateNodeCreator<TemplateNodeTree>
{
//printf("TemplateNodeTree::renderChildren(%d)\n",ctx->list->count());
// render all children of node to a string and return it
TemplateContext *c = ctx->templateCtx;
TemplateContextImpl* ci = dynamic_cast<TemplateContextImpl*>(c);
if (ci==0) return QCString(); // should not happen
QGString result;
FTextStream ss(&result);
TemplateContext *c = ctx->templateCtx;
c->push();
TemplateVariant node;
TemplateListIntf::ConstIterator *it = ctx->list->createIterator();
......@@ -3642,14 +3656,21 @@ class TemplateNodeTree : public TemplateNodeCreator<TemplateNodeTree>
if (list && list->count()>0) // non-empty list
{
TreeContext childCtx(this,list,ctx->templateCtx);
// TemplateVariant children(&childCtx,renderChildrenStub);
TemplateVariant children(TemplateVariant::Delegate::fromFunction(&childCtx,renderChildrenStub));
children.setRaw(TRUE);
c->set("children",children);
m_treeNodes.render(ss,c);
hasChildren=TRUE;
}
else if (list==0)
{
ci->warn(m_templateName,m_line,"recursetree: children attribute has type '%s' instead of list\n",v.typeAsString().data());
}
}
//else
//{
// ci->warn(m_templateName,m_line,"recursetree: children attribute is not valid");
//}
}
if (!hasChildren)
{
......
......@@ -227,7 +227,7 @@ span.lineno a:hover {
background-color: #C8C8C8;
}
div.ah {
div.ah, span.ah {
background-color: black;
font-weight: bold;
color: #ffffff;
......@@ -245,6 +245,15 @@ div.ah {
background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
}
div.classindex ul {
list-style: none;
padding-left: 0;
}
div.classindex span.ai {
display: inline-block;
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
......
......@@ -4,18 +4,46 @@
<div class="textblock">
{% indexentry nav name=tr.classIndex file=page.fileName anchor='' %}
</div>
<div class="classindex" style="column-count:{{ config.COLS_IN_ALPHA_INDEX }};-moz-column-count:{{ config.COLS_IN_ALPHA_INDEX }};-webkit-column-count:{{ config.COLS_IN_ALPHA_INDEX}}">
<ul>
{% with index=classIndex.list|alphaIndex:'name' %}
{# quick index at top #}
<div class="qindex">
{% for section in index %}
<div class="ah">&#160;&#160;{{ section.letter }}&#160;&#160;</div>
<a class="qindex" href="#letter_{{ section.label }}">{{ section.letter }}</a>
{% if not forloop.last %}
&#160;|&#160;
{% endif %}
{% endfor %}
</div>
{# multi column index #}
<div class="classindex" style="column-count:{{ config.COLS_IN_ALPHA_INDEX }};-moz-column-count:{{ config.COLS_IN_ALPHA_INDEX }};-webkit-column-count:{{ config.COLS_IN_ALPHA_INDEX}}">
{% for section in index %}
<ul>
{% for cls in section.items %}
<li>{{ cls.name }}</li>
<li>
<span class="ai">
{% if forloop.first %}
<a name="#letter_{{ section.label }}"></a>
<span class="ah">&#160;&#160;{{ section.letter }}&#160;&#160;</span><br/>
{% endif %}
{% with obj=cls text=cls.name %}
{% include 'htmlobjlink.tpl' %}
{% endwith %}
</span>
</li>
{% endfor %}
</ul>
{% endfor %}
</div><!-- classindex -->
{# quick index at bottom #}
<div class="qindex">
{% for section in index %}
<a class="qindex" href="#letter_{{ section.label }}">{{ section.letter }}</a>
{% if not forloop.last %}
&#160;|&#160;
{% endif %}
{% endfor %}
</div>
{% endwith %}
</ul>
</div><!-- classindex -->
</div><!-- contents -->
{% endblock %}
{% extend 'htmlbase.tpl' %}
{% block content %}
<div class="contents">
<div class="textblock">
<p><a href="hierarchy{{ config.HTML_FILE_EXTENSION }}">{{ tr.gotoTextualHierarchy }}</a></p>
</div>
<table border="0" cellspacing="10" cellpadding="0">
{% for d in classHierarchy.diagrams %}
<tr><td>{{ d.graph }}</td></tr>
{% endfor %}
</table>
</div>
{% endblock %}
{% extend 'htmlbase.tpl' %}
{% block content %}
<div class="contents">
<div class="textblock">
<p>{{ tr.classHierarchyDescription }}</p>
{% if config.HAVE_DOT and config.GRAPHICAL_HIERARCHY %}
<p><a href="inherits{{ config.HTML_FILE_EXTENSION }}">{{ tr.gotoGraphicalHierarchy }}</a></p>
{% endif %}
</div>
{% indexentry nav name=tr.classHierarchy file=page.fileName anchor='' %}
{% opensubindex nav %}
{% with tree=classHierarchy %}
{% include 'htmldirtree.tpl' %}
{% endwith %}
{% closesubindex nav %}
</div>
{% endblock %}
......@@ -179,7 +179,12 @@
{# TODO: write the class inheritance hierarchy #}
{% if classHierarchy.tree %}
{% with page=classHierarchy %}
{# {% create classHierarchy.fileName|append:config.HTML_FILE_EXTENSION from 'hierarchy.tpl' %} #}
{% create classHierarchy.fileName|append:config.HTML_FILE_EXTENSION from 'htmlhierarchy.tpl' %}
{% endwith %}
{% with page=classHierarchy %}
{% if config.HAVE_DOT and config.GRAPHICAL_HIERARCHY %}
{% create 'inherits'|append:config.HTML_FILE_EXTENSION from 'htmlgraphhierarchy.tpl' %}
{% endif %}
{% endwith %}
{% endif %}
......
......@@ -34,7 +34,9 @@
{% if classHierarchy.tree %}
<li><a href="{{ page.relPath }}hierarchy{{ config.HTML_FILE_EXTENSION }}"><span>{{ tr.classHierarchy }} </span></a></li>
{% endif %}
{% if classMembersIndex.all %}
<li><a href="{{ page.relPath }}functions{{ config.HTML_FILE_EXTENSION }}"><span>{{ tr.classMembers }} </span></a></li>
{% endif %}
</ul>
</li>
{% endif %}
......
......@@ -83,7 +83,9 @@
{% if classHierarchy.tree %}
<li{% if page.subhighlight=='classhierarchy' %} class="current"{% endif %}><a href="{{ page.relPath }}hierarchy{{ config.HTML_FILE_EXTENSION }}"><span>{{ tr.classHierarchy|nowrap }}</span></a></li>
{% endif %}
{% if classMembersIndex.all %}
<li{% if page.subhighlight=='classmembers' %} class="current"{% endif %}><a href="{{ page.relPath }}functions{{ config.HTML_FILE_EXTENSION }}"><span>{{ tr.classMembers|nowrap }}</span></a></li>
{% endif %}
{% endif %}
{# file subtabs #}
{% if page.highlight=='files' %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment