summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt20
-rw-r--r--src/classdef.cpp14
-rw-r--r--src/code.l40
-rw-r--r--src/commentcnv.l19
-rw-r--r--src/commentscan.h8
-rw-r--r--src/commentscan.l244
-rw-r--r--src/config.xml16
-rw-r--r--src/configimpl.l24
-rw-r--r--src/context.cpp10
-rw-r--r--src/defgen.cpp5
-rw-r--r--src/dia.cpp2
-rw-r--r--src/dirdef.cpp1
-rw-r--r--src/docbookgen.cpp25
-rw-r--r--src/docbookgen.h12
-rw-r--r--src/docbookvisitor.cpp84
-rw-r--r--src/docbookvisitor.h2
-rw-r--r--src/docgroup.cpp209
-rw-r--r--src/docgroup.h54
-rw-r--r--src/docparser.cpp94
-rw-r--r--src/docparser.h18
-rw-r--r--src/docvisitor.h3
-rw-r--r--src/dot.cpp4537
-rw-r--r--src/dot.h489
-rw-r--r--src/dotcallgraph.cpp217
-rw-r--r--src/dotcallgraph.h52
-rw-r--r--src/dotclassgraph.cpp548
-rw-r--r--src/dotclassgraph.h62
-rw-r--r--src/dotdirdeps.cpp220
-rw-r--r--src/dotdirdeps.h51
-rw-r--r--src/dotfilepatcher.cpp539
-rw-r--r--src/dotfilepatcher.h53
-rw-r--r--src/dotgfxhierarchytable.cpp302
-rw-r--r--src/dotgfxhierarchytable.h55
-rw-r--r--src/dotgraph.cpp400
-rw-r--r--src/dotgraph.h113
-rw-r--r--src/dotgroupcollaboration.cpp381
-rw-r--r--src/dotgroupcollaboration.h85
-rw-r--r--src/dotincldepgraph.cpp238
-rw-r--r--src/dotincldepgraph.h56
-rw-r--r--src/dotnode.cpp950
-rw-r--r--src/dotnode.h138
-rw-r--r--src/dotrunner.cpp294
-rw-r--r--src/dotrunner.h138
-rw-r--r--src/doxygen.cpp14
-rw-r--r--src/doxygen.h2
-rw-r--r--src/filedef.cpp1
-rw-r--r--src/fortranscanner.l6
-rw-r--r--src/groupdef.cpp1
-rw-r--r--src/htags.cpp4
-rw-r--r--src/htmldocvisitor.cpp14
-rw-r--r--src/htmldocvisitor.h2
-rw-r--r--src/htmlgen.cpp53
-rw-r--r--src/htmlgen.h16
-rw-r--r--src/index.cpp3
-rw-r--r--src/latexdocvisitor.cpp16
-rw-r--r--src/latexdocvisitor.h2
-rw-r--r--src/latexgen.cpp36
-rw-r--r--src/latexgen.h12
-rw-r--r--src/layout.cpp5
-rw-r--r--src/layout.h2
-rw-r--r--src/lodepng.cpp2445
-rw-r--r--src/lodepng.h119
-rw-r--r--src/mandocvisitor.cpp20
-rw-r--r--src/mandocvisitor.h2
-rw-r--r--src/mangen.h12
-rw-r--r--src/markdown.cpp5
-rw-r--r--src/memberdef.cpp3
-rw-r--r--src/msc.cpp76
-rw-r--r--src/outputgen.h12
-rw-r--r--src/outputlist.cpp12
-rw-r--r--src/outputlist.h24
-rw-r--r--src/perlmodgen.cpp10
-rw-r--r--src/portable.cpp13
-rw-r--r--src/portable.h1
-rw-r--r--src/pre.l12
-rw-r--r--src/printdocvisitor.h10
-rw-r--r--src/pyscanner.l10
-rw-r--r--src/rtfdocvisitor.cpp22
-rw-r--r--src/rtfdocvisitor.h2
-rw-r--r--src/rtfgen.cpp35
-rw-r--r--src/rtfgen.h18
-rw-r--r--src/scanner.l49
-rw-r--r--src/searchindex.cpp3
-rw-r--r--src/sqlite3gen.cpp6
-rw-r--r--src/tclscanner.l6
-rw-r--r--src/textdocvisitor.h2
-rw-r--r--src/util.cpp44
-rw-r--r--src/util.h1
-rw-r--r--src/vhdljjparser.cpp4
-rw-r--r--src/xmldocvisitor.cpp20
-rw-r--r--src/xmldocvisitor.h2
-rw-r--r--src/xmlgen.cpp6
92 files changed, 5775 insertions, 8242 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 37a21ff..9721bf0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,7 +3,9 @@
include_directories(
${CMAKE_SOURCE_DIR}/qtools
${CMAKE_SOURCE_DIR}/libmd5
- ${CMAKE_SOURCE_DIR}/vhdlparser/
+ ${CMAKE_SOURCE_DIR}/liblodepng
+ ${CMAKE_SOURCE_DIR}/libmscgen
+ ${CMAKE_SOURCE_DIR}/vhdlparser
${CMAKE_SOURCE_DIR}/src
${CLANG_INCLUDEDIR}
${GENERATED_SRC}
@@ -14,7 +16,7 @@ file(MAKE_DIRECTORY ${GENERATED_SRC})
file(GLOB LANGUAGE_FILES "${CMAKE_SOURCE_DIR}/src/translator_??.h")
# instead of increasebuffer.py
-add_definitions(-DYY_BUF_SIZE=262144 -DYY_READ_BUF_SIZE=262144)
+add_definitions(-DYY_BUF_SIZE=${enlarge_lex_buffers} -DYY_READ_BUF_SIZE=${enlarge_lex_buffers})
# generate settings.h
file(GENERATE OUTPUT ${GENERATED_SRC}/settings.h
@@ -185,6 +187,16 @@ add_library(_doxygen STATIC
docparser.cpp
docsets.cpp
dot.cpp
+ dotcallgraph.cpp
+ dotclassgraph.cpp
+ dotdirdeps.cpp
+ dotfilepatcher.cpp
+ dotgfxhierarchytable.cpp
+ dotgraph.cpp
+ dotgroupcollaboration.cpp
+ dotincldepgraph.cpp
+ dotnode.cpp
+ dotrunner.cpp
doxygen.cpp
eclipsehelp.cpp
emoji.cpp
@@ -207,7 +219,6 @@ add_library(_doxygen STATIC
latexdocvisitor.cpp
latexgen.cpp
layout.cpp
- lodepng.cpp
mandocvisitor.cpp
mangen.cpp
sqlite3gen.cpp
@@ -243,6 +254,7 @@ add_library(_doxygen STATIC
xmlgen.cpp
docbookvisitor.cpp
docbookgen.cpp
+ docgroup.cpp
)
add_executable(doxygen main.cpp)
@@ -272,6 +284,8 @@ target_link_libraries(doxygen
doxycfg
qtools
md5
+ lodepng
+ mscgen
vhdlparser
${SQLITE3_LIBRARIES}
${ICONV_LIBRARIES}
diff --git a/src/classdef.cpp b/src/classdef.cpp
index 3a680c5..a7f24ed 100644
--- a/src/classdef.cpp
+++ b/src/classdef.cpp
@@ -33,6 +33,8 @@
#include "example.h"
#include "outputlist.h"
#include "dot.h"
+#include "dotclassgraph.h"
+#include "dotrunner.h"
#include "defargs.h"
#include "debug.h"
#include "docparser.h"
@@ -516,7 +518,7 @@ class ClassDefAliasImpl : public DefinitionAliasImpl, public ClassDef
{ return getCdAlias()->countInheritanceNodes(); }
virtual int countMemberDeclarations(MemberListType lt,const ClassDef *inheritedFrom,
int lt2,bool invert,bool showAlways,QPtrDict<void> *visitedClasses) const
- { return countMemberDeclarations(lt,inheritedFrom,lt2,invert,showAlways,visitedClasses); }
+ { return getCdAlias()->countMemberDeclarations(lt,inheritedFrom,lt2,invert,showAlways,visitedClasses); }
virtual void writeMemberDeclarations(OutputList &ol,MemberListType lt,const QCString &title,
const char *subTitle=0,bool showInline=FALSE,const ClassDef *inheritedFrom=0,
int lt2=-1,bool invert=FALSE,bool showAlways=FALSE,
@@ -1717,8 +1719,12 @@ void ClassDefImpl::writeInheritanceGraph(OutputList &ol) const
(Config_getBool(CLASS_DIAGRAMS) || Config_getBool(CLASS_GRAPH)))
// write class diagram using dot
{
- DotClassGraph inheritanceGraph(this,DotNode::Inheritance);
- if (!inheritanceGraph.isTrivial() && !inheritanceGraph.isTooBig())
+ DotClassGraph inheritanceGraph(this,Inheritance);
+ if (inheritanceGraph.isTooBig())
+ {
+ warn_uncond("Inheritance graph for '%s' not generated, too many nodes. Consider increasing DOT_GRAPH_MAX_NODES.\n",name().data());
+ }
+ else if (!inheritanceGraph.isTrivial())
{
ol.pushGeneratorState();
ol.disable(OutputGenerator::Man);
@@ -1836,7 +1842,7 @@ void ClassDefImpl::writeCollaborationGraph(OutputList &ol) const
{
if (Config_getBool(HAVE_DOT) /*&& Config_getBool(COLLABORATION_GRAPH)*/)
{
- DotClassGraph usageImplGraph(this,DotNode::Collaboration);
+ DotClassGraph usageImplGraph(this,Collaboration);
if (!usageImplGraph.isTrivial())
{
ol.pushGeneratorState();
diff --git a/src/code.l b/src/code.l
index ad39e0e..533875e 100644
--- a/src/code.l
+++ b/src/code.l
@@ -338,15 +338,15 @@ class CallContext
public:
struct Ctx
{
- Ctx() : name(g_name), type(g_type), d(0) {}
+ Ctx(QCString _name, QCString _type) : name(_name), type(_type), d(0) {}
QCString name;
QCString type;
const Definition *d;
};
- CallContext()
+ CallContext()
{
- m_defList.append(new Ctx);
+ m_defList.append(new Ctx("",""));
m_defList.setAutoDelete(TRUE);
}
virtual ~CallContext() {}
@@ -359,12 +359,12 @@ class CallContext
ctx->d=d;
}
}
- void pushScope()
+ void pushScope(QCString _name, QCString _type)
{
- m_defList.append(new Ctx);
+ m_defList.append(new Ctx(_name,_type));
DBG_CTX((stderr,"** Push call context %d\n",m_defList.count()));
}
- void popScope()
+ void popScope(QCString &_name, QCString &_type)
{
if (m_defList.count()>1)
{
@@ -372,8 +372,8 @@ class CallContext
Ctx *ctx = m_defList.getLast();
if (ctx)
{
- g_name = ctx->name;
- g_type = ctx->type;
+ _name = ctx->name;
+ _type = ctx->type;
}
m_defList.removeLast();
}
@@ -386,7 +386,7 @@ class CallContext
{
DBG_CTX((stderr,"** Clear call context\n"));
m_defList.clear();
- m_defList.append(new Ctx);
+ m_defList.append(new Ctx("",""));
}
const Definition *getScope() const
{
@@ -2510,7 +2510,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
}
<Body>"*"{B}*")" { // end of cast?
g_code->codify(yytext);
- g_theCallContext.popScope();
+ g_theCallContext.popScope(g_name, g_type);
g_bracketCount--;
g_parmType = g_name;
BEGIN(FuncCall);
@@ -2520,7 +2520,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
g_name.resize(0);g_type.resize(0);
if (*yytext==')')
{
- g_theCallContext.popScope();
+ g_theCallContext.popScope(g_name, g_type);
g_bracketCount--;
BEGIN(FuncCall);
}
@@ -2852,7 +2852,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
}
else if (*yytext=='[')
{
- g_theCallContext.pushScope();
+ g_theCallContext.pushScope(g_name, g_type);
}
g_args.resize(0);
g_parmType.resize(0);
@@ -2878,7 +2878,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
}
<ObjCMemberCall>"[" {
g_code->codify(yytext);
- g_theCallContext.pushScope();
+ g_theCallContext.pushScope(g_name, g_type);
}
<ObjCMemberCall2>{ID}":"? {
g_name+=yytext;
@@ -2900,7 +2900,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
BEGIN(ObjCMemberCall3);
}
<ObjCMemberCall2,ObjCMemberCall3>"]" {
- g_theCallContext.popScope();
+ g_theCallContext.popScope(g_name, g_type);
g_code->codify(yytext);
BEGIN(Body);
}
@@ -2990,7 +2990,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
<ObjCCall,ObjCMName,ObjCSkipStr>\n { g_currentCtx->format+=*yytext; }
<Body>"]" {
- g_theCallContext.popScope();
+ g_theCallContext.popScope(g_name, g_type);
g_code->codify(yytext);
// TODO: nested arrays like: a[b[0]->func()]->func()
g_name = g_saveName.copy();
@@ -3093,7 +3093,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
g_parmType.resize(0);g_parmName.resize(0);
g_code->codify(yytext);
g_bracketCount++;
- g_theCallContext.pushScope();
+ g_theCallContext.pushScope(g_name, g_type);
if (YY_START==FuncCall && !g_insideBody)
{
g_theVarContext.pushScope();
@@ -3127,7 +3127,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
g_parmName.resize(0);
g_theVarContext.addVariable(g_parmType,g_parmName);
}
- g_theCallContext.popScope();
+ g_theCallContext.popScope(g_name, g_type);
g_inForEachExpression = FALSE;
//g_theCallContext.setClass(0); // commented out, otherwise a()->b() does not work for b().
g_code->codify(yytext);
@@ -3184,7 +3184,7 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
g_theVarContext.pushScope();
}
g_theVarContext.addVariable(g_parmType,g_parmName);
- //g_theCallContext.popScope();
+ //g_theCallContext.popScope(g_name, g_type);
g_parmType.resize(0);g_parmName.resize(0);
int index = g_name.findRev("::");
DBG_CTX((stderr,"g_name=%s\n",g_name.data()));
@@ -3644,11 +3644,11 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\"
}
<*>"("|"[" {
g_code->codify(yytext);
- g_theCallContext.pushScope();
+ g_theCallContext.pushScope(g_name, g_type);
}
<*>")"|"]" {
g_code->codify(yytext);
- g_theCallContext.popScope();
+ g_theCallContext.popScope(g_name, g_type);
}
<*>\n {
g_yyColNr++;
diff --git a/src/commentcnv.l b/src/commentcnv.l
index 0ea5ff7..031b6f5 100644
--- a/src/commentcnv.l
+++ b/src/commentcnv.l
@@ -266,7 +266,7 @@ void replaceComment(int offset);
else
{
g_pythonDocString = TRUE;
- g_nestingCount=0;
+ g_nestingCount=1;
g_commentStack.clear(); /* to be on the save side */
copyToOutput(yytext,(int)yyleng);
BEGIN(CComment);
@@ -281,7 +281,7 @@ void replaceComment(int offset);
else
{
copyToOutput(yytext,(int)yyleng);
- g_nestingCount=0;
+ g_nestingCount=0; // Fortran doesn't have an end comment
g_commentStack.clear(); /* to be on the save side */
BEGIN(CComment);
g_commentStack.push(new CommentCtx(g_lineNr));
@@ -298,7 +298,7 @@ void replaceComment(int offset);
if (isFixedForm && (g_col == 0))
{
copyToOutput(yytext,(int)yyleng);
- g_nestingCount=0;
+ g_nestingCount=0; // Fortran doesn't have an end comment
g_commentStack.clear(); /* to be on the safe side */
BEGIN(CComment);
g_commentStack.push(new CommentCtx(g_lineNr));
@@ -404,7 +404,7 @@ void replaceComment(int offset);
REJECT;
}
g_specialComment=(int)yyleng==3;
- g_nestingCount=0;
+ g_nestingCount=1;
g_commentStack.clear(); /* to be on the save side */
copyToOutput(yytext,(int)yyleng);
BEGIN(CComment);
@@ -418,7 +418,7 @@ void replaceComment(int offset);
else
{
copyToOutput(yytext,(int)yyleng);
- g_nestingCount=0;
+ g_nestingCount=0; // Python doesn't have an end comment for #
g_commentStack.clear(); /* to be on the save side */
BEGIN(CComment);
g_commentStack.push(new CommentCtx(g_lineNr));
@@ -433,7 +433,7 @@ void replaceComment(int offset);
{
g_vhdl = TRUE;
copyToOutput(yytext,(int)yyleng);
- g_nestingCount=0;
+ g_nestingCount=0; // VHDL doesn't have an end comment
g_commentStack.clear(); /* to be on the save side */
BEGIN(CComment);
g_commentStack.push(new CommentCtx(g_lineNr));
@@ -447,7 +447,7 @@ void replaceComment(int offset);
else
{
copyToOutput(yytext,(int)yyleng);
- g_nestingCount=0;
+ g_nestingCount=0; // Fortran doesn't have an end comment
g_commentStack.clear(); /* to be on the save side */
BEGIN(CComment);
g_commentStack.push(new CommentCtx(g_lineNr));
@@ -680,13 +680,14 @@ void replaceComment(int offset);
else
{
copyToOutput(yytext,(int)yyleng);
+ g_nestingCount--;
if (g_nestingCount<=0)
{
BEGIN(Scan);
}
else
{
- g_nestingCount--;
+ //g_nestingCount--;
delete g_commentStack.pop();
}
}
@@ -1119,7 +1120,7 @@ void convertCppComments(BufStr *inBuf,BufStr *outBuf,const char *fileName)
}
tmp += ")";
warn(g_fileName,g_lineNr,"Reached end of file while still inside a (nested) comment. "
- "Nesting level %d %s",g_nestingCount+1,tmp.data()); // add one for "normal" expected end of comment
+ "Nesting level %d %s",g_nestingCount,tmp.data());
}
g_commentStack.clear();
g_nestingCount = 0;
diff --git a/src/commentscan.h b/src/commentscan.h
index 75cd99f..7d2189f 100644
--- a/src/commentscan.h
+++ b/src/commentscan.h
@@ -85,13 +85,5 @@ bool parseCommentBlock(ParserInterface *parser,
bool &newEntryNeeded
);
-void groupEnterFile(const char *file,int line);
-void groupLeaveFile(const char *file,int line);
-void groupLeaveCompound(const char *file,int line,const char *name);
-void groupEnterCompound(const char *file,int line,const char *name);
-void openGroup(Entry *e,const char *file,int line);
-void closeGroup(Entry *,const char *file,int line,bool foundInline=FALSE);
-void initGroupInfo(Entry *e);
-
#endif
diff --git a/src/commentscan.l b/src/commentscan.l
index eb1629e..d2e51e4 100644
--- a/src/commentscan.l
+++ b/src/commentscan.l
@@ -386,11 +386,6 @@ class GuardedSection
bool m_parentVisible;
};
-void openGroup(Entry *e,const char *file,int line);
-void closeGroup(Entry *e,const char *file,int line,bool foundInline=FALSE);
-void initGroupInfo(Entry *e);
-static void groupAddDocs(Entry *e);
-
/* -----------------------------------------------------------------
*
* statics
@@ -451,20 +446,11 @@ static bool g_insideParBlock;
//-----------------------------------------------------------------------------
-static QStack<Grouping> g_autoGroupStack;
-static int g_memberGroupId = DOX_NOGROUP;
-static QCString g_memberGroupHeader;
-static QCString g_memberGroupDocs;
-static QCString g_memberGroupRelates;
-static QCString g_compoundName;
-
-//-----------------------------------------------------------------------------
-
static void initParser()
{
g_sectionLabel.resize(0);
g_sectionTitle.resize(0);
- g_memberGroupHeader.resize(0);
+ Doxygen::docGroup.clearHeader();
g_insideParBlock = FALSE;
}
@@ -1218,12 +1204,12 @@ RCSTAG "$"{ID}":"[^\n$]+"$"
}
<Comment>{B}*{CMD}"{" { // begin of a group
//langParser->handleGroupStartCommand(g_memberGroupHeader);
- openGroup(current,yyFileName,yyLineNr);
+ Doxygen::docGroup.open(current,yyFileName,yyLineNr);
}
<Comment>{B}*{CMD}"}" { // end of a group
//langParser->handleGroupEndCommand();
- closeGroup(current,yyFileName,yyLineNr,TRUE);
- g_memberGroupHeader.resize(0);
+ Doxygen::docGroup.close(current,yyFileName,yyLineNr,TRUE);
+ Doxygen::docGroup.clearHeader();
parseMore=TRUE;
needNewEntry = TRUE;
#if YY_FLEX_MAJOR_VERSION>=2 && (YY_FLEX_MINOR_VERSION>5 || (YY_FLEX_MINOR_VERSION==5 && YY_FLEX_SUBMINOR_VERSION>=33))
@@ -1576,7 +1562,7 @@ RCSTAG "$"{ID}":"[^\n$]+"$"
yyLineNr++;
addOutput('\n');
}
-<GroupDocArg2>[^\n\*]+ { // title (stored in type)
+<GroupDocArg2>[^\n\\]+ { // title (stored in type)
current->type += yytext;
current->type = current->type.stripWhiteSpace();
}
@@ -1594,11 +1580,16 @@ RCSTAG "$"{ID}":"[^\n$]+"$"
addOutput('\n');
BEGIN( Comment );
}
+<GroupDocArg2>. { // title (stored in type)
+ current->type += yytext;
+ current->type = current->type.stripWhiteSpace();
+ }
/* --------- handle arguments of page/mainpage command ------------------- */
<PageDocArg1>{FILE} { // first argument; page name
current->name = stripQuotes(yytext);
+ current->args = "";
BEGIN( PageDocArg2 );
}
<PageDocArg1>{LC} { yyLineNr++;
@@ -1615,17 +1606,21 @@ RCSTAG "$"{ID}":"[^\n$]+"$"
}
<PageDocArg1>. { // ignore other stuff
}
-<PageDocArg2>.*"\n" { // second argument; page title
- yyLineNr++;
+<PageDocArg2>{DOCNL} { // second argument; page title
+ if (*yytext=='\n') yyLineNr++;
+ addOutput('\n');
+ BEGIN( Comment );
+ }
+<PageDocArg2>{CMD}[<>] {
// bug 748927
QCString tmp = yytext;
tmp = substitute(substitute(tmp,"@<","&lt;"),"@>","&gt;");
tmp = substitute(substitute(tmp,"\\<","&lt;"),"\\>","&gt;");
- current->args = tmp;
- addOutput('\n');
- BEGIN( Comment );
+ current->args += tmp;
}
-
+<PageDocArg2>. {
+ current->args += yytext;
+ }
/* --------- handle arguments of the param command ------------ */
<ParamArg1>{ID}/{B}*"," {
addOutput(yytext);
@@ -1965,6 +1960,7 @@ RCSTAG "$"{ID}":"[^\n$]+"$"
addOutput(*yytext);
}
<GuardParamEnd>{B}*{DOCNL} {
+ lineCount();
g_spaceBeforeIf.resize(0);
BEGIN(Comment);
}
@@ -2119,10 +2115,10 @@ RCSTAG "$"{ID}":"[^\n$]+"$"
<NameParam>{LC} { // line continuation
yyLineNr++;
addOutput('\n');
- g_memberGroupHeader+=' ';
+ Doxygen::docGroup.appendHeader(' ');
}
<NameParam>. { // ignore other stuff
- g_memberGroupHeader+=*yytext;
+ Doxygen::docGroup.appendHeader(*yytext);
current->name+=*yytext;
}
@@ -2298,7 +2294,7 @@ RCSTAG "$"{ID}":"[^\n$]+"$"
if (*yytext=='\n') yyLineNr++;
addOutput('\n');
setOutput(OutputDoc);
- addOutput("\\copydetails ");
+ addOutput(" \\copydetails ");
addOutput(g_copyDocArg);
addOutput("\n");
BEGIN(Comment);
@@ -2359,6 +2355,7 @@ static bool handleDefGroup(const QCString &, const QCStringList &)
{
bool stop=makeStructuralIndicator(Entry::GROUPDOC_SEC);
current->groupDocType = Entry::GROUPDOC_NORMAL;
+ setOutput(OutputBrief);
BEGIN( GroupDocArg1 );
return stop;
}
@@ -2462,6 +2459,7 @@ static bool handleMainpage(const QCString &, const QCStringList &)
{
current->name = "mainpage";
}
+ current->name = "";
BEGIN( PageDocArg2 );
return stop;
}
@@ -2540,11 +2538,11 @@ static bool handleName(const QCString &, const QCStringList &)
bool stop=makeStructuralIndicator(Entry::MEMBERGRP_SEC);
if (!stop)
{
- g_memberGroupHeader.resize(0);
+ Doxygen::docGroup.clearHeader();
BEGIN( NameParam );
- if (g_memberGroupId!=DOX_NOGROUP) // end of previous member group
+ if (!Doxygen::docGroup.isEmpty()) // end of previous member group
{
- closeGroup(current,yyFileName,yyLineNr,TRUE);
+ Doxygen::docGroup.close(current,yyFileName,yyLineNr,TRUE);
}
}
return stop;
@@ -3192,9 +3190,9 @@ bool parseCommentBlock(/* in */ ParserInterface *parser,
}
if (current->section==Entry::MEMBERGRP_SEC &&
- g_memberGroupId==DOX_NOGROUP) // @name section but no group started yet
+ Doxygen::docGroup.isEmpty()) // @name section but no group started yet
{
- openGroup(current,yyFileName,yyLineNr);
+ Doxygen::docGroup.open(current,yyFileName,yyLineNr);
}
Debug::print(Debug::CommentScan,0,"-----------\nCommentScanner: %s:%d\noutput=[\n"
@@ -3208,7 +3206,7 @@ bool parseCommentBlock(/* in */ ParserInterface *parser,
checkFormula();
prot = protection;
- groupAddDocs(curEntry);
+ Doxygen::docGroup.addDocs(curEntry);
newEntryNeeded = needNewEntry;
@@ -3227,184 +3225,6 @@ bool parseCommentBlock(/* in */ ParserInterface *parser,
return parseMore;
}
-//---------------------------------------------------------------------------
-
-void groupEnterFile(const char *fileName,int)
-{
- g_autoGroupStack.setAutoDelete(TRUE);
- g_autoGroupStack.clear();
- g_memberGroupId = DOX_NOGROUP;
- g_memberGroupDocs.resize(0);
- g_memberGroupRelates.resize(0);
- g_compoundName=fileName;
-}
-
-void groupLeaveFile(const char *fileName,int line)
-{
- //if (g_memberGroupId!=DOX_NOGROUP)
- //{
- // warn(fileName,line,"end of file while inside a member group\n");
- //}
- g_memberGroupId=DOX_NOGROUP;
- g_memberGroupRelates.resize(0);
- g_memberGroupDocs.resize(0);
- if (!g_autoGroupStack.isEmpty())
- {
- warn(fileName,line,"end of file while inside a group\n");
- }
-}
-
-void groupEnterCompound(const char *fileName,int line,const char *name)
-{
- if (g_memberGroupId!=DOX_NOGROUP)
- {
- warn(fileName,line,"try to put compound %s inside a member group\n",name);
- }
- g_memberGroupId=DOX_NOGROUP;
- g_memberGroupRelates.resize(0);
- g_memberGroupDocs.resize(0);
- g_compoundName = name;
- int i = g_compoundName.find('(');
- if (i!=-1)
- {
- g_compoundName=g_compoundName.left(i); // strip category (Obj-C)
- }
- if (g_compoundName.isEmpty())
- {
- g_compoundName=fileName;
- }
- //printf("groupEnterCompound(%s)\n",name);
-}
-
-void groupLeaveCompound(const char *,int,const char * /*name*/)
-{
- //printf("groupLeaveCompound(%s)\n",name);
- //if (g_memberGroupId!=DOX_NOGROUP)
- //{
- // warn(fileName,line,"end of compound %s while inside a member group\n",name);
- //}
- g_memberGroupId=DOX_NOGROUP;
- g_memberGroupRelates.resize(0);
- g_memberGroupDocs.resize(0);
- g_compoundName.resize(0);
-}
-
-static int findExistingGroup(int &groupId,const MemberGroupInfo *info)
-{
- //printf("findExistingGroup %s:%s\n",info->header.data(),info->compoundName.data());
- QIntDictIterator<MemberGroupInfo> di(Doxygen::memGrpInfoDict);
- MemberGroupInfo *mi;
- for (di.toFirst();(mi=di.current());++di)
- {
- if (g_compoundName==mi->compoundName && // same file or scope
- !mi->header.isEmpty() && // not a nameless group
- qstricmp(mi->header,info->header)==0 // same header name
- )
- {
- //printf("Found it!\n");
- return (int)di.currentKey(); // put the item in this group
- }
- }
- groupId++; // start new group
- return groupId;
-}
-
-void openGroup(Entry *e,const char *,int)
-{
- //printf("==> openGroup(name=%s,sec=%x) g_autoGroupStack=%d\n",
- // e->name.data(),e->section,g_autoGroupStack.count());
- if (e->section==Entry::GROUPDOC_SEC) // auto group
- {
- g_autoGroupStack.push(new Grouping(e->name,e->groupingPri()));
- }
- else // start of a member group
- {
- //printf(" membergroup id=%d %s\n",g_memberGroupId,g_memberGroupHeader.data());
- if (g_memberGroupId==DOX_NOGROUP) // no group started yet
- {
- static int curGroupId=0;
-
- MemberGroupInfo *info = new MemberGroupInfo;
- info->header = g_memberGroupHeader.stripWhiteSpace();
- info->compoundName = g_compoundName;
- g_memberGroupId = findExistingGroup(curGroupId,info);
- //printf(" use membergroup %d\n",g_memberGroupId);
- Doxygen::memGrpInfoDict.insert(g_memberGroupId,info);
-
- g_memberGroupRelates = e->relates;
- e->mGrpId = g_memberGroupId;
- }
- }
-}
-
-void closeGroup(Entry *e,const char *fileName,int line,bool foundInline)
-{
- //printf("==> closeGroup(name=%s,sec=%x,file=%s,line=%d) g_autoGroupStack=%d\n",
- // e->name.data(),e->section,fileName,line,g_autoGroupStack.count());
- if (g_memberGroupId!=DOX_NOGROUP) // end of member group
- {
- MemberGroupInfo *info=Doxygen::memGrpInfoDict.find(g_memberGroupId);
- if (info) // known group
- {
- info->doc = g_memberGroupDocs;
- info->docFile = fileName;
- info->docLine = line;
- }
- g_memberGroupId=DOX_NOGROUP;
- g_memberGroupRelates.resize(0);
- g_memberGroupDocs.resize(0);
- if (!foundInline) e->mGrpId=DOX_NOGROUP;
- //printf("new group id=%d\n",g_memberGroupId);
- }
- else if (!g_autoGroupStack.isEmpty()) // end of auto group
- {
- Grouping *grp = g_autoGroupStack.pop();
- // see bug577005: we should not remove the last group for e
- if (!foundInline) e->groups->removeLast();
- //printf("Removing %s e=%p\n",grp->groupname.data(),e);
- delete grp;
- if (!foundInline) initGroupInfo(e);
- }
-}
-
-void initGroupInfo(Entry *e)
-{
- //printf("==> initGroup(id=%d,related=%s,e=%p)\n",g_memberGroupId,
- // g_memberGroupRelates.data(),e);
- e->mGrpId = g_memberGroupId;
- e->relates = g_memberGroupRelates;
- if (!g_autoGroupStack.isEmpty())
- {
- //printf("Appending group %s to %s: count=%d entry=%p\n",
- // g_autoGroupStack.top()->groupname.data(),
- // e->name.data(),e->groups->count(),e);
- e->groups->append(new Grouping(*g_autoGroupStack.top()));
- }
-}
-
-static void groupAddDocs(Entry *e)
-{
- if (e->section==Entry::MEMBERGRP_SEC)
- {
- g_memberGroupDocs=e->brief.stripWhiteSpace();
- e->doc = stripLeadingAndTrailingEmptyLines(e->doc,e->docLine);
- if (!g_memberGroupDocs.isEmpty() && !e->doc.isEmpty())
- {
- g_memberGroupDocs+="\n\n";
- }
- g_memberGroupDocs+=e->doc;
- MemberGroupInfo *info=Doxygen::memGrpInfoDict.find(g_memberGroupId);
- if (info)
- {
- info->doc = g_memberGroupDocs;
- info->docFile = e->docFile;
- info->docLine = e->docLine;
- info->setRefItems(e->sli);
- }
- e->doc.resize(0);
- e->brief.resize(0);
- }
-}
static void handleGuard(const QCString &expr)
{
diff --git a/src/config.xml b/src/config.xml
index 0bf34a8..8e19d67 100644
--- a/src/config.xml
+++ b/src/config.xml
@@ -960,8 +960,8 @@ Go to the <a href="commands.html">next</a> section or return to the
will only generate file names in lower-case letters. If set to
\c YES, upper-case letters are also allowed. This is useful if you have
classes or files whose names only differ in case and if your file system
- supports case sensitive file names. Windows and Mac users are advised to set this
- option to \c NO.
+ supports case sensitive file names. Windows (including Cygwin) ands
+ Mac users are advised to set this option to \c NO.
]]>
</docs>
</option>
@@ -3251,17 +3251,6 @@ where `loc1` and `loc2` can be relative or absolute paths or URLs.
]]>
</docs>
</option>
- <option type='string' id='MSCGEN_PATH' format='dir' defval=''>
- <docs>
-<![CDATA[
- You can define message sequence charts within doxygen comments using the \ref cmdmsc "\\msc"
- command. Doxygen will then run the <a href="http://www.mcternan.me.uk/mscgen/">mscgen tool</a>) to
- produce the chart and insert it in the documentation. The <code>MSCGEN_PATH</code> tag allows you to
- specify the directory where the \c mscgen tool resides. If left empty the tool is assumed to
- be found in the default search path.
-]]>
- </docs>
- </option>
<option type='string' id='DIA_PATH' format='dir' defval=''>
<docs>
<![CDATA[
@@ -3641,5 +3630,6 @@ remove the intermediate dot files that are used to generate the various graphs.
<option type='obsolete' id='XML_SCHEMA'/>
<option type='obsolete' id='XML_DTD'/>
<option type='obsolete' id='PERL_PATH'/>
+ <option type='obsolete' id='MSCGEN_PATH'/>
</group>
</doxygenconfig>
diff --git a/src/configimpl.l b/src/configimpl.l
index d114b4a..bc08cbf 100644
--- a/src/configimpl.l
+++ b/src/configimpl.l
@@ -1565,30 +1565,6 @@ void Config::checkAndCorrect()
dotPath="";
}
- // check mscgen path
- QCString &mscgenPath = ConfigImpl_getString("MSCGEN_PATH");
- if (!mscgenPath.isEmpty())
- {
- QFileInfo dp(mscgenPath+"/mscgen"+portable_commandExtension());
- if (!dp.exists() || !dp.isFile())
- {
- warn_uncond("the mscgen tool could not be found at %s\n",mscgenPath.data());
- mscgenPath="";
- }
- else
- {
- mscgenPath=dp.dirPath(TRUE).utf8()+"/";
-#if defined(_WIN32) // convert slashes
- uint i=0,l=mscgenPath.length();
- for (i=0;i<l;i++) if (mscgenPath.at(i)=='/') mscgenPath.at(i)='\\';
-#endif
- }
- }
- else // make sure the string is empty but not null!
- {
- mscgenPath="";
- }
-
// check plantuml path
QCString &plantumlJarPath = ConfigImpl_getString("PLANTUML_JAR_PATH");
if (!plantumlJarPath.isEmpty())
diff --git a/src/context.cpp b/src/context.cpp
index 26a5b0e..66206b1 100644
--- a/src/context.cpp
+++ b/src/context.cpp
@@ -39,6 +39,12 @@
#include "latexgen.h"
#include "latexdocvisitor.h"
#include "dot.h"
+#include "dotcallgraph.h"
+#include "dotclassgraph.h"
+#include "dotdirdeps.h"
+#include "dotgfxhierarchytable.h"
+#include "dotgroupcollaboration.h"
+#include "dotincldepgraph.h"
#include "diagram.h"
#include "example.h"
#include "membername.h"
@@ -1926,7 +1932,7 @@ class ClassContext::Private : public DefinitionContext<ClassContext::Private>
Cachable &cache = getCache();
if (!cache.classGraph)
{
- cache.classGraph.reset(new DotClassGraph(m_classDef,DotNode::Inheritance));
+ cache.classGraph.reset(new DotClassGraph(m_classDef,Inheritance));
}
return cache.classGraph.get();
}
@@ -2048,7 +2054,7 @@ class ClassContext::Private : public DefinitionContext<ClassContext::Private>
Cachable &cache = getCache();
if (!cache.collaborationGraph)
{
- cache.collaborationGraph.reset(new DotClassGraph(m_classDef,DotNode::Collaboration));
+ cache.collaborationGraph.reset(new DotClassGraph(m_classDef,Collaboration));
}
return cache.collaborationGraph.get();
}
diff --git a/src/defgen.cpp b/src/defgen.cpp
index 6601f6e..ab19c2a 100644
--- a/src/defgen.cpp
+++ b/src/defgen.cpp
@@ -27,6 +27,7 @@
#include "defargs.h"
#include "outputgen.h"
#include "dot.h"
+#include "dotclassgraph.h"
#include "arguments.h"
#include "memberlist.h"
#include "namespacedef.h"
@@ -466,14 +467,14 @@ void generateDEFForClass(ClassDef *cd,FTextStream &t)
t << " cp-documentation = <<_EnD_oF_dEf_TeXt_" << endl
<< cd->documentation() << endl << "_EnD_oF_dEf_TeXt_;" << endl;
- DotClassGraph inheritanceGraph(cd,DotNode::Inheritance);
+ DotClassGraph inheritanceGraph(cd,Inheritance);
if (!inheritanceGraph.isTrivial())
{
t << " cp-inheritancegraph = <<_EnD_oF_dEf_TeXt_" << endl;
inheritanceGraph.writeDEF(t);
t << endl << "_EnD_oF_dEf_TeXt_;" << endl;
}
- DotClassGraph collaborationGraph(cd,DotNode::Collaboration);
+ DotClassGraph collaborationGraph(cd,Collaboration);
if (!collaborationGraph.isTrivial())
{
t << " cp-collaborationgraph = <<_EnD_oF_dEf_TeXt_" << endl;
diff --git a/src/dia.cpp b/src/dia.cpp
index 5adbc7c..8dab5b0 100644
--- a/src/dia.cpp
+++ b/src/dia.cpp
@@ -65,6 +65,8 @@ void writeDiaGraphFromFile(const char *inFile,const char *outDir,
portable_sysTimerStart();
if ((exitCode=portable_system(diaExe,diaArgs,FALSE))!=0)
{
+ err("Problems running %s. Check your installation or look typos in you dia file %s\n",
+ diaExe.data(),inFile);
portable_sysTimerStop();
goto error;
}
diff --git a/src/dirdef.cpp b/src/dirdef.cpp
index 3803335..5db8b99 100644
--- a/src/dirdef.cpp
+++ b/src/dirdef.cpp
@@ -8,6 +8,7 @@
#include "language.h"
#include "message.h"
#include "dot.h"
+#include "dotdirdeps.h"
#include "layout.h"
#include "ftextstream.h"
#include "config.h"
diff --git a/src/docbookgen.cpp b/src/docbookgen.cpp
index 8a062fc..7fe849a 100644
--- a/src/docbookgen.cpp
+++ b/src/docbookgen.cpp
@@ -33,6 +33,11 @@
#include "defargs.h"
#include "outputgen.h"
#include "dot.h"
+#include "dotcallgraph.h"
+#include "dotclassgraph.h"
+#include "dotdirdeps.h"
+#include "dotgroupcollaboration.h"
+#include "dotincldepgraph.h"
#include "pagedef.h"
#include "filename.h"
#include "version.h"
@@ -180,7 +185,7 @@ void DocbookCodeGenerator::startCodeLine(bool)
}
void DocbookCodeGenerator::endCodeLine()
{
- m_t << endl;
+ if (m_insideCodeLine) m_t << endl;
Docbook_DB(("(endCodeLine)\n"));
m_lineNumber = -1;
m_refId.resize(0);
@@ -238,7 +243,7 @@ void DocbookCodeGenerator::addWord(const char *,bool)
}
void DocbookCodeGenerator::finish()
{
- if (m_insideCodeLine) endCodeLine();
+ endCodeLine();
}
void DocbookCodeGenerator::startCodeFragment()
{
@@ -246,6 +251,9 @@ void DocbookCodeGenerator::startCodeFragment()
}
void DocbookCodeGenerator::endCodeFragment()
{
+ //endCodeLine checks is there is still an open code line, if so closes it.
+ endCodeLine();
+
m_t << "</computeroutput></literallayout>" << endl;
}
@@ -1002,6 +1010,9 @@ DB_GEN_C
void DocbookGenerator::endCodeFragment()
{
DB_GEN_C
+ //endCodeLine checks is there is still an open code line, if so closes it.
+ endCodeLine();
+
t << "</programlisting>";
}
void DocbookGenerator::startMemberTemplateParams()
@@ -1095,7 +1106,7 @@ void DocbookGenerator::startGroupCollaboration()
{
DB_GEN_C
}
-void DocbookGenerator::endGroupCollaboration(const DotGroupCollaboration &g)
+void DocbookGenerator::endGroupCollaboration(DotGroupCollaboration &g)
{
DB_GEN_C
g.writeGraph(t,GOF_BITMAP,EOF_DocBook,Config_getString(DOCBOOK_OUTPUT),fileName,relPath,FALSE);
@@ -1104,7 +1115,7 @@ void DocbookGenerator::startDotGraph()
{
DB_GEN_C
}
-void DocbookGenerator::endDotGraph(const DotClassGraph &g)
+void DocbookGenerator::endDotGraph(DotClassGraph &g)
{
DB_GEN_C
g.writeGraph(t,GOF_BITMAP,EOF_DocBook,Config_getString(DOCBOOK_OUTPUT),fileName,relPath,TRUE,FALSE);
@@ -1113,7 +1124,7 @@ void DocbookGenerator::startInclDepGraph()
{
DB_GEN_C
}
-void DocbookGenerator::endInclDepGraph(const DotInclDepGraph &g)
+void DocbookGenerator::endInclDepGraph(DotInclDepGraph &g)
{
DB_GEN_C
QCString fn = g.writeGraph(t,GOF_BITMAP,EOF_DocBook,Config_getString(DOCBOOK_OUTPUT), fileName,relPath,FALSE);
@@ -1122,7 +1133,7 @@ void DocbookGenerator::startCallGraph()
{
DB_GEN_C
}
-void DocbookGenerator::endCallGraph(const DotCallGraph &g)
+void DocbookGenerator::endCallGraph(DotCallGraph &g)
{
DB_GEN_C
QCString fn = g.writeGraph(t,GOF_BITMAP,EOF_DocBook,Config_getString(DOCBOOK_OUTPUT), fileName,relPath,FALSE);
@@ -1131,7 +1142,7 @@ void DocbookGenerator::startDirDepGraph()
{
DB_GEN_C
}
-void DocbookGenerator::endDirDepGraph(const DotDirDeps &g)
+void DocbookGenerator::endDirDepGraph(DotDirDeps &g)
{
DB_GEN_C
QCString fn = g.writeGraph(t,GOF_BITMAP,EOF_DocBook,Config_getString(DOCBOOK_OUTPUT), fileName,relPath,FALSE);
diff --git a/src/docbookgen.h b/src/docbookgen.h
index 8674150..8f71722 100644
--- a/src/docbookgen.h
+++ b/src/docbookgen.h
@@ -281,16 +281,16 @@ class DocbookGenerator : public OutputGenerator
void startClassDiagram();
void endClassDiagram(const ClassDiagram &,const char *,const char *);
void startDotGraph();
- void endDotGraph(const DotClassGraph &g);
+ void endDotGraph(DotClassGraph &g);
void startInclDepGraph();
- void endInclDepGraph(const DotInclDepGraph &g);
+ void endInclDepGraph(DotInclDepGraph &g);
void startGroupCollaboration();
- void endGroupCollaboration(const DotGroupCollaboration &g);
+ void endGroupCollaboration(DotGroupCollaboration &g);
void startCallGraph();
- void endCallGraph(const DotCallGraph &g);
+ void endCallGraph(DotCallGraph &g);
void startDirDepGraph();
- void endDirDepGraph(const DotDirDeps &g);
- void writeGraphicalHierarchy(const DotGfxHierarchyTable &g){DB_GEN_NEW};
+ void endDirDepGraph(DotDirDeps &g);
+ void writeGraphicalHierarchy(DotGfxHierarchyTable &g){DB_GEN_NEW};
void startQuickIndices(){DB_GEN_EMPTY};
void endQuickIndices(){DB_GEN_EMPTY};
void writeSplitBar(const char *){DB_GEN_EMPTY};
diff --git a/src/docbookvisitor.cpp b/src/docbookvisitor.cpp
index 64425c6..1901454 100644
--- a/src/docbookvisitor.cpp
+++ b/src/docbookvisitor.cpp
@@ -472,20 +472,22 @@ DB_VIS_C
pushEnabled();
m_hide = TRUE;
}
- SrcLangExt langExt = getLanguageFromFileName(m_langExt);
+ QCString locLangExt = getFileNameExtension(op->includeFileName());
+ if (locLangExt.isEmpty()) locLangExt = m_langExt;
+ SrcLangExt langExt = getLanguageFromFileName(locLangExt);
if (op->type()!=DocIncOperator::Skip)
{
popEnabled();
if (!m_hide)
{
- FileDef *fd;
+ FileDef *fd = 0;
if (!op->includeFileName().isEmpty())
{
QFileInfo cfi( op->includeFileName() );
fd = createFileDef( cfi.dirPath().utf8(), cfi.fileName().utf8() );
}
- Doxygen::parserManager->getParser(m_langExt)
+ Doxygen::parserManager->getParser(locLangExt)
->parseCode(m_ci,op->context(),
op->text(),langExt,op->isExample(),
op->exampleFile(),
@@ -634,152 +636,152 @@ DB_VIS_C
case DocSimpleSect::See:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trSeeAlso() << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trSeeAlso() << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trSeeAlso()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trSeeAlso()) << "</title>" << endl;
}
break;
case DocSimpleSect::Return:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trReturns()<< ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trReturns()<< "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trReturns()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trReturns()) << "</title>" << endl;
}
break;
case DocSimpleSect::Author:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trAuthor(TRUE, TRUE) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trAuthor(TRUE, TRUE) << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trAuthor(TRUE, TRUE)) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trAuthor(TRUE, TRUE)) << "</title>" << endl;
}
break;
case DocSimpleSect::Authors:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trAuthor(TRUE, FALSE) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trAuthor(TRUE, FALSE) << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trAuthor(TRUE, FALSE)) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trAuthor(TRUE, FALSE)) << "</title>" << endl;
}
break;
case DocSimpleSect::Version:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trVersion() << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trVersion() << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trVersion()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trVersion()) << "</title>" << endl;
}
break;
case DocSimpleSect::Since:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trSince() << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trSince() << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trSince()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trSince()) << "</title>" << endl;
}
break;
case DocSimpleSect::Date:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trDate() << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trDate() << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trDate()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trDate()) << "</title>" << endl;
}
break;
case DocSimpleSect::Note:
if (m_insidePre)
{
- m_t << "<note><title>" << theTranslator->trNote() << ": </title>" << endl;
+ m_t << "<note><title>" << theTranslator->trNote() << "</title>" << endl;
}
else
{
- m_t << "<note><title>" << convertToDocBook(theTranslator->trNote()) << ": </title>" << endl;
+ m_t << "<note><title>" << convertToDocBook(theTranslator->trNote()) << "</title>" << endl;
}
break;
case DocSimpleSect::Warning:
if (m_insidePre)
{
- m_t << "<warning><title>" << theTranslator->trWarning() << ": </title>" << endl;
+ m_t << "<warning><title>" << theTranslator->trWarning() << "</title>" << endl;
}
else
{
- m_t << "<warning><title>" << convertToDocBook(theTranslator->trWarning()) << ": </title>" << endl;
+ m_t << "<warning><title>" << convertToDocBook(theTranslator->trWarning()) << "</title>" << endl;
}
break;
case DocSimpleSect::Pre:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trPrecondition() << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trPrecondition() << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trPrecondition()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trPrecondition()) << "</title>" << endl;
}
break;
case DocSimpleSect::Post:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trPostcondition() << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trPostcondition() << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trPostcondition()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trPostcondition()) << "</title>" << endl;
}
break;
case DocSimpleSect::Copyright:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trCopyright() << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trCopyright() << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trCopyright()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trCopyright()) << "</title>" << endl;
}
break;
case DocSimpleSect::Invar:
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trInvariant() << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trInvariant() << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trInvariant()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trInvariant()) << "</title>" << endl;
}
break;
case DocSimpleSect::Remark:
// <remark> is miising the <title> possibility
if (m_insidePre)
{
- m_t << "<formalpara><title>" << theTranslator->trRemarks() << ": </title>" << endl;
+ m_t << "<formalpara><title>" << theTranslator->trRemarks() << "</title>" << endl;
}
else
{
- m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trRemarks()) << ": </title>" << endl;
+ m_t << "<formalpara><title>" << convertToDocBook(theTranslator->trRemarks()) << "</title>" << endl;
}
break;
case DocSimpleSect::Attention:
if (m_insidePre)
{
- m_t << "<caution><title>" << theTranslator->trAttention() << ": </title>" << endl;
+ m_t << "<caution><title>" << theTranslator->trAttention() << "</title>" << endl;
}
else
{
- m_t << "<caution><title>" << convertToDocBook(theTranslator->trAttention()) << ": </title>" << endl;
+ m_t << "<caution><title>" << convertToDocBook(theTranslator->trAttention()) << "</title>" << endl;
}
break;
case DocSimpleSect::User:
@@ -1521,22 +1523,6 @@ DB_VIS_C
m_t << " ";
}
-void DocbookDocVisitor::visitPre(DocCopy *)
-{
-DB_VIS_C
- if (m_hide) return;
- // TODO: to be implemented
-}
-
-
-void DocbookDocVisitor::visitPost(DocCopy *)
-{
-DB_VIS_C
- if (m_hide) return;
- // TODO: to be implemented
-}
-
-
void DocbookDocVisitor::visitPre(DocText *)
{
DB_VIS_C
diff --git a/src/docbookvisitor.h b/src/docbookvisitor.h
index 24b1fbb..47275f7 100644
--- a/src/docbookvisitor.h
+++ b/src/docbookvisitor.h
@@ -127,8 +127,6 @@ class DocbookDocVisitor : public DocVisitor
void visitPost(DocXRefItem *);
void visitPre(DocInternalRef *);
void visitPost(DocInternalRef *);
- void visitPre(DocCopy *);
- void visitPost(DocCopy *);
void visitPre(DocText *);
void visitPost(DocText *);
void visitPre(DocHtmlBlockQuote *);
diff --git a/src/docgroup.cpp b/src/docgroup.cpp
new file mode 100644
index 0000000..2ed7473
--- /dev/null
+++ b/src/docgroup.cpp
@@ -0,0 +1,209 @@
+#include "doxygen.h"
+#include "util.h"
+#include "entry.h"
+#include "message.h"
+#include "docgroup.h"
+
+
+void DocGroup::enterFile(const char *fileName,int)
+{
+ m_openCount = 0;
+ m_autoGroupStack.setAutoDelete(TRUE);
+ m_autoGroupStack.clear();
+ m_memberGroupId = DOX_NOGROUP;
+ m_memberGroupDocs.resize(0);
+ m_memberGroupRelates.resize(0);
+ m_compoundName=fileName;
+}
+
+void DocGroup::leaveFile(const char *fileName,int line)
+{
+ //if (m_memberGroupId!=DOX_NOGROUP)
+ //{
+ // warn(fileName,line,"end of file while inside a member group\n");
+ //}
+ m_memberGroupId=DOX_NOGROUP;
+ m_memberGroupRelates.resize(0);
+ m_memberGroupDocs.resize(0);
+ if (!m_autoGroupStack.isEmpty())
+ {
+ warn(fileName,line,"end of file while inside a group");
+ }
+ else if (m_openCount > 0) // < 0 is already handled on close call
+ {
+ warn(fileName,line,"end of file with unbalanced grouping commands");
+ }
+}
+
+void DocGroup::enterCompound(const char *fileName,int line,const char *name)
+{
+ if (m_memberGroupId!=DOX_NOGROUP)
+ {
+ warn(fileName,line,"try to put compound %s inside a member group\n",name);
+ }
+ m_memberGroupId=DOX_NOGROUP;
+ m_memberGroupRelates.resize(0);
+ m_memberGroupDocs.resize(0);
+ m_compoundName = name;
+ int i = m_compoundName.find('(');
+ if (i!=-1)
+ {
+ m_compoundName=m_compoundName.left(i); // strip category (Obj-C)
+ }
+ if (m_compoundName.isEmpty())
+ {
+ m_compoundName=fileName;
+ }
+ //printf("groupEnterCompound(%s)\n",name);
+}
+
+void DocGroup::leaveCompound(const char *,int,const char * /*name*/)
+{
+ //printf("groupLeaveCompound(%s)\n",name);
+ //if (m_memberGroupId!=DOX_NOGROUP)
+ //{
+ // warn(fileName,line,"end of compound %s while inside a member group\n",name);
+ //}
+ m_memberGroupId=DOX_NOGROUP;
+ m_memberGroupRelates.resize(0);
+ m_memberGroupDocs.resize(0);
+ m_compoundName.resize(0);
+}
+
+int DocGroup::findExistingGroup(int &groupId,const MemberGroupInfo *info)
+{
+ //printf("findExistingGroup %s:%s\n",info->header.data(),info->compoundName.data());
+ QIntDictIterator<MemberGroupInfo> di(Doxygen::memGrpInfoDict);
+ MemberGroupInfo *mi;
+ for (di.toFirst();(mi=di.current());++di)
+ {
+ if (m_compoundName==mi->compoundName && // same file or scope
+ !mi->header.isEmpty() && // not a nameless group
+ qstricmp(mi->header,info->header)==0 // same header name
+ )
+ {
+ //printf("Found it!\n");
+ return (int)di.currentKey(); // put the item in this group
+ }
+ }
+ groupId++; // start new group
+ return groupId;
+}
+
+void DocGroup::open(Entry *e,const char *,int)
+{
+ m_openCount++;
+ //printf("==> openGroup(name=%s,sec=%x) m_autoGroupStack=%d\n",
+ // e->name.data(),e->section,m_autoGroupStack.count());
+ if (e->section==Entry::GROUPDOC_SEC) // auto group
+ {
+ m_autoGroupStack.push(new Grouping(e->name,e->groupingPri()));
+ }
+ else // start of a member group
+ {
+ //printf(" membergroup id=%d %s\n",m_memberGroupId,m_memberGroupHeader.data());
+ if (m_memberGroupId==DOX_NOGROUP) // no group started yet
+ {
+ static int curGroupId=0;
+
+ MemberGroupInfo *info = new MemberGroupInfo;
+ info->header = m_memberGroupHeader.stripWhiteSpace();
+ info->compoundName = m_compoundName;
+ m_memberGroupId = findExistingGroup(curGroupId,info);
+ //printf(" use membergroup %d\n",m_memberGroupId);
+ Doxygen::memGrpInfoDict.insert(m_memberGroupId,info);
+
+ m_memberGroupRelates = e->relates;
+ e->mGrpId = m_memberGroupId;
+ }
+ }
+}
+
+void DocGroup::close(Entry *e,const char *fileName,int line,bool foundInline)
+{
+ m_openCount--;
+ if (m_openCount < 0)
+ {
+ warn(fileName,line,"unbalanced grouping commands");
+ }
+ //printf("==> closeGroup(name=%s,sec=%x,file=%s,line=%d) m_autoGroupStack=%d\n",
+ // e->name.data(),e->section,fileName,line,m_autoGroupStack.count());
+ if (m_memberGroupId!=DOX_NOGROUP) // end of member group
+ {
+ MemberGroupInfo *info=Doxygen::memGrpInfoDict.find(m_memberGroupId);
+ if (info) // known group
+ {
+ info->doc = m_memberGroupDocs;
+ info->docFile = fileName;
+ info->docLine = line;
+ }
+ m_memberGroupId=DOX_NOGROUP;
+ m_memberGroupRelates.resize(0);
+ m_memberGroupDocs.resize(0);
+ if (!foundInline) e->mGrpId=DOX_NOGROUP;
+ //printf("new group id=%d\n",m_memberGroupId);
+ }
+ else if (!m_autoGroupStack.isEmpty()) // end of auto group
+ {
+ Grouping *grp = m_autoGroupStack.pop();
+ // see bug577005: we should not remove the last group for e
+ if (!foundInline) e->groups->removeLast();
+ //printf("Removing %s e=%p\n",grp->groupname.data(),e);
+ delete grp;
+ if (!foundInline) initGroupInfo(e);
+ }
+}
+
+void DocGroup::initGroupInfo(Entry *e)
+{
+ //printf("==> initGroup(id=%d,related=%s,e=%p)\n",m_memberGroupId,
+ // m_memberGroupRelates.data(),e);
+ e->mGrpId = m_memberGroupId;
+ e->relates = m_memberGroupRelates;
+ if (!m_autoGroupStack.isEmpty())
+ {
+ //printf("Appending group %s to %s: count=%d entry=%p\n",
+ // m_autoGroupStack.top()->groupname.data(),
+ // e->name.data(),e->groups->count(),e);
+ e->groups->append(new Grouping(*m_autoGroupStack.top()));
+ }
+}
+
+void DocGroup::addDocs(Entry *e)
+{
+ if (e->section==Entry::MEMBERGRP_SEC)
+ {
+ m_memberGroupDocs=e->brief.stripWhiteSpace();
+ e->doc = stripLeadingAndTrailingEmptyLines(e->doc,e->docLine);
+ if (!m_memberGroupDocs.isEmpty() && !e->doc.isEmpty())
+ {
+ m_memberGroupDocs+="\n\n";
+ }
+ m_memberGroupDocs+=e->doc;
+ MemberGroupInfo *info=Doxygen::memGrpInfoDict.find(m_memberGroupId);
+ if (info)
+ {
+ info->doc = m_memberGroupDocs;
+ info->docFile = e->docFile;
+ info->docLine = e->docLine;
+ info->setRefItems(e->sli);
+ }
+ e->doc.resize(0);
+ e->brief.resize(0);
+ }
+}
+
+bool DocGroup::isEmpty() const
+{
+ return (m_memberGroupId==DOX_NOGROUP);
+}
+
+void DocGroup::clearHeader()
+{
+ m_memberGroupHeader.resize(0);
+}
+
+void DocGroup::appendHeader(const char text)
+{
+ m_memberGroupHeader += text;
+}
diff --git a/src/docgroup.h b/src/docgroup.h
new file mode 100644
index 0000000..38ee997
--- /dev/null
+++ b/src/docgroup.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1997-2019 by Dimitri van Heesch.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby
+ * granted. No representations are made about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * Documents produced by Doxygen are derivative works derived from the
+ * input used in their production; they are not affected by this license.
+ *
+ */
+
+#ifndef DOCGROUP_H
+#define DOCGROUP_H
+
+#include <qstack.h>
+#include <qstring.h>
+#include "membergroup.h"
+
+class Entry;
+
+class DocGroup
+{
+ public:
+ DocGroup() {};
+
+ public:
+ void enterFile(const char *fileName,int);
+ void leaveFile(const char *fileName,int line);
+ void enterCompound(const char *fileName,int line,const char *name);
+ void leaveCompound(const char *,int,const char * /*name*/);
+ void open(Entry *e,const char *,int);
+ void close(Entry *e,const char *fileName,int line,bool foundInline);
+ void initGroupInfo(Entry *e);
+ bool isEmpty() const;
+ void clearHeader();
+ void appendHeader(const char);
+ void addDocs(Entry *e);
+
+ private:
+ int findExistingGroup(int &groupId,const MemberGroupInfo *info);
+ int m_openCount;
+ QCString m_memberGroupHeader;
+ int m_memberGroupId;
+ QCString m_memberGroupRelates;
+ QCString m_memberGroupDocs;
+ QStack<Grouping> m_autoGroupStack;
+ QCString m_compoundName;
+};
+
+#endif
diff --git a/src/docparser.cpp b/src/docparser.cpp
index 70eb1d8..3a526d4 100644
--- a/src/docparser.cpp
+++ b/src/docparser.cpp
@@ -2098,100 +2098,6 @@ void DocIncOperator::parse()
//---------------------------------------------------------------------------
-void DocCopy::parse(QList<DocNode> &children)
-{
- QCString doc,brief;
- const Definition *def = 0;
- if (findDocsForMemberOrCompound(m_link,&doc,&brief,&def))
- {
- if (g_copyStack.findRef(def)==-1) // definition not parsed earlier
- {
- bool hasParamCommand = g_hasParamCommand;
- bool hasReturnCommand = g_hasReturnCommand;
- QDict<void> paramsFound = g_paramsFound;
- //printf("..1 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
- // g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
-
- docParserPushContext(FALSE);
- g_scope = def;
- if (def->definitionType()==Definition::TypeMember && def->getOuterScope())
- {
- if (def->getOuterScope()!=Doxygen::globalScope)
- {
- g_context=def->getOuterScope()->name();
- }
- }
- else if (def!=Doxygen::globalScope)
- {
- g_context=def->name();
- }
- g_styleStack.clear();
- g_nodeStack.clear();
- g_paramsFound.clear();
- g_copyStack.append(def);
- // make sure the descriptions end with a newline, so the parser will correctly
- // handle them in all cases.
- //printf("doc='%s'\n",doc.data());
- //printf("brief='%s'\n",brief.data());
- if (m_copyBrief)
- {
- brief+='\n';
- internalValidatingParseDoc(m_parent,children,brief);
-
- //printf("..2 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
- // g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
- hasParamCommand = hasParamCommand || g_hasParamCommand;
- hasReturnCommand = hasReturnCommand || g_hasReturnCommand;
- QDictIterator<void> it(g_paramsFound);
- void *item;
- for (;(item=it.current());++it)
- {
- paramsFound.insert(it.currentKey(),it.current());
- }
- }
- if (m_copyDetails)
- {
- doc+='\n';
- internalValidatingParseDoc(m_parent,children,doc);
-
- //printf("..3 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
- // g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
- hasParamCommand = hasParamCommand || g_hasParamCommand;
- hasReturnCommand = hasReturnCommand || g_hasReturnCommand;
- QDictIterator<void> it(g_paramsFound);
- void *item;
- for (;(item=it.current());++it)
- {
- paramsFound.insert(it.currentKey(),it.current());
- }
- }
- g_copyStack.remove(def);
- ASSERT(g_styleStack.isEmpty());
- ASSERT(g_nodeStack.isEmpty());
- docParserPopContext(TRUE);
-
- g_hasParamCommand = hasParamCommand;
- g_hasReturnCommand = hasReturnCommand;
- g_paramsFound = paramsFound;
-
- //printf("..4 hasParamCommand=%d hasReturnCommand=%d paramsFound=%d\n",
- // g_hasParamCommand,g_hasReturnCommand,g_paramsFound.count());
- }
- else // oops, recursion
- {
- warn_doc_error(g_fileName,doctokenizerYYlineno,"recursive call chain of \\copydoc commands detected at %d\n",
- doctokenizerYYlineno);
- }
- }
- else
- {
- warn_doc_error(g_fileName,doctokenizerYYlineno,"target %s of \\copydoc command not found",
- qPrint(m_link));
- }
-}
-
-//---------------------------------------------------------------------------
-
DocXRefItem::DocXRefItem(DocNode *parent,int id,const char *key) :
m_id(id), m_key(key), m_relPath(g_relPath)
{
diff --git a/src/docparser.h b/src/docparser.h
index ef01089..2cc607e 100644
--- a/src/docparser.h
+++ b/src/docparser.h
@@ -694,24 +694,6 @@ class DocIndexEntry : public DocNode
//-----------------------------------------------------------------------
-/** Node representing a copy of documentation block. */
-class DocCopy : public DocNode
-{
- public:
- DocCopy(DocNode *parent,const QCString &link,bool copyBrief,bool copyDetails)
- : m_link(link),
- m_copyBrief(copyBrief), m_copyDetails(copyDetails) { m_parent = parent; }
- Kind kind() const { return Kind_Copy; }
- QCString link() const { return m_link; }
- void accept(DocVisitor * /*v*/) { /*CompAccept<DocCopy>::accept(this,v);*/ }
- void parse(QList<DocNode> &children);
-
- private:
- QCString m_link;
- bool m_copyBrief;
- bool m_copyDetails;
-};
-
/** Node representing an auto List */
class DocAutoList : public CompAccept<DocAutoList>
{
diff --git a/src/docvisitor.h b/src/docvisitor.h
index d2318c9..0a53595 100644
--- a/src/docvisitor.h
+++ b/src/docvisitor.h
@@ -79,7 +79,6 @@ class DocLinkedWord;
class DocParamSect;
class DocParamList;
class DocInternalRef;
-class DocCopy; // TODO: no longer generated => remove
class DocText;
class DocSimpleSectSep;
class DocHtmlBlockQuote;
@@ -187,8 +186,6 @@ class DocVisitor
virtual void visitPost(DocXRefItem *) = 0;
virtual void visitPre(DocInternalRef *) = 0;
virtual void visitPost(DocInternalRef *) = 0;
- virtual void visitPre(DocCopy *) = 0;
- virtual void visitPost(DocCopy *) = 0;
virtual void visitPre(DocText *) = 0;
virtual void visitPost(DocText *) = 0;
virtual void visitPre(DocHtmlBlockQuote *) = 0;
diff --git a/src/dot.cpp b/src/dot.cpp
index 5aca277..5cdf92c 100644
--- a/src/dot.cpp
+++ b/src/dot.cpp
@@ -1,13 +1,10 @@
/*****************************************************************************
*
- *
- *
- *
- * Copyright (C) 1997-2015 by Dimitri van Heesch.
+ * Copyright (C) 1997-2019 by Dimitri van Heesch.
*
* Permission to use, copy, modify, and distribute this software and its
- * documentation under the terms of the GNU General Public License is hereby
- * granted. No representations are made about the suitability of this software
+ * documentation under the terms of the GNU General Public License is hereby
+ * granted. No representations are made about the suitability of this software
* for any purpose. It is provided "as is" without express or implied warranty.
* See the GNU General Public License for more details.
*
@@ -26,392 +23,47 @@
#include <qwaitcondition.h>
#include <qregexp.h>
+#include "config.h"
#include "dot.h"
-#include "doxygen.h"
-#include "message.h"
+#include "dotrunner.h"
+#include "dotfilepatcher.h"
#include "util.h"
-#include "config.h"
-#include "language.h"
-#include "defargs.h"
-#include "docparser.h"
-#include "debug.h"
-#include "pagedef.h"
#include "portable.h"
-#include "dirdef.h"
-#include "vhdldocgen.h"
+#include "message.h"
#include "ftextstream.h"
-#include "md5.h"
-#include "memberlist.h"
-#include "groupdef.h"
-#include "classlist.h"
-#include "filename.h"
-#include "namespacedef.h"
-#include "memberdef.h"
-#include "membergroup.h"
+#include "doxygen.h"
+#include "language.h"
+#include "index.h"
#define MAP_CMD "cmapx"
-//#define FONTNAME "Helvetica"
-#define FONTNAME getDotFontName()
-#define FONTSIZE getDotFontSize()
-
-//--------------------------------------------------------------------
-
-static const char svgZoomHeader[] =
-"<svg id=\"main\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:space=\"preserve\" onload=\"init(evt)\">\n"
-"<style type=\"text/css\"><![CDATA[\n"
-".edge:hover path { stroke: red; }\n"
-".edge:hover polygon { stroke: red; fill: red; }\n"
-"]]></style>\n"
-"<script type=\"text/javascript\"><![CDATA[\n"
-"var edges = document.getElementsByTagName('g');\n"
-"if (edges && edges.length) {\n"
-" for (var i=0;i<edges.length;i++) {\n"
-" if (edges[i].id.substr(0,4)=='edge') {\n"
-" edges[i].setAttribute('class','edge');\n"
-" }\n"
-" }\n"
-"}\n"
-"]]></script>\n"
-" <defs>\n"
-" <circle id=\"rim\" cx=\"0\" cy=\"0\" r=\"7\"/>\n"
-" <circle id=\"rim2\" cx=\"0\" cy=\"0\" r=\"3.5\"/>\n"
-" <g id=\"zoomPlus\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"zoomplus.mouseover\" end=\"zoomplus.mouseout\"/>\n"
-" </use>\n"
-" <path d=\"M-4,0h8M0,-4v8\" fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" pointer-events=\"none\"/>\n"
-" </g>\n"
-" <g id=\"zoomMin\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"zoomminus.mouseover\" end=\"zoomminus.mouseout\"/>\n"
-" </use>\n"
-" <path d=\"M-4,0h8\" fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" pointer-events=\"none\"/>\n"
-" </g>\n"
-" <g id=\"dirArrow\">\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-" <g id=\"resetDef\">\n"
-" <use xlink:href=\"#rim2\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"reset.mouseover\" end=\"reset.mouseout\"/>\n"
-" </use>\n"
-" </g>\n"
-" </defs>\n"
-"\n"
-"<script type=\"text/javascript\">\n"
-;
-
-static const char svgZoomFooter[] =
-// navigation panel
-" <g id=\"navigator\" transform=\"translate(0 0)\" fill=\"#404254\">\n"
-" <rect fill=\"#f2f5e9\" fill-opacity=\"0.5\" stroke=\"#606060\" stroke-width=\".5\" x=\"0\" y=\"0\" width=\"60\" height=\"60\"/>\n"
-// zoom in
-" <use id=\"zoomplus\" xlink:href=\"#zoomPlus\" x=\"17\" y=\"9\" onmousedown=\"handleZoom(evt,'in')\"/>\n"
-// zoom out
-" <use id=\"zoomminus\" xlink:href=\"#zoomMin\" x=\"42\" y=\"9\" onmousedown=\"handleZoom(evt,'out')\"/>\n"
-// reset zoom
-" <use id=\"reset\" xlink:href=\"#resetDef\" x=\"30\" y=\"36\" onmousedown=\"handleReset()\"/>\n"
-// arrow up
-" <g id=\"arrowUp\" xlink:href=\"#dirArrow\" transform=\"translate(30 24)\" onmousedown=\"handlePan(0,-1)\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowUp.mouseover\" end=\"arrowUp.mouseout\"/>\n"
-" </use>\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-// arrow right
-" <g id=\"arrowRight\" xlink:href=\"#dirArrow\" transform=\"rotate(90) translate(36 -43)\" onmousedown=\"handlePan(1,0)\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowRight.mouseover\" end=\"arrowRight.mouseout\"/>\n"
-" </use>\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-// arrow down
-" <g id=\"arrowDown\" xlink:href=\"#dirArrow\" transform=\"rotate(180) translate(-30 -48)\" onmousedown=\"handlePan(0,1)\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowDown.mouseover\" end=\"arrowDown.mouseout\"/>\n"
-" </use>\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-// arrow left
-" <g id=\"arrowLeft\" xlink:href=\"#dirArrow\" transform=\"rotate(270) translate(-36 17)\" onmousedown=\"handlePan(-1,0)\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowLeft.mouseover\" end=\"arrowLeft.mouseout\"/>\n"
-" </use>\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-" </g>\n"
-// link to original SVG
-" <svg viewBox=\"0 0 15 15\" width=\"100%\" height=\"30px\" preserveAspectRatio=\"xMaxYMin meet\">\n"
-" <g id=\"arrow_out\" transform=\"scale(0.3 0.3)\">\n"
-" <a xlink:href=\"$orgname\" target=\"_base\">\n"
-" <rect id=\"button\" ry=\"5\" rx=\"5\" y=\"6\" x=\"6\" height=\"38\" width=\"38\"\n"
-" fill=\"#f2f5e9\" fill-opacity=\"0.5\" stroke=\"#606060\" stroke-width=\"1.0\"/>\n"
-" <path id=\"arrow\"\n"
-" d=\"M 11.500037,31.436501 C 11.940474,20.09759 22.043105,11.32322 32.158766,21.979434 L 37.068811,17.246167 C 37.068811,17.246167 37.088388,32 37.088388,32 L 22.160133,31.978069 C 22.160133,31.978069 26.997745,27.140456 26.997745,27.140456 C 18.528582,18.264221 13.291696,25.230495 11.500037,31.436501 z\"\n"
-" style=\"fill:#404040;\"/>\n"
-" </a>\n"
-" </g>\n"
-" </svg>\n"
-"</svg>\n"
-;
+static int DOT_NUM_THREADS; // will be initialized in initDot
//--------------------------------------------------------------------
-/*! mapping from protection levels to color names */
-static const char *normalEdgeColorMap[] =
-{
- "midnightblue", // Public
- "darkgreen", // Protected
- "firebrick4", // Private
- "darkorchid3", // "use" relation
- "grey75", // Undocumented
- "orange", // template relation
- "orange" // type constraint
-};
-
-static const char *normalArrowStyleMap[] =
-{
- "empty", // Public
- "empty", // Protected
- "empty", // Private
- "open", // "use" relation
- 0, // Undocumented
- 0 // template relation
-};
-
-static const char *normalEdgeStyleMap[] =
-{
- "solid", // inheritance
- "dashed" // usage
-};
-
-static const char *umlEdgeColorMap[] =
-{
- "midnightblue", // Public
- "darkgreen", // Protected
- "firebrick4", // Private
- "grey25", // "use" relation
- "grey75", // Undocumented
- "orange", // template relation
- "orange" // type constraint
-};
-
-static const char *umlArrowStyleMap[] =
-{
- "onormal", // Public
- "onormal", // Protected
- "onormal", // Private
- "odiamond", // "use" relation
- 0, // Undocumented
- 0 // template relation
-};
-
-static const char *umlEdgeStyleMap[] =
-{
- "solid", // inheritance
- "solid" // usage
-};
-
-/** Helper struct holding the properties of a edge in a dot graph. */
-struct EdgeProperties
-{
- const char * const *edgeColorMap;
- const char * const *arrowStyleMap;
- const char * const *edgeStyleMap;
-};
-
-static EdgeProperties normalEdgeProps =
-{
- normalEdgeColorMap, normalArrowStyleMap, normalEdgeStyleMap
-};
-
-static EdgeProperties umlEdgeProps =
+void initDot()
{
- umlEdgeColorMap, umlArrowStyleMap, umlEdgeStyleMap
-};
-
-
-static QCString convertLabel(const QCString &l);
-
-static QCString getDotFontName()
-{
- static QCString dotFontName = Config_getString(DOT_FONTNAME);
- if (dotFontName.isEmpty())
+ DotGraph::DOT_FONTNAME = Config_getString(DOT_FONTNAME);
+ if (DotGraph::DOT_FONTNAME.isEmpty())
{
- //dotFontName="FreeSans.ttf";
- dotFontName="Helvetica";
+ DotGraph::DOT_FONTNAME="Helvetica";
}
- return dotFontName;
-}
-static int getDotFontSize()
-{
- static int dotFontSize = Config_getInt(DOT_FONTSIZE);
- if (dotFontSize<4) dotFontSize=4;
- return dotFontSize;
-}
+ DotGraph::DOT_FONTSIZE = Config_getInt(DOT_FONTSIZE);
+ if (DotGraph::DOT_FONTSIZE<4) DotGraph::DOT_FONTSIZE=4;
-static void writeGraphHeader(FTextStream &t,const QCString &title=QCString())
-{
- static bool interactiveSVG = Config_getBool(INTERACTIVE_SVG);
- t << "digraph ";
- if (title.isEmpty())
- {
- t << "\"Dot Graph\"";
- }
- else
- {
- t << "\"" << convertLabel(title) << "\"";
- }
- t << endl << "{" << endl;
- if (interactiveSVG) // insert a comment to force regeneration when this
- // option is toggled
- {
- t << " // INTERACTIVE_SVG=YES\n";
- }
- t << " // LATEX_PDF_SIZE\n"; // write placeholder for LaTeX PDF bounding box size repacement
- if (Config_getBool(DOT_TRANSPARENT))
- {
- t << " bgcolor=\"transparent\";" << endl;
- }
- t << " edge [fontname=\"" << FONTNAME << "\","
- "fontsize=\"" << FONTSIZE << "\","
- "labelfontname=\"" << FONTNAME << "\","
- "labelfontsize=\"" << FONTSIZE << "\"];\n";
- t << " node [fontname=\"" << FONTNAME << "\","
- "fontsize=\"" << FONTSIZE << "\",shape=record];\n";
-}
+ DOT_NUM_THREADS = Config_getInt(DOT_NUM_THREADS);
+ if (DOT_NUM_THREADS > 32) DOT_NUM_THREADS = 32;
+ if (DOT_NUM_THREADS <= 0) DOT_NUM_THREADS = QMAX(2,QThread::idealThreadCount()+1);
-static void writeGraphFooter(FTextStream &t)
-{
- t << "}" << endl;
-}
+ // these are copied to be sure to be thread save
+ DotRunner::DOT_CLEANUP = Config_getBool(DOT_CLEANUP);
+ DotRunner::DOT_MULTI_TARGETS = Config_getBool(DOT_MULTI_TARGETS);
+ DotRunner::DOT_EXE.init(Config_getString(DOT_PATH) + "dot");
-static QCString replaceRef(const QCString &buf,const QCString relPath,
- bool urlOnly,const QCString &context,const QCString &target=QCString())
-{
- // search for href="...", store ... part in link
- QCString href = "href";
- //bool isXLink=FALSE;
- int len = 6;
- int indexS = buf.find("href=\""), indexE;
- bool setTarget = FALSE;
- if (indexS>5 && buf.find("xlink:href=\"")!=-1) // XLink href (for SVG)
- {
- indexS-=6;
- len+=6;
- href.prepend("xlink:");
- //isXLink=TRUE;
- }
- if (indexS>=0 && (indexE=buf.find('"',indexS+len))!=-1)
- {
- QCString link = buf.mid(indexS+len,indexE-indexS-len);
- QCString result;
- if (urlOnly) // for user defined dot graphs
- {
- if (link.left(5)=="\\ref " || link.left(5)=="@ref ") // \ref url
- {
- result=href+"=\"";
- // fake ref node to resolve the url
- DocRef *df = new DocRef( (DocNode*) 0, link.mid(5), context );
- result+=externalRef(relPath,df->ref(),TRUE);
- if (!df->file().isEmpty())
- result += df->file().data() + Doxygen::htmlFileExtension;
- if (!df->anchor().isEmpty())
- result += "#" + df->anchor();
- delete df;
- result += "\"";
- }
- else
- {
- result = href+"=\"" + link + "\"";
- }
- }
- else // ref$url (external ref via tag file), or $url (local ref)
- {
- int marker = link.find('$');
- if (marker!=-1)
- {
- QCString ref = link.left(marker);
- QCString url = link.mid(marker+1);
- if (!ref.isEmpty())
- {
- result = externalLinkTarget();
- if (result != "") setTarget = TRUE;
- }
- result+= href+"=\"";
- result+=externalRef(relPath,ref,TRUE);
- result+= url + "\"";
- }
- else // should not happen, but handle properly anyway
- {
- result = href+"=\"" + link + "\"";
- }
- }
- if (!target.isEmpty() && !setTarget)
- {
- result+=" target=\""+target+"\"";
- }
- QCString leftPart = buf.left(indexS);
- QCString rightPart = buf.mid(indexE+1);
- return leftPart + result + rightPart;
- }
- else
- {
- return buf;
- }
+ DotGraph::IMG_EXT = getDotImageExtension();
}
-/*! converts the rectangles in a client site image map into a stream
- * \param t the stream to which the result is written.
- * \param mapName the name of the map file.
- * \param relPath the relative path to the root of the output directory
- * (used in case CREATE_SUBDIRS is enabled).
- * \param urlOnly if FALSE the url field in the map contains an external
- * references followed by a $ and then the URL.
- * \param context the context (file, class, or namespace) in which the
- * map file was found
- * \returns TRUE if successful.
- */
-static bool convertMapFile(FTextStream &t,const char *mapName,
- const QCString relPath, bool urlOnly=FALSE,
- const QCString &context=QCString())
-{
- QFile f(mapName);
- if (!f.open(IO_ReadOnly))
- {
- err("problems opening map file %s for inclusion in the docs!\n"
- "If you installed Graphviz/dot after a previous failing run, \n"
- "try deleting the output directory and rerun doxygen.\n",mapName);
- return FALSE;
- }
- const int maxLineLen=10240;
- while (!f.atEnd()) // foreach line
- {
- QCString buf(maxLineLen);
- int numBytes = f.readLine(buf.rawData(),maxLineLen);
- if (numBytes>0)
- {
- buf.resize(numBytes+1);
-
- if (buf.left(5)=="<area")
- {
- QCString replBuf = replaceRef(buf,relPath,urlOnly,context);
- // strip id="..." from replBuf since the id's are not needed and not unique.
- int indexS = replBuf.find("id=\""), indexE;
- if (indexS>0 && (indexE=replBuf.find('"',indexS+4))!=-1)
- {
- t << replBuf.left(indexS-1) << replBuf.right(replBuf.length() - indexE - 1);
- }
- else
- {
- t << replBuf;
- }
- }
- }
- }
- return TRUE;
-}
static QCString g_dotFontPath;
@@ -450,147 +102,6 @@ static void unsetDotFontPath()
g_dotFontPath="";
}
-static bool resetPDFSize(const int width,const int height, const char *base)
-{
- QString tmpName = QString::fromUtf8(QCString(base)+".tmp");
- QString patchFile = QString::fromUtf8(QCString(base)+".dot");
- if (!QDir::current().rename(patchFile,tmpName))
- {
- err("Failed to rename file %s to %s!\n",patchFile.data(),tmpName.data());
- return FALSE;
- }
- QFile fi(tmpName);
- QFile fo(patchFile);
- if (!fi.open(IO_ReadOnly))
- {
- err("problem opening file %s for patching!\n",tmpName.data());
- QDir::current().rename(tmpName,patchFile);
- return FALSE;
- }
- if (!fo.open(IO_WriteOnly))
- {
- err("problem opening file %s for patching!\n",patchFile.data());
- QDir::current().rename(tmpName,patchFile);
- fi.close();
- return FALSE;
- }
- FTextStream t(&fo);
- const int maxLineLen=100*1024;
- while (!fi.atEnd()) // foreach line
- {
- QCString line(maxLineLen);
- int numBytes = fi.readLine(line.rawData(),maxLineLen);
- if (numBytes<=0)
- {
- break;
- }
- line.resize(numBytes+1);
- if (line.find("LATEX_PDF_SIZE") != -1)
- {
- double scale = (width > height ? width : height)/double(MAX_LATEX_GRAPH_INCH);
- t << " size=\""<<width/scale << "," <<height/scale <<"\";\n";
- }
- else
- t << line;
- }
- fi.close();
- fo.close();
- // remove temporary file
- QDir::current().remove(tmpName);
- return TRUE;
-}
-static bool readBoundingBox(const char *fileName,int *width,int *height,bool isEps)
-{
- QCString bb = isEps ? QCString("%%PageBoundingBox:") : QCString("/MediaBox [");
- QFile f(fileName);
- if (!f.open(IO_ReadOnly|IO_Raw))
- {
- //printf("readBoundingBox: could not open %s\n",fileName);
- return FALSE;
- }
- const int maxLineLen=1024;
- char buf[maxLineLen];
- while (!f.atEnd())
- {
- int numBytes = f.readLine(buf,maxLineLen-1); // read line
- if (numBytes>0)
- {
- buf[numBytes]='\0';
- const char *p = strstr(buf,bb);
- if (p) // found PageBoundingBox or /MediaBox string
- {
- int x,y;
- if (sscanf(p+bb.length(),"%d %d %d %d",&x,&y,width,height)!=4)
- {
- //printf("readBoundingBox sscanf fail\n");
- return FALSE;
- }
- return TRUE;
- }
- }
- else // read error!
- {
- //printf("Read error %d!\n",numBytes);
- return FALSE;
- }
- }
- err("Failed to extract bounding box from generated diagram file %s\n",fileName);
- return FALSE;
-}
-
-static bool writeVecGfxFigure(FTextStream &out,const QCString &baseName,
- const QCString &figureName)
-{
- int width=400,height=550;
- static bool usePdfLatex = Config_getBool(USE_PDFLATEX);
- if (usePdfLatex)
- {
- if (!readBoundingBox(figureName+".pdf",&width,&height,FALSE))
- {
- //printf("writeVecGfxFigure()=0\n");
- return FALSE;
- }
- }
- else
- {
- if (!readBoundingBox(figureName+".eps",&width,&height,TRUE))
- {
- //printf("writeVecGfxFigure()=0\n");
- return FALSE;
- }
- }
- //printf("Got PDF/EPS size %d,%d\n",width,height);
- int maxWidth = 350; /* approx. page width in points, excl. margins */
- int maxHeight = 550; /* approx. page height in points, excl. margins */
- out << "\\nopagebreak\n"
- "\\begin{figure}[H]\n"
- "\\begin{center}\n"
- "\\leavevmode\n";
- if (width>maxWidth || height>maxHeight) // figure too big for page
- {
- // c*width/maxWidth > c*height/maxHeight, where c=maxWidth*maxHeight>0
- if (width*maxHeight>height*maxWidth)
- {
- out << "\\includegraphics[width=" << maxWidth << "pt]";
- }
- else
- {
- out << "\\includegraphics[height=" << maxHeight << "pt]";
- }
- }
- else
- {
- out << "\\includegraphics[width=" << width << "pt]";
- }
-
- out << "{" << baseName << "}\n"
- "\\end{center}\n"
- "\\end{figure}\n";
-
- //printf("writeVecGfxFigure()=1\n");
- return TRUE;
-}
-
// extract size from a dot generated SVG file
static bool readSVGSize(const QCString &fileName,int *width,int *height)
{
@@ -638,8 +149,8 @@ static void writeSVGNotSupported(FTextStream &out)
// check if a reference to a SVG figure can be written and does so if possible.
// return FALSE if not possible (for instance because the SVG file is not yet generated).
-static bool writeSVGFigureLink(FTextStream &out,const QCString &relPath,
- const QCString &baseName,const QCString &absImgName)
+bool writeSVGFigureLink(FTextStream &out,const QCString &relPath,
+ const QCString &baseName,const QCString &absImgName)
{
int width=600,height=600;
if (!readSVGSize(absImgName,&width,&height))
@@ -680,593 +191,6 @@ static bool writeSVGFigureLink(FTextStream &out,const QCString &relPath,
return TRUE;
}
-// since dot silently reproduces the input file when it does not
-// support the PNG format, we need to check the result.
-static void checkDotResult(const char *imgExt, const char *imgName)
-{
- if (qstrcmp(imgExt,"png")==0)
- {
- FILE *f = portable_fopen(imgName,"rb");
- if (f)
- {
- char data[4];
- if (fread(data,1,4,f)==4)
- {
- if (!(data[1]=='P' && data[2]=='N' && data[3]=='G'))
- {
- err("Image `%s' produced by dot is not a valid PNG!\n"
- "You should either select a different format "
- "(DOT_IMAGE_FORMAT in the config file) or install a more "
- "recent version of graphviz (1.7+)\n",imgName
- );
- }
- }
- else
- {
- err("Could not read image `%s' generated by dot!\n",imgName);
- }
- fclose(f);
- }
- else
- {
- err("Could not open image `%s' generated by dot!\n",imgName);
- }
- }
-}
-
-static bool insertMapFile(FTextStream &out,const QCString &mapFile,
- const QCString &relPath,const QCString &mapLabel)
-{
- QFileInfo fi(mapFile);
- if (fi.exists() && fi.size()>0) // reuse existing map file
- {
- QGString tmpstr;
- FTextStream tmpout(&tmpstr);
- convertMapFile(tmpout,mapFile,relPath,FALSE);
- if (!tmpstr.isEmpty())
- {
- out << "<map name=\"" << mapLabel << "\" id=\"" << mapLabel << "\">" << endl;
- out << tmpstr;
- out << "</map>" << endl;
- }
- return TRUE;
- }
- return FALSE; // no map file yet, need to generate it
-}
-
-static void removeDotGraph(const QCString &dotName)
-{
- static bool dotCleanUp = Config_getBool(DOT_CLEANUP);
- if (dotCleanUp)
- {
- QDir d;
- d.remove(dotName);
- }
-}
-
-
-
-/*! Checks if a file "baseName".md5 exists. If so the contents
- * are compared with \a md5. If equal FALSE is returned. If the .md5
- * file does not exist or its contents are not equal to \a md5,
- * a new .md5 is generated with the \a md5 string as contents.
- */
-static bool checkAndUpdateMd5Signature(const QCString &baseName,
- const QCString &md5)
-{
- QFile f(baseName+".md5");
- if (f.open(IO_ReadOnly))
- {
- // read checksum
- QCString md5stored(33);
- int bytesRead=f.readBlock(md5stored.rawData(),32);
- md5stored[32]='\0';
- // compare checksum
- if (bytesRead==32 && md5==md5stored)
- {
- // bail out if equal
- return FALSE;
- }
- }
- f.close();
- // create checksum file
- if (f.open(IO_WriteOnly))
- {
- f.writeBlock(md5.data(),32);
- f.close();
- }
- return TRUE;
-}
-
-static bool checkDeliverables(const QCString &file1,
- const QCString &file2=QCString())
-{
- bool file1Ok = TRUE;
- bool file2Ok = TRUE;
- if (!file1.isEmpty())
- {
- QFileInfo fi(file1);
- file1Ok = (fi.exists() && fi.size()>0);
- }
- if (!file2.isEmpty())
- {
- QFileInfo fi(file2);
- file2Ok = (fi.exists() && fi.size()>0);
- }
- return file1Ok && file2Ok;
-}
-
-//--------------------------------------------------------------------
-
-inline int DotNode::findParent( DotNode *n )
-{
- 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);
-}
-
-//--------------------------------------------------------------------
-
-DotRunner::DotRunner(const QCString &file,const QCString &path,
- bool checkResult,const QCString &imageName)
- : m_dotExe(Config_getString(DOT_PATH)+"dot"),
- m_file(file), m_path(path),
- m_checkResult(checkResult), m_imageName(imageName),
- m_imgExt(getDotImageExtension())
-{
- static bool dotCleanUp = Config_getBool(DOT_CLEANUP);
- static bool dotMultiTargets = Config_getBool(DOT_MULTI_TARGETS);
- m_cleanUp = dotCleanUp;
- m_multiTargets = dotMultiTargets;
- m_jobs.setAutoDelete(TRUE);
-}
-
-void DotRunner::addJob(const char *format,const char *output, const char *base)
-{
- QCString args = QCString("-T")+format+" -o \""+output+"\"";
- m_jobs.append(new DotConstString(args, base));
-}
-
-void DotRunner::addPostProcessing(const char *cmd,const char *args)
-{
- m_postCmd.set(cmd);
- m_postArgs.set(args);
-}
-
-bool DotRunner::run()
-{
- int exitCode=0;
- int width=0,height=0;
-
- QCString dotArgs;
- QListIterator<DotConstString> li(m_jobs);
- DotConstString *s;
- if (m_multiTargets)
- {
- dotArgs=QCString("\"")+m_file.data()+"\"";
- for (li.toFirst();(s=li.current());++li)
- {
- dotArgs+=' ';
- dotArgs+=s->data();
- }
- if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0) goto error;
- dotArgs=QCString("\"")+m_file.data()+"\"";
- bool redo = FALSE;
- for (li.toFirst();(s=li.current());++li)
- {
- if (s->pdfData())
- {
- if (!readBoundingBox(QCString(s->pdfData())+".pdf",&width,&height,FALSE)) goto error;
- if ((width > MAX_LATEX_GRAPH_SIZE) || (height > MAX_LATEX_GRAPH_SIZE))
- {
- if (!resetPDFSize(width,height,s->pdfData())) goto error;
- dotArgs+=' ';
- dotArgs+=s->data();
- redo = TRUE;
- }
- }
- }
- if (redo)
- {
- if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0) goto error;
- }
- }
- else
- {
- for (li.toFirst();(s=li.current());++li)
- {
- dotArgs=QCString("\"")+m_file.data()+"\" "+s->data();
- if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0) goto error;
- if (s->pdfData())
- {
- if (!readBoundingBox(QCString(s->pdfData())+".pdf",&width,&height,FALSE)) goto error;
- if ((width > MAX_LATEX_GRAPH_SIZE) || (height > MAX_LATEX_GRAPH_SIZE))
- {
- if (!resetPDFSize(width,height,s->pdfData())) goto error;
- if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0) goto error;
- }
- }
- }
- }
- if (!m_postCmd.isEmpty() && portable_system(m_postCmd.data(),m_postArgs.data())!=0)
- {
- err("Problems running '%s' as a post-processing step for dot output\n",m_postCmd.data());
- return FALSE;
- }
- if (m_checkResult)
- {
- checkDotResult(m_imgExt.data(),m_imageName.data());
- }
- if (m_cleanUp)
- {
- //printf("removing dot file %s\n",m_file.data());
- //QDir(path).remove(file);
- m_cleanupItem.file.set(m_file.data());
- m_cleanupItem.path.set(m_path.data());
- }
- return TRUE;
-error:
- err("Problems running dot: exit code=%d, command='%s', arguments='%s'\n",
- exitCode,m_dotExe.data(),dotArgs.data());
- return FALSE;
-}
-
-//--------------------------------------------------------------------
-
-DotFilePatcher::DotFilePatcher(const char *patchFile)
- : m_patchFile(patchFile)
-{
- m_maps.setAutoDelete(TRUE);
-}
-
-QCString DotFilePatcher::file() const
-{
- return m_patchFile;
-}
-
-int DotFilePatcher::addMap(const QCString &mapFile,const QCString &relPath,
- bool urlOnly,const QCString &context,const QCString &label)
-{
- int id = m_maps.count();
- Map *map = new Map;
- map->mapFile = mapFile;
- map->relPath = relPath;
- map->urlOnly = urlOnly;
- map->context = context;
- map->label = label;
- map->zoomable = FALSE;
- map->graphId = -1;
- m_maps.append(map);
- return id;
-}
-
-int DotFilePatcher::addFigure(const QCString &baseName,
- const QCString &figureName,bool heightCheck)
-{
- int id = m_maps.count();
- Map *map = new Map;
- map->mapFile = figureName;
- map->urlOnly = heightCheck;
- map->label = baseName;
- map->zoomable = FALSE;
- map->graphId = -1;
- m_maps.append(map);
- return id;
-}
-
-int DotFilePatcher::addSVGConversion(const QCString &relPath,bool urlOnly,
- const QCString &context,bool zoomable,
- int graphId)
-{
- int id = m_maps.count();
- Map *map = new Map;
- map->relPath = relPath;
- map->urlOnly = urlOnly;
- map->context = context;
- map->zoomable = zoomable;
- map->graphId = graphId;
- m_maps.append(map);
- return id;
-}
-
-int DotFilePatcher::addSVGObject(const QCString &baseName,
- const QCString &absImgName,
- const QCString &relPath)
-{
- int id = m_maps.count();
- Map *map = new Map;
- map->mapFile = absImgName;
- map->relPath = relPath;
- map->label = baseName;
- map->zoomable = FALSE;
- map->graphId = -1;
- m_maps.append(map);
- return id;
-}
-
-bool DotFilePatcher::run()
-{
- //printf("DotFilePatcher::run(): %s\n",m_patchFile.data());
- static bool interactiveSVG = Config_getBool(INTERACTIVE_SVG);
- bool isSVGFile = m_patchFile.right(4)==".svg";
- int graphId = -1;
- QCString relPath;
- if (isSVGFile)
- {
- Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
- interactiveSVG = interactiveSVG && map->zoomable;
- graphId = map->graphId;
- relPath = map->relPath;
- //printf("DotFilePatcher::addSVGConversion: file=%s zoomable=%d\n",
- // m_patchFile.data(),map->zoomable);
- }
- QString tmpName = QString::fromUtf8(m_patchFile+".tmp");
- QString patchFile = QString::fromUtf8(m_patchFile);
- if (!QDir::current().rename(patchFile,tmpName))
- {
- err("Failed to rename file %s to %s!\n",m_patchFile.data(),tmpName.data());
- return FALSE;
- }
- QFile fi(tmpName);
- QFile fo(patchFile);
- if (!fi.open(IO_ReadOnly))
- {
- err("problem opening file %s for patching!\n",tmpName.data());
- QDir::current().rename(tmpName,patchFile);
- return FALSE;
- }
- if (!fo.open(IO_WriteOnly))
- {
- err("problem opening file %s for patching!\n",m_patchFile.data());
- QDir::current().rename(tmpName,patchFile);
- return FALSE;
- }
- FTextStream t(&fo);
- const int maxLineLen=100*1024;
- int lineNr=1;
- int width,height;
- bool insideHeader=FALSE;
- bool replacedHeader=FALSE;
- bool foundSize=FALSE;
- while (!fi.atEnd()) // foreach line
- {
- QCString line(maxLineLen);
- int numBytes = fi.readLine(line.rawData(),maxLineLen);
- if (numBytes<=0)
- {
- break;
- }
- line.resize(numBytes+1);
-
- //printf("line=[%s]\n",line.stripWhiteSpace().data());
- int i;
- ASSERT(numBytes<maxLineLen);
- if (isSVGFile)
- {
- if (interactiveSVG)
- {
- if (line.find("<svg")!=-1 && !replacedHeader)
- {
- int count;
- count = sscanf(line.data(),"<svg width=\"%dpt\" height=\"%dpt\"",&width,&height);
- //printf("width=%d height=%d\n",width,height);
- foundSize = count==2 && (width>500 || height>450);
- if (foundSize) insideHeader=TRUE;
- }
- else if (insideHeader && !replacedHeader && line.find("<title>")!=-1)
- {
- if (foundSize)
- {
- // insert special replacement header for interactive SVGs
- t << "<!--zoomable " << height << " -->\n";
- t << svgZoomHeader;
- t << "var viewWidth = " << width << ";\n";
- t << "var viewHeight = " << height << ";\n";
- if (graphId>=0)
- {
- t << "var sectionId = 'dynsection-" << graphId << "';\n";
- }
- t << "</script>\n";
- t << "<script xlink:href=\"" << relPath << "svgpan.js\"/>\n";
- t << "<svg id=\"graph\" class=\"graph\">\n";
- t << "<g id=\"viewport\">\n";
- }
- insideHeader=FALSE;
- replacedHeader=TRUE;
- }
- }
- if (!insideHeader || !foundSize) // copy SVG and replace refs,
- // unless we are inside the header of the SVG.
- // Then we replace it with another header.
- {
- Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
- t << replaceRef(line,map->relPath,map->urlOnly,map->context,"_top");
- }
- }
- else if ((i=line.find("<!-- SVG"))!=-1 || (i=line.find("[!-- SVG"))!=-1)
- {
- //printf("Found marker at %d\n",i);
- int mapId=-1;
- t << line.left(i);
- int n = sscanf(line.data()+i+1,"!-- SVG %d",&mapId);
- if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
- {
- int e = QMAX(line.find("--]"),line.find("-->"));
- Map *map = m_maps.at(mapId);
- //printf("DotFilePatcher::writeSVGFigure: file=%s zoomable=%d\n",
- // m_patchFile.data(),map->zoomable);
- if (!writeSVGFigureLink(t,map->relPath,map->label,map->mapFile))
- {
- err("Problem extracting size from SVG file %s\n",map->mapFile.data());
- }
- if (e!=-1) t << line.mid(e+3);
- }
- else // error invalid map id!
- {
- err("Found invalid SVG id in file %s!\n",m_patchFile.data());
- t << line.mid(i);
- }
- }
- else if ((i=line.find("<!-- MAP"))!=-1)
- {
- int mapId=-1;
- t << line.left(i);
- int n = sscanf(line.data()+i,"<!-- MAP %d",&mapId);
- if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
- {
- QGString result;
- FTextStream tt(&result);
- Map *map = m_maps.at(mapId);
- //printf("patching MAP %d in file %s with contents of %s\n",
- // mapId,m_patchFile.data(),map->mapFile.data());
- convertMapFile(tt,map->mapFile,map->relPath,map->urlOnly,map->context);
- if (!result.isEmpty())
- {
- t << "<map name=\"" << map->label << "\" id=\"" << map->label << "\">" << endl;
- t << result;
- t << "</map>" << endl;
- }
- }
- else // error invalid map id!
- {
- err("Found invalid MAP id in file %s!\n",m_patchFile.data());
- t << line.mid(i);
- }
- }
- else if ((i=line.find("% FIG"))!=-1)
- {
- int mapId=-1;
- int n = sscanf(line.data()+i+2,"FIG %d",&mapId);
- //printf("line='%s' n=%d\n",line.data()+i,n);
- if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
- {
- Map *map = m_maps.at(mapId);
- //printf("patching FIG %d in file %s with contents of %s\n",
- // mapId,m_patchFile.data(),map->mapFile.data());
- if (!writeVecGfxFigure(t,map->label,map->mapFile))
- {
- err("problem writing FIG %d figure!\n",mapId);
- return FALSE;
- }
- }
- else // error invalid map id!
- {
- err("Found invalid bounding FIG %d in file %s!\n",mapId,m_patchFile.data());
- t << line;
- }
- }
- else
- {
- t << line;
- }
- lineNr++;
- }
- fi.close();
- if (isSVGFile && interactiveSVG && replacedHeader)
- {
- QCString orgName=m_patchFile.left(m_patchFile.length()-4)+"_org.svg";
- t << substitute(svgZoomFooter,"$orgname",stripPath(orgName));
- fo.close();
- // keep original SVG file so we can refer to it, we do need to replace
- // dummy link by real ones
- QFile fi(tmpName);
- QFile fo(orgName);
- if (!fi.open(IO_ReadOnly))
- {
- err("problem opening file %s for reading!\n",tmpName.data());
- return FALSE;
- }
- if (!fo.open(IO_WriteOnly))
- {
- err("problem opening file %s for writing!\n",orgName.data());
- return FALSE;
- }
- FTextStream t(&fo);
- while (!fi.atEnd()) // foreach line
- {
- QCString line(maxLineLen);
- int numBytes = fi.readLine(line.rawData(),maxLineLen);
- if (numBytes<=0)
- {
- break;
- }
- line.resize(numBytes+1);
- Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
- t << replaceRef(line,map->relPath,map->urlOnly,map->context,"_top");
- }
- fi.close();
- fo.close();
- }
- // remove temporary file
- QDir::current().remove(tmpName);
- return TRUE;
-}
-
-//--------------------------------------------------------------------
-
-void DotRunnerQueue::enqueue(DotRunner *runner)
-{
- QMutexLocker locker(&m_mutex);
- m_queue.enqueue(runner);
- m_bufferNotEmpty.wakeAll();
-}
-
-DotRunner *DotRunnerQueue::dequeue()
-{
- QMutexLocker locker(&m_mutex);
- while (m_queue.isEmpty())
- {
- // wait until something is added to the queue
- m_bufferNotEmpty.wait(&m_mutex);
- }
- DotRunner *result = m_queue.dequeue();
- return result;
-}
-
-uint DotRunnerQueue::count() const
-{
- QMutexLocker locker(&m_mutex);
- return m_queue.count();
-}
-
-//--------------------------------------------------------------------
-
-DotWorkerThread::DotWorkerThread(DotRunnerQueue *queue)
- : m_queue(queue)
-{
- m_cleanupItems.setAutoDelete(TRUE);
-}
-
-void DotWorkerThread::run()
-{
- DotRunner *runner;
- while ((runner=m_queue->dequeue()))
- {
- runner->run();
- const DotRunner::CleanupItem &cleanup = runner->cleanup();
- if (!cleanup.file.isEmpty())
- {
- m_cleanupItems.append(new DotRunner::CleanupItem(cleanup));
- }
- }
-}
-
-void DotWorkerThread::cleanup()
-{
- QListIterator<DotRunner::CleanupItem> it(m_cleanupItems);
- DotRunner::CleanupItem *ci;
- for (;(ci=it.current());++it)
- {
- QDir(ci->path.data()).remove(ci->file.data());
- }
-}
-
//--------------------------------------------------------------------
DotManager *DotManager::m_theInstance = 0;
@@ -1282,15 +206,13 @@ DotManager *DotManager::instance()
DotManager::DotManager() : m_dotMaps(1009)
{
- m_dotRuns.setAutoDelete(TRUE);
+ m_runners.setAutoDelete(TRUE);
m_dotMaps.setAutoDelete(TRUE);
m_queue = new DotRunnerQueue;
int i;
- int numThreads = QMIN(32,Config_getInt(DOT_NUM_THREADS));
- if (numThreads!=1)
+ if (DOT_NUM_THREADS!=1)
{
- if (numThreads==0) numThreads = QMAX(2,QThread::idealThreadCount()+1);
- for (i=0;i<numThreads;i++)
+ for (i=0;i<DOT_NUM_THREADS;i++)
{
DotWorkerThread *thread = new DotWorkerThread(m_queue);
thread->start();
@@ -1312,11 +234,26 @@ DotManager::~DotManager()
delete m_queue;
}
-void DotManager::addRun(DotRunner *run)
+DotRunner* DotManager::createRunner(const QCString& absDotName, const QCString& md5Hash)
{
- m_dotRuns.append(run);
+ DotRunner * run = m_runners.find(absDotName);
+ if (run == 0)
+ {
+ run = new DotRunner(absDotName, md5Hash);
+ m_runners.insert(absDotName, run);
+ }
+ else
+ {
+ // we have a match
+ if (md5Hash != QCString(run->getMd5Hash().data()))
+ {
+ err("md5 hash does not match for two different runs of %s !\n", absDotName.data());
+ }
+ }
+ return run;
}
+
int DotManager::addMap(const QCString &file,const QCString &mapFile,
const QCString &relPath,bool urlOnly,const QCString &context,
const QCString &label)
@@ -1369,7 +306,7 @@ int DotManager::addSVGObject(const QCString &file,const QCString &baseName,
bool DotManager::run()
{
- uint numDotRuns = m_dotRuns.count();
+ uint numDotRuns = m_runners.count();
uint numDotMaps = m_dotMaps.count();
if (numDotRuns+numDotMaps>1)
{
@@ -1383,7 +320,7 @@ bool DotManager::run()
}
}
int i=1;
- QListIterator<DotRunner> li(m_dotRuns);
+ QDictIterator<DotRunner> li(m_runners);
bool setPath=FALSE;
if (Config_getBool(GENERATE_HTML))
@@ -1451,11 +388,6 @@ bool DotManager::run()
{
m_workers.at(i)->wait();
}
- // clean up dot files from main thread
- for (i=0;i<(int)m_workers.count();i++)
- {
- m_workers.at(i)->cleanup();
- }
}
portable_sysTimerStop();
if (setPath)
@@ -1494,2736 +426,59 @@ bool DotManager::run()
//--------------------------------------------------------------------
-
-/*! helper function that deletes all nodes in a connected graph, given
- * one of the graph's nodes
- */
-static void deleteNodes(DotNode *node,SDict<DotNode> *skipNodes=0)
-{
- //printf("deleteNodes skipNodes=%p\n",skipNodes);
- static DotNodeList deletedNodes;
- deletedNodes.setAutoDelete(TRUE);
- node->deleteNode(deletedNodes,skipNodes); // collect nodes to be deleted.
- deletedNodes.clear(); // actually remove the nodes.
-}
-
-DotNode::DotNode(int n,const char *lab,const char *tip, const char *url,
- bool isRoot,const ClassDef *cd)
- : m_subgraphId(-1)
- , m_number(n)
- , m_label(lab)
- , m_tooltip(tip)
- , m_url(url)
- , m_parents(0)
- , m_children(0)
- , m_edgeInfo(0)
- , m_deleted(FALSE)
- , m_written(FALSE)
- , m_hasDoc(FALSE)
- , m_isRoot(isRoot)
- , m_classDef(cd)
- , m_visible(FALSE)
- , m_truncated(Unknown)
- , m_distance(1000)
- , m_renumbered(false)
-{
-}
-
-DotNode::~DotNode()
-{
- delete m_children;
- delete m_parents;
- delete m_edgeInfo;
-}
-
-void DotNode::addChild(DotNode *n,
- int edgeColor,
- int edgeStyle,
- const char *edgeLab,
- const char *edgeURL,
- int edgeLabCol
- )
-{
- if (m_children==0)
- {
- m_children = new QList<DotNode>;
- m_edgeInfo = new QList<EdgeInfo>;
- m_edgeInfo->setAutoDelete(TRUE);
- }
- m_children->append(n);
- EdgeInfo *ei = new EdgeInfo;
- ei->m_color = edgeColor;
- ei->m_style = edgeStyle;
- ei->m_label = edgeLab;
- ei->m_url = edgeURL;
- if (edgeLabCol==-1)
- ei->m_labColor=edgeColor;
- else
- ei->m_labColor=edgeLabCol;
- m_edgeInfo->append(ei);
-}
-
-void DotNode::addParent(DotNode *n)
-{
- if (m_parents==0)
- {
- m_parents = new QList<DotNode>;
- }
- m_parents->append(n);
-}
-
-void DotNode::removeChild(DotNode *n)
-{
- if (m_children) m_children->remove(n);
-}
-
-void DotNode::removeParent(DotNode *n)
-{
- if (m_parents) m_parents->remove(n);
-}
-
-void DotNode::deleteNode(DotNodeList &deletedList,SDict<DotNode> *skipNodes)
-{
- if (m_deleted) return; // avoid recursive loops in case the graph has cycles
- m_deleted=TRUE;
- if (m_parents!=0) // delete all parent nodes of this node
- {
- QListIterator<DotNode> dnlip(*m_parents);
- DotNode *pn;
- for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
- {
- //pn->removeChild(this);
- pn->deleteNode(deletedList,skipNodes);
- }
- }
- if (m_children!=0) // delete all child nodes of this node
- {
- QListIterator<DotNode> dnlic(*m_children);
- DotNode *cn;
- for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
- {
- //cn->removeParent(this);
- cn->deleteNode(deletedList,skipNodes);
- }
- }
- // add this node to the list of deleted nodes.
- //printf("skipNodes=%p find(%p)=%p\n",skipNodes,this,skipNodes ? skipNodes->find((int)this) : 0);
- if (skipNodes==0 || skipNodes->find((char*)this)==0)
- {
- //printf("deleting\n");
- deletedList.append(this);
- }
-}
-
-void DotNode::setDistance(int distance)
-{
- if (distance<m_distance) m_distance = distance;
-}
-
-static QCString convertLabel(const QCString &l)
-{
- QString bBefore("\\_/<({[: =-+@%#~?$"); // break before character set
- QString bAfter(">]),:;|"); // break after character set
- QString p(l);
- if (p.isEmpty()) return QCString();
- QString result;
- QChar c,pc=0;
- uint idx = 0;
- int len=p.length();
- int charsLeft=len;
- int sinceLast=0;
- int foldLen=17; // ideal text length
- while (idx < p.length())
- {
- c = p[idx++];
- QString replacement;
- switch(c)
- {
- case '\\': replacement="\\\\"; break;
- case '\n': replacement="\\n"; break;
- case '<': replacement="\\<"; break;
- case '>': replacement="\\>"; break;
- case '|': replacement="\\|"; break;
- case '{': replacement="\\{"; break;
- case '}': replacement="\\}"; break;
- case '"': replacement="\\\""; break;
- default: replacement=c; break;
- }
- // Some heuristics to insert newlines to prevent too long
- // boxes and at the same time prevent ugly breaks
- if (c=='\n')
- {
- result+=replacement;
- foldLen = (3*foldLen+sinceLast+2)/4;
- sinceLast=1;
- }
- else if ((pc!=':' || c!=':') && charsLeft>foldLen/3 && sinceLast>foldLen && bBefore.contains(c))
- {
- result+="\\l";
- result+=replacement;
- foldLen = (foldLen+sinceLast+1)/2;
- sinceLast=1;
- }
- else if (charsLeft>1+foldLen/4 && sinceLast>foldLen+foldLen/3 &&
- !isupper(c) && p[idx].category()==QChar::Letter_Uppercase)
- {
- result+=replacement;
- result+="\\l";
- foldLen = (foldLen+sinceLast+1)/2;
- sinceLast=0;
- }
- else if (charsLeft>foldLen/3 && sinceLast>foldLen && bAfter.contains(c) && (c!=':' || p[idx]!=':'))
- {
- result+=replacement;
- result+="\\l";
- foldLen = (foldLen+sinceLast+1)/2;
- sinceLast=0;
- }
- else
- {
- result+=replacement;
- sinceLast++;
- }
- charsLeft--;
- pc=c;
- }
- return result.utf8();
-}
-
-static QCString escapeTooltip(const QCString &tooltip)
-{
- QCString result;
- const char *p=tooltip.data();
- if (p==0) return result;
- char c;
- while ((c=*p++))
- {
- switch(c)
- {
- case '"': result+="\\\""; break;
- case '\\': result+="\\\\"; break;
- default: result+=c; break;
- }
- }
- return result;
-}
-
-static void writeBoxMemberList(FTextStream &t,
- char prot,MemberList *ml,const ClassDef *scope,
- bool isStatic=FALSE,const QDict<void> *skipNames=0)
-{
- (void)isStatic;
- if (ml)
- {
- MemberListIterator mlia(*ml);
- MemberDef *mma;
- int totalCount=0;
- for (mlia.toFirst();(mma = mlia.current());++mlia)
- {
- if (mma->getClassDef()==scope &&
- (skipNames==0 || skipNames->find(mma->name())==0))
- {
- totalCount++;
- }
- }
-
- int count=0;
- for (mlia.toFirst();(mma = mlia.current());++mlia)
- {
- if (mma->getClassDef() == scope &&
- (skipNames==0 || skipNames->find(mma->name())==0))
- {
- static int limit = Config_getInt(UML_LIMIT_NUM_FIELDS);
- if (limit>0 && (totalCount>limit*3/2 && count>=limit))
- {
- t << theTranslator->trAndMore(QCString().sprintf("%d",totalCount-count)) << "\\l";
- break;
- }
- else
- {
- t << prot << " ";
- t << convertLabel(mma->name());
- if (!mma->isObjCMethod() &&
- (mma->isFunction() || mma->isSlot() || mma->isSignal())) t << "()";
- t << "\\l";
- count++;
- }
- }
- }
- // write member groups within the memberlist
- MemberGroupList *mgl = ml->getMemberGroupList();
- if (mgl)
- {
- MemberGroupListIterator mgli(*mgl);
- MemberGroup *mg;
- for (mgli.toFirst();(mg=mgli.current());++mgli)
- {
- if (mg->members())
- {
- writeBoxMemberList(t,prot,mg->members(),scope,isStatic,skipNames);
- }
- }
- }
- }
-}
-
-static QCString stripProtectionPrefix(const QCString &s)
-{
- if (!s.isEmpty() && (s[0]=='-' || s[0]=='+' || s[0]=='~' || s[0]=='#'))
- {
- return s.mid(1);
- }
- else
- {
- return s;
- }
-}
-
-void DotNode::writeBox(FTextStream &t,
- GraphType gt,
- GraphOutputFormat /*format*/,
- bool hasNonReachableChildren
- )
-{
- const char *labCol =
- m_url.isEmpty() ? "grey75" : // non link
- (
- (hasNonReachableChildren) ? "red" : "black"
- );
- t << " Node" << m_number << " [label=\"";
- static bool umlLook = Config_getBool(UML_LOOK);
-
- if (m_classDef && umlLook && (gt==Inheritance || gt==Collaboration))
- {
- // add names shown as relations to a dictionary, so we don't show
- // them as attributes as well
- QDict<void> arrowNames(17);
- if (m_edgeInfo)
- {
- // for each edge
- QListIterator<EdgeInfo> li(*m_edgeInfo);
- EdgeInfo *ei;
- for (li.toFirst();(ei=li.current());++li)
- {
- if (!ei->m_label.isEmpty()) // labels joined by \n
- {
- int li=ei->m_label.find('\n');
- int p=0;
- QCString lab;
- while ((li=ei->m_label.find('\n',p))!=-1)
- {
- lab = stripProtectionPrefix(ei->m_label.mid(p,li-p));
- arrowNames.insert(lab,(void*)0x8);
- p=li+1;
- }
- lab = stripProtectionPrefix(ei->m_label.right(ei->m_label.length()-p));
- arrowNames.insert(lab,(void*)0x8);
- }
- }
- }
-
- //printf("DotNode::writeBox for %s\n",m_classDef->name().data());
- static bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
- t << "{" << convertLabel(m_label);
- t << "\\n|";
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubAttribs),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubStaticAttribs),m_classDef,TRUE,&arrowNames);
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_properties),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacAttribs),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacStaticAttribs),m_classDef,TRUE,&arrowNames);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proAttribs),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proStaticAttribs),m_classDef,TRUE,&arrowNames);
- if (extractPrivate)
- {
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priAttribs),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priStaticAttribs),m_classDef,TRUE,&arrowNames);
- }
- t << "|";
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubMethods),m_classDef);
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubStaticMethods),m_classDef,TRUE);
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubSlots),m_classDef);
- writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacMethods),m_classDef);
- writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacStaticMethods),m_classDef,TRUE);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proMethods),m_classDef);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proStaticMethods),m_classDef,TRUE);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proSlots),m_classDef);
- if (extractPrivate)
- {
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priMethods),m_classDef);
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priStaticMethods),m_classDef,TRUE);
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priSlots),m_classDef);
- }
- if (m_classDef->getLanguage()!=SrcLangExt_Fortran &&
- m_classDef->getMemberGroupSDict())
- {
- MemberGroupSDict::Iterator mgdi(*m_classDef->getMemberGroupSDict());
- MemberGroup *mg;
- for (mgdi.toFirst();(mg=mgdi.current());++mgdi)
- {
- if (mg->members())
- {
- writeBoxMemberList(t,'*',mg->members(),m_classDef,FALSE,&arrowNames);
- }
- }
- }
- t << "}";
- }
- else // standard look
- {
- t << convertLabel(m_label);
- }
- t << "\",height=0.2,width=0.4";
- if (m_isRoot)
- {
- t << ",color=\"black\", fillcolor=\"grey75\", style=\"filled\", fontcolor=\"black\"";
- }
- else
- {
- static bool dotTransparent = Config_getBool(DOT_TRANSPARENT);
- if (!dotTransparent)
- {
- t << ",color=\"" << labCol << "\", fillcolor=\"";
- t << "white";
- t << "\", style=\"filled\"";
- }
- else
- {
- t << ",color=\"" << labCol << "\"";
- }
- if (!m_url.isEmpty())
- {
- int anchorPos = m_url.findRev('#');
- if (anchorPos==-1)
- {
- t << ",URL=\"" << m_url << Doxygen::htmlFileExtension << "\"";
- }
- else
- {
- t << ",URL=\"" << m_url.left(anchorPos) << Doxygen::htmlFileExtension
- << m_url.right(m_url.length()-anchorPos) << "\"";
- }
- }
- }
- if (!m_tooltip.isEmpty())
- {
- t << ",tooltip=\"" << escapeTooltip(m_tooltip) << "\"";
- }
- else
- {
- t << ",tooltip=\" \""; // space in tooltip is required otherwise still something like 'Node0' is used
- }
- t << "];" << endl;
-}
-
-void DotNode::writeArrow(FTextStream &t,
- GraphType gt,
- GraphOutputFormat format,
- DotNode *cn,
- EdgeInfo *ei,
- bool topDown,
- bool pointBack
- )
-{
- t << " Node";
- if (topDown)
- t << cn->number();
- else
- t << m_number;
- t << " -> Node";
- if (topDown)
- t << m_number;
- else
- t << cn->number();
- t << " [";
-
- static bool umlLook = Config_getBool(UML_LOOK);
- const EdgeProperties *eProps = umlLook ? &umlEdgeProps : &normalEdgeProps;
- QCString aStyle = eProps->arrowStyleMap[ei->m_color];
- bool umlUseArrow = aStyle=="odiamond";
-
- if (pointBack && !umlUseArrow) t << "dir=\"back\",";
- t << "color=\"" << eProps->edgeColorMap[ei->m_color]
- << "\",fontsize=\"" << FONTSIZE << "\",";
- t << "style=\"" << eProps->edgeStyleMap[ei->m_style] << "\"";
- if (!ei->m_label.isEmpty())
- {
- t << ",label=\" " << convertLabel(ei->m_label) << "\" ";
- }
- if (umlLook &&
- eProps->arrowStyleMap[ei->m_color] &&
- (gt==Inheritance || gt==Collaboration)
- )
- {
- bool rev = pointBack;
- if (umlUseArrow) rev=!rev; // UML use relates has arrow on the start side
- if (rev)
- t << ",arrowtail=\"" << eProps->arrowStyleMap[ei->m_color] << "\"";
- else
- t << ",arrowhead=\"" << eProps->arrowStyleMap[ei->m_color] << "\"";
- }
-
- if (format==GOF_BITMAP) t << ",fontname=\"" << FONTNAME << "\"";
- t << "];" << endl;
-}
-
-void DotNode::write(FTextStream &t,
- GraphType gt,
- GraphOutputFormat format,
- bool topDown,
- bool toChildren,
- bool backArrows
- )
-{
- //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);
- m_written=TRUE;
- QList<DotNode> *nl = toChildren ? m_children : m_parents;
- if (nl)
- {
- if (toChildren)
- {
- QListIterator<DotNode> dnli1(*nl);
- QListIterator<EdgeInfo> dnli2(*m_edgeInfo);
- DotNode *cn;
- for (dnli1.toFirst();(cn=dnli1.current());++dnli1,++dnli2)
- {
- if (cn->isVisible())
- {
- //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",cn->label().data());
- writeArrow(t,gt,format,cn,dnli2.current(),topDown,backArrows);
- }
- cn->write(t,gt,format,topDown,toChildren,backArrows);
- }
- }
- else // render parents
- {
- QListIterator<DotNode> dnli(*nl);
- DotNode *pn;
- for (dnli.toFirst();(pn=dnli.current());++dnli)
- {
- if (pn->isVisible())
- {
- //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",pn->label().data());
- writeArrow(t,
- gt,
- format,
- pn,
- pn->m_edgeInfo->at(pn->m_children->findRef(this)),
- FALSE,
- backArrows
- );
- }
- pn->write(t,gt,format,TRUE,FALSE,backArrows);
- }
- }
- }
- //printf("end DotNode::write(%d) name=%s\n",distance,m_label.data());
-}
-
-void DotNode::writeXML(FTextStream &t,bool isClassGraph)
-{
- t << " <node id=\"" << m_number << "\">" << endl;
- t << " <label>" << convertToXML(m_label) << "</label>" << endl;
- if (!m_url.isEmpty())
- {
- QCString url(m_url);
- const char *refPtr = url.data();
- char *urlPtr = strchr(url.rawData(),'$');
- if (urlPtr)
- {
- *urlPtr++='\0';
- t << " <link refid=\"" << convertToXML(urlPtr) << "\"";
- if (*refPtr!='\0')
- {
- t << " external=\"" << convertToXML(refPtr) << "\"";
- }
- t << "/>" << endl;
- }
- }
- if (m_children)
- {
- QListIterator<DotNode> nli(*m_children);
- QListIterator<EdgeInfo> eli(*m_edgeInfo);
- DotNode *childNode;
- EdgeInfo *edgeInfo;
- for (;(childNode=nli.current());++nli,++eli)
- {
- edgeInfo=eli.current();
- t << " <childnode refid=\"" << childNode->m_number << "\" relation=\"";
- if (isClassGraph)
- {
- switch(edgeInfo->m_color)
- {
- case EdgeInfo::Blue: t << "public-inheritance"; break;
- case EdgeInfo::Green: t << "protected-inheritance"; break;
- case EdgeInfo::Red: t << "private-inheritance"; break;
- case EdgeInfo::Purple: t << "usage"; break;
- case EdgeInfo::Orange: t << "template-instance"; break;
- case EdgeInfo::Orange2: t << "type-constraint"; break;
- case EdgeInfo::Grey: ASSERT(0); break;
- }
- }
- else // include graph
- {
- t << "include";
- }
- t << "\">" << endl;
- if (!edgeInfo->m_label.isEmpty())
- {
- int p=0;
- int ni;
- while ((ni=edgeInfo->m_label.find('\n',p))!=-1)
- {
- t << " <edgelabel>"
- << convertToXML(edgeInfo->m_label.mid(p,ni-p))
- << "</edgelabel>" << endl;
- p=ni+1;
- }
- t << " <edgelabel>"
- << convertToXML(edgeInfo->m_label.right(edgeInfo->m_label.length()-p))
- << "</edgelabel>" << endl;
- }
- t << " </childnode>" << endl;
- }
- }
- t << " </node>" << endl;
-}
-
-void DotNode::writeDocbook(FTextStream &t,bool isClassGraph)
-{
- t << " <node id=\"" << m_number << "\">" << endl;
- t << " <label>" << convertToXML(m_label) << "</label>" << endl;
- if (!m_url.isEmpty())
- {
- QCString url(m_url);
- const char *refPtr = url.data();
- char *urlPtr = strchr(url.rawData(),'$');
- if (urlPtr)
- {
- *urlPtr++='\0';
- t << " <link refid=\"" << convertToXML(urlPtr) << "\"";
- if (*refPtr!='\0')
- {
- t << " external=\"" << convertToXML(refPtr) << "\"";
- }
- t << "/>" << endl;
- }
- }
- if (m_children)
- {
- QListIterator<DotNode> nli(*m_children);
- QListIterator<EdgeInfo> eli(*m_edgeInfo);
- DotNode *childNode;
- EdgeInfo *edgeInfo;
- for (;(childNode=nli.current());++nli,++eli)
- {
- edgeInfo=eli.current();
- t << " <childnode refid=\"" << childNode->m_number << "\" relation=\"";
- if (isClassGraph)
- {
- switch(edgeInfo->m_color)
- {
- case EdgeInfo::Blue: t << "public-inheritance"; break;
- case EdgeInfo::Green: t << "protected-inheritance"; break;
- case EdgeInfo::Red: t << "private-inheritance"; break;
- case EdgeInfo::Purple: t << "usage"; break;
- case EdgeInfo::Orange: t << "template-instance"; break;
- case EdgeInfo::Orange2: t << "type-constraint"; break;
- case EdgeInfo::Grey: ASSERT(0); break;
- }
- }
- else // include graph
- {
- t << "include";
- }
- t << "\">" << endl;
- if (!edgeInfo->m_label.isEmpty())
- {
- int p=0;
- int ni;
- while ((ni=edgeInfo->m_label.find('\n',p))!=-1)
- {
- t << " <edgelabel>"
- << convertToXML(edgeInfo->m_label.mid(p,ni-p))
- << "</edgelabel>" << endl;
- p=ni+1;
- }
- t << " <edgelabel>"
- << convertToXML(edgeInfo->m_label.right(edgeInfo->m_label.length()-p))
- << "</edgelabel>" << endl;
- }
- t << " </childnode>" << endl;
- }
- }
- t << " </node>" << endl;
-}
-
-
-void DotNode::writeDEF(FTextStream &t)
-{
- const char* nodePrefix = " node-";
-
- t << " node = {" << endl;
- t << nodePrefix << "id = " << m_number << ';' << endl;
- t << nodePrefix << "label = '" << m_label << "';" << endl;
-
- if (!m_url.isEmpty())
- {
- QCString url(m_url);
- const char *refPtr = url.data();
- char *urlPtr = strchr(url.rawData(),'$');
- if (urlPtr)
- {
- *urlPtr++='\0';
- t << nodePrefix << "link = {" << endl << " "
- << nodePrefix << "link-id = '" << urlPtr << "';" << endl;
-
- if (*refPtr!='\0')
- {
- t << " " << nodePrefix << "link-external = '"
- << refPtr << "';" << endl;
- }
- t << " };" << endl;
- }
- }
- if (m_children)
- {
- QListIterator<DotNode> nli(*m_children);
- QListIterator<EdgeInfo> eli(*m_edgeInfo);
- DotNode *childNode;
- EdgeInfo *edgeInfo;
- for (;(childNode=nli.current());++nli,++eli)
- {
- edgeInfo=eli.current();
- t << " node-child = {" << endl;
- t << " child-id = '" << childNode->m_number << "';" << endl;
- t << " relation = ";
-
- switch(edgeInfo->m_color)
- {
- case EdgeInfo::Blue: t << "public-inheritance"; break;
- case EdgeInfo::Green: t << "protected-inheritance"; break;
- case EdgeInfo::Red: t << "private-inheritance"; break;
- case EdgeInfo::Purple: t << "usage"; break;
- case EdgeInfo::Orange: t << "template-instance"; break;
- case EdgeInfo::Orange2: t << "type-constraint"; break;
- case EdgeInfo::Grey: ASSERT(0); break;
- }
- t << ';' << endl;
-
- if (!edgeInfo->m_label.isEmpty())
- {
- t << " edgelabel = <<_EnD_oF_dEf_TeXt_" << endl
- << edgeInfo->m_label << endl
- << "_EnD_oF_dEf_TeXt_;" << endl;
- }
- t << " }; /* node-child */" << endl;
- } /* for (;childNode...) */
- }
- t << " }; /* node */" << endl;
-}
-
-
-void DotNode::clearWriteFlag()
-{
- m_written=FALSE;
- if (m_parents!=0)
- {
- QListIterator<DotNode> dnlip(*m_parents);
- DotNode *pn;
- for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
- {
- if (pn->m_written)
- {
- pn->clearWriteFlag();
- }
- }
- }
- if (m_children!=0)
- {
- QListIterator<DotNode> dnlic(*m_children);
- DotNode *cn;
- for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
- {
- if (cn->m_written)
- {
- cn->clearWriteFlag();
- }
- }
- }
-}
-
-void DotNode::colorConnectedNodes(int curColor)
-{
- if (m_children)
- {
- QListIterator<DotNode> dnlic(*m_children);
- DotNode *cn;
- for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
- {
- if (cn->m_subgraphId==-1) // uncolored child node
- {
- cn->m_subgraphId=curColor;
- cn->markAsVisible();
- cn->colorConnectedNodes(curColor);
- //printf("coloring node %s (%p): %d\n",cn->m_label.data(),cn,cn->m_subgraphId);
- }
- }
- }
-
- if (m_parents)
- {
- QListIterator<DotNode> dnlip(*m_parents);
- DotNode *pn;
- for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
- {
- if (pn->m_subgraphId==-1) // uncolored parent node
- {
- pn->m_subgraphId=curColor;
- pn->markAsVisible();
- pn->colorConnectedNodes(curColor);
- //printf("coloring node %s (%p): %d\n",pn->m_label.data(),pn,pn->m_subgraphId);
- }
- }
- }
-}
-
-void DotNode::renumberNodes(int &number)
-{
- m_number = number++;
- if (m_children)
- {
- QListIterator<DotNode> dnlic(*m_children);
- DotNode *cn;
- for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
- {
- if (!cn->m_renumbered)
- {
- cn->m_renumbered = true;
- cn->renumberNodes(number);
- }
- }
- }
-}
-
-const DotNode *DotNode::findDocNode() const
-{
- if (!m_url.isEmpty()) return this;
- //printf("findDocNode(): `%s'\n",m_label.data());
- if (m_parents)
- {
- QListIterator<DotNode> dnli(*m_parents);
- DotNode *pn;
- for (dnli.toFirst();(pn=dnli.current());++dnli)
- {
- if (!pn->m_hasDoc)
- {
- pn->m_hasDoc=TRUE;
- const DotNode *dn = pn->findDocNode();
- if (dn) return dn;
- }
- }
- }
- if (m_children)
- {
- QListIterator<DotNode> dnli(*m_children);
- DotNode *cn;
- for (dnli.toFirst();(cn=dnli.current());++dnli)
- {
- if (!cn->m_hasDoc)
- {
- cn->m_hasDoc=TRUE;
- const DotNode *dn = cn->findDocNode();
- if (dn) return dn;
- }
- }
- }
- return 0;
-}
-
-//--------------------------------------------------------------------
-
-void DotGfxHierarchyTable::createGraph(DotNode *n,FTextStream &out,
- const char *path,const char *fileName,int id) const
+class GraphLegendDotGraph : public DotGraph
{
- QDir d(path);
- QCString baseName;
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- if (m_prefix.isEmpty())
- baseName.sprintf("inherit_graph_%d",id);
- else
- baseName.sprintf("%sinherit_graph_%d",m_prefix.data(),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)
+ private:
+ virtual QCString getBaseName() const
{
- node->clearWriteFlag();
+ return "graph_legend";
}
- }
- for (dnli2.toFirst();(node=dnli2.current());++dnli2)
- {
- if (node->m_subgraphId==n->m_subgraphId)
- {
- node->write(md5stream,DotNode::Hierarchy,GOF_BITMAP,FALSE,TRUE,TRUE);
- }
- }
- writeGraphFooter(md5stream);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),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();
- DotRunner *dotRun = new DotRunner(dotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,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))
+ virtual void computeTheGraph()
{
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,QCString(),
- FALSE,QCString(),FALSE,0);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,
- absImgName,QCString());
- out << "<!-- SVG " << mapId << " -->" << endl;
+ FTextStream md5stream(&m_theGraph);
+ writeGraphHeader(md5stream,theTranslator->trLegendTitle());
+ md5stream << " Node9 [shape=\"box\",label=\"Inherited\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",fillcolor=\"grey75\",style=\"filled\" fontcolor=\"black\"];\n";
+ md5stream << " Node10 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node10 [shape=\"box\",label=\"PublicBase\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classPublicBase" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node11 -> Node10 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node11 [shape=\"box\",label=\"Truncated\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"red\",URL=\"$classTruncated" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node13 -> Node9 [dir=\"back\",color=\"darkgreen\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node13 [shape=\"box\",label=\"ProtectedBase\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classProtectedBase" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node14 -> Node9 [dir=\"back\",color=\"firebrick4\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node14 [shape=\"box\",label=\"PrivateBase\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classPrivateBase" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node15 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node15 [shape=\"box\",label=\"Undocumented\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"grey75\"];\n";
+ md5stream << " Node16 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node16 [shape=\"box\",label=\"Templ< int >\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node17 -> Node16 [dir=\"back\",color=\"orange\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"dashed\",label=\"< int >\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node17 [shape=\"box\",label=\"Templ< T >\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node18 -> Node9 [dir=\"back\",color=\"darkorchid3\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"dashed\",label=\"m_usedClass\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node18 [shape=\"box\",label=\"Used\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classUsed" << Doxygen::htmlFileExtension << "\"];\n";
+ writeGraphFooter(md5stream);
}
- }
- else // normal bitmap
- {
- out << "<img src=\"" << imgName << "\" border=\"0\" alt=\"\" usemap=\"#"
- << mapLabel << "\"/>" << endl;
- if (regenerate || !insertMapFile(out,absMapName,QCString(),mapLabel))
+ virtual QCString getMapLabel() const
{
- int mapId = DotManager::instance()->addMap(fileName,absMapName,QCString(),
- FALSE,QCString(),mapLabel);
- out << "<!-- MAP " << mapId << " -->" << endl;
+ return "";
}
- }
-}
-
-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);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- // put each connected subgraph of the hierarchy in a row of the HTML output
- out << "<table border=\"0\" cellspacing=\"10\" cellpadding=\"0\">" << endl;
-
- QListIterator<DotNode> dnli(*m_rootSubgraphs);
- DotNode *n;
- int count=0;
- for (dnli.toFirst();(n=dnli.current());++dnli)
- {
- out << "<tr><td>";
- createGraph(n,out,path,fileName,count++);
- out << "</td></tr>" << endl;
- }
- out << "</table>" << endl;
-}
-
-void DotGfxHierarchyTable::addHierarchy(DotNode *n,const ClassDef *cd,bool hideSuper)
-{
- //printf("addHierarchy `%s' baseClasses=%d\n",cd->name().data(),cd->baseClasses()->count());
- if (cd->subClasses())
- {
- BaseClassListIterator bcli(*cd->subClasses());
- BaseClassDef *bcd;
- for ( ; (bcd=bcli.current()) ; ++bcli )
- {
- ClassDef *bClass=bcd->classDef;
- //printf(" Trying sub class=`%s' usedNodes=%d\n",bClass->name().data(),m_usedNodes->count());
- if (bClass->isVisibleInHierarchy() && hasVisibleRoot(bClass->baseClasses()))
- {
- DotNode *bn;
- //printf(" Node `%s' Found visible class=`%s'\n",n->m_label.data(),
- // bClass->name().data());
- if ((bn=m_usedNodes->find(bClass->name()))) // node already present
- {
- if (n->m_children==0 || n->m_children->findRef(bn)==-1) // no arrow yet
- {
- n->addChild(bn,bcd->prot);
- bn->addParent(n);
- //printf(" Adding node %s to existing base node %s (c=%d,p=%d)\n",
- // n->m_label.data(),
- // bn->m_label.data(),
- // bn->m_children ? bn->m_children->count() : 0,
- // bn->m_parents ? bn->m_parents->count() : 0
- // );
- }
- //else
- //{
- // printf(" Class already has an arrow!\n");
- //}
- }
- else
- {
- QCString tmp_url="";
- if (bClass->isLinkable() && !bClass->isHidden())
- {
- tmp_url=bClass->getReference()+"$"+bClass->getOutputFileBase();
- if (!bClass->anchor().isEmpty())
- {
- tmp_url+="#"+bClass->anchor();
- }
- }
- QCString tooltip = bClass->briefDescriptionAsTooltip();
- bn = new DotNode(getNextNodeNumber(),
- bClass->displayName(),
- tooltip,
- tmp_url.data()
- );
- n->addChild(bn,bcd->prot);
- bn->addParent(n);
- //printf(" Adding node %s to new base node %s (c=%d,p=%d)\n",
- // n->m_label.data(),
- // bn->m_label.data(),
- // bn->m_children ? bn->m_children->count() : 0,
- // bn->m_parents ? bn->m_parents->count() : 0
- // );
- //printf(" inserting %s (%p)\n",bClass->name().data(),bn);
- m_usedNodes->insert(bClass->name(),bn); // add node to the used list
- }
- if (!bClass->isVisited() && !hideSuper && bClass->subClasses())
- {
- bool wasVisited=bClass->isVisited();
- bClass->setVisited(TRUE);
- addHierarchy(bn,bClass,wasVisited);
- }
- }
- }
- }
- //printf("end addHierarchy\n");
-}
-
-void DotGfxHierarchyTable::addClassList(const ClassSDict *cl)
-{
- static bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
- ClassSDict::Iterator cli(*cl);
- ClassDef *cd;
- for (cli.toLast();(cd=cli.current());--cli)
- {
- //printf("Trying %s subClasses=%d\n",cd->name().data(),cd->subClasses()->count());
- if (cd->getLanguage()==SrcLangExt_VHDL &&
- (VhdlDocGen::VhdlClasses)cd->protection()!=VhdlDocGen::ENTITYCLASS
- )
- {
- continue;
- }
- if (sliceOpt && cd->compoundType() != m_classType)
- {
- continue;
- }
- if (!hasVisibleRoot(cd->baseClasses()) &&
- cd->isVisibleInHierarchy()
- ) // root node in the forest
- {
- QCString tmp_url="";
- if (cd->isLinkable() && !cd->isHidden())
- {
- tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
- if (!cd->anchor().isEmpty())
- {
- tmp_url+="#"+cd->anchor();
- }
- }
- //printf("Inserting root class %s\n",cd->name().data());
- QCString tooltip = cd->briefDescriptionAsTooltip();
- DotNode *n = new DotNode(getNextNodeNumber(),
- cd->displayName(),
- tooltip,
- tmp_url.data());
-
- //m_usedNodes->clear();
- m_usedNodes->insert(cd->name(),n);
- m_rootNodes->insert(0,n);
- if (!cd->isVisited() && cd->subClasses())
- {
- addHierarchy(n,cd,cd->isVisited());
- cd->setVisited(TRUE);
- }
- }
- }
-}
-
-DotGfxHierarchyTable::DotGfxHierarchyTable(const char *prefix,ClassDef::CompoundType ct)
- : m_prefix(prefix)
- , m_classType(ct)
-{
- m_rootNodes = new QList<DotNode>;
- m_usedNodes = new QDict<DotNode>(1009);
- m_usedNodes->setAutoDelete(TRUE);
- m_rootSubgraphs = new DotNodeList;
-
- // build a graph with each class as a node and the inheritance relations
- // as edges
- initClassHierarchy(Doxygen::classSDict);
- initClassHierarchy(Doxygen::hiddenClasses);
- addClassList(Doxygen::classSDict);
- addClassList(Doxygen::hiddenClasses);
- // m_usedNodes now contains all nodes in the graph
-
- // color the graph into a set of independent subgraphs
- bool done=FALSE;
- int curColor=0;
- QListIterator<DotNode> dnli(*m_rootNodes);
- while (!done) // there are still nodes to color
- {
- DotNode *n;
- done=TRUE; // we are done unless there are still uncolored nodes
- for (dnli.toLast();(n=dnli.current());--dnli)
- {
- if (n->m_subgraphId==-1) // not yet colored
- {
- //printf("Starting at node %s (%p): %d\n",n->m_label.data(),n,curColor);
- done=FALSE; // still uncolored nodes
- n->m_subgraphId=curColor;
- n->markAsVisible();
- n->colorConnectedNodes(curColor);
- curColor++;
- const DotNode *dn=n->findDocNode();
- if (dn!=0)
- m_rootSubgraphs->inSort(dn);
- else
- m_rootSubgraphs->inSort(n);
- }
- }
- }
-
- //printf("Number of independent subgraphs: %d\n",curColor);
- QListIterator<DotNode> dnli2(*m_rootSubgraphs);
- DotNode *n;
- for (dnli2.toFirst();(n=dnli2.current());++dnli2)
- {
- //printf("Node %s color=%d (c=%d,p=%d)\n",
- // n->m_label.data(),n->m_subgraphId,
- // n->m_children?n->m_children->count():0,
- // n->m_parents?n->m_parents->count():0);
- int number=0;
- n->renumberNodes(number);
- }
-}
-
-DotGfxHierarchyTable::~DotGfxHierarchyTable()
-{
- //printf("DotGfxHierarchyTable::~DotGfxHierarchyTable\n");
-
- //QDictIterator<DotNode> di(*m_usedNodes);
- //DotNode *n;
- //for (;(n=di.current());++di)
- //{
- // printf("Node %p: %s\n",n,n->label().data());
- //}
-
- delete m_rootNodes;
- delete m_usedNodes;
- delete m_rootSubgraphs;
-}
-
-//--------------------------------------------------------------------
-
-void DotClassGraph::addClass(const ClassDef *cd,DotNode *n,int prot,
- const char *label,const char *usedName,const char *templSpec,bool base,int distance)
-{
- if (Config_getBool(HIDE_UNDOC_CLASSES) && !cd->isLinkable()) return;
-
- int edgeStyle = (label || prot==EdgeInfo::Orange || prot==EdgeInfo::Orange2) ? EdgeInfo::Dashed : EdgeInfo::Solid;
- QCString className;
- if (cd->isAnonymous())
- {
- className="anonymous:";
- className+=label;
- }
- else if (usedName) // name is a typedef
- {
- className=usedName;
- }
- else if (templSpec) // name has a template part
- {
- className=insertTemplateSpecifierInScope(cd->name(),templSpec);
- }
- else // just a normal name
- {
- className=cd->displayName();
- }
- //printf("DotClassGraph::addClass(class=`%s',parent=%s,prot=%d,label=%s,dist=%d,usedName=%s,templSpec=%s,base=%d)\n",
- // className.data(),n->m_label.data(),prot,label,distance,usedName,templSpec,base);
- DotNode *bn = m_usedNodes->find(className);
- if (bn) // class already inserted
- {
- if (base)
- {
- n->addChild(bn,prot,edgeStyle,label);
- bn->addParent(n);
- }
- else
- {
- bn->addChild(n,prot,edgeStyle,label);
- n->addParent(bn);
- }
- bn->setDistance(distance);
- //printf(" add exiting node %s of %s\n",bn->m_label.data(),n->m_label.data());
- }
- else // new class
- {
- QCString displayName=className;
- if (Config_getBool(HIDE_SCOPE_NAMES)) displayName=stripScope(displayName);
- QCString tmp_url;
- if (cd->isLinkable() && !cd->isHidden())
- {
- tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
- if (!cd->anchor().isEmpty())
- {
- tmp_url+="#"+cd->anchor();
- }
- }
- QCString tooltip = cd->briefDescriptionAsTooltip();
- bn = new DotNode(getNextNodeNumber(),
- displayName,
- tooltip,
- tmp_url.data(),
- FALSE, // rootNode
- cd
- );
- if (base)
- {
- n->addChild(bn,prot,edgeStyle,label);
- bn->addParent(n);
- }
- else
- {
- bn->addChild(n,prot,edgeStyle,label);
- n->addParent(bn);
- }
- bn->setDistance(distance);
- m_usedNodes->insert(className,bn);
- //printf(" add new child node `%s' to %s hidden=%d url=%s\n",
- // className.data(),n->m_label.data(),cd->isHidden(),tmp_url.data());
-
- buildGraph(cd,bn,base,distance+1);
- }
-}
-
-void DotClassGraph::determineTruncatedNodes(QList<DotNode> &queue,bool includeParents)
-{
- while (queue.count()>0)
- {
- DotNode *n = queue.take(0);
- if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
- {
- bool truncated = FALSE;
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- if (!dn->isVisible())
- truncated = TRUE;
- else
- queue.append(dn);
- }
- }
- if (n->m_parents && includeParents)
- {
- QListIterator<DotNode> li(*n->m_parents);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- if (!dn->isVisible())
- truncated = TRUE;
- else
- queue.append(dn);
- }
- }
- n->markAsTruncated(truncated);
- }
- }
-}
-
-bool DotClassGraph::determineVisibleNodes(DotNode *rootNode,
- int maxNodes,bool includeParents)
-{
- QList<DotNode> childQueue;
- QList<DotNode> parentQueue;
- QArray<int> childTreeWidth;
- QArray<int> parentTreeWidth;
- childQueue.append(rootNode);
- if (includeParents) parentQueue.append(rootNode);
- bool firstNode=TRUE; // flag to force reprocessing rootNode in the parent loop
- // despite being marked visible in the child loop
- while ((childQueue.count()>0 || parentQueue.count()>0) && maxNodes>0)
- {
- static int maxDistance = Config_getInt(MAX_DOT_GRAPH_DEPTH);
- if (childQueue.count()>0)
- {
- DotNode *n = childQueue.take(0);
- int distance = n->distance();
- if (!n->isVisible() && distance<=maxDistance) // not yet processed
- {
- if (distance>0)
- {
- int oldSize=(int)childTreeWidth.size();
- if (distance>oldSize)
- {
- childTreeWidth.resize(QMAX(childTreeWidth.size(),(uint)distance));
- int i; for (i=oldSize;i<distance;i++) childTreeWidth[i]=0;
- }
- childTreeWidth[distance-1]+=n->label().length();
- }
- n->markAsVisible();
- maxNodes--;
- // add direct children
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- childQueue.append(dn);
- }
- }
- }
- }
- if (includeParents && parentQueue.count()>0)
- {
- DotNode *n = parentQueue.take(0);
- if ((!n->isVisible() || firstNode) && n->distance()<=maxDistance) // not yet processed
- {
- firstNode=FALSE;
- int distance = n->distance();
- if (distance>0)
- {
- int oldSize = (int)parentTreeWidth.size();
- if (distance>oldSize)
- {
- parentTreeWidth.resize(QMAX(parentTreeWidth.size(),(uint)distance));
- int i; for (i=oldSize;i<distance;i++) parentTreeWidth[i]=0;
- }
- parentTreeWidth[distance-1]+=n->label().length();
- }
- n->markAsVisible();
- maxNodes--;
- // add direct parents
- if (n->m_parents)
- {
- QListIterator<DotNode> li(*n->m_parents);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- parentQueue.append(dn);
- }
- }
- }
- }
- }
- if (Config_getBool(UML_LOOK)) return FALSE; // UML graph are always top to bottom
- int maxWidth=0;
- int maxHeight=(int)QMAX(childTreeWidth.size(),parentTreeWidth.size());
- uint i;
- for (i=0;i<childTreeWidth.size();i++)
- {
- if (childTreeWidth.at(i)>maxWidth) maxWidth=childTreeWidth.at(i);
- }
- for (i=0;i<parentTreeWidth.size();i++)
- {
- if (parentTreeWidth.at(i)>maxWidth) maxWidth=parentTreeWidth.at(i);
- }
- //printf("max tree width=%d, max tree height=%d\n",maxWidth,maxHeight);
- return maxWidth>80 && maxHeight<12; // used metric to decide to render the tree
- // from left to right instead of top to bottom,
- // with the idea to render very wide trees in
- // left to right order.
-}
-
-void DotClassGraph::buildGraph(const ClassDef *cd,DotNode *n,bool base,int distance)
-{
- static bool templateRelations = Config_getBool(TEMPLATE_RELATIONS);
- //printf("DocClassGraph::buildGraph(%s,distance=%d,base=%d)\n",
- // cd->name().data(),distance,base);
- // ---- Add inheritance relations
-
- if (m_graphType == DotNode::Inheritance || m_graphType==DotNode::Collaboration)
- {
- BaseClassList *bcl = base ? cd->baseClasses() : cd->subClasses();
- if (bcl)
- {
- BaseClassListIterator bcli(*bcl);
- BaseClassDef *bcd;
- for ( ; (bcd=bcli.current()) ; ++bcli )
- {
- //printf("-------- inheritance relation %s->%s templ=`%s'\n",
- // cd->name().data(),bcd->classDef->name().data(),bcd->templSpecifiers.data());
- addClass(bcd->classDef,n,bcd->prot,0,bcd->usedName,
- bcd->templSpecifiers,base,distance);
- }
- }
- }
- if (m_graphType == DotNode::Collaboration)
- {
- // ---- Add usage relations
-
- UsesClassDict *dict =
- base ? cd->usedImplementationClasses() :
- cd->usedByImplementationClasses()
- ;
- if (dict)
- {
- UsesClassDictIterator ucdi(*dict);
- UsesClassDef *ucd;
- for (;(ucd=ucdi.current());++ucdi)
- {
- QCString label;
- QDictIterator<void> dvi(*ucd->accessors);
- const char *s;
- bool first=TRUE;
- int count=0;
- int maxLabels=10;
- for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count)
- {
- if (first)
- {
- label=s;
- first=FALSE;
- }
- else
- {
- label+=QCString("\n")+s;
- }
- }
- if (count==maxLabels) label+="\n...";
- //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data());
- addClass(ucd->classDef,n,EdgeInfo::Purple,label,0,
- ucd->templSpecifiers,base,distance);
- }
- }
- }
- if (templateRelations && base)
- {
- ConstraintClassDict *dict = cd->templateTypeConstraints();
- if (dict)
- {
- ConstraintClassDictIterator ccdi(*dict);
- ConstraintClassDef *ccd;
- for (;(ccd=ccdi.current());++ccdi)
- {
- QCString label;
- QDictIterator<void> dvi(*ccd->accessors);
- const char *s;
- bool first=TRUE;
- int count=0;
- int maxLabels=10;
- for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count)
- {
- if (first)
- {
- label=s;
- first=FALSE;
- }
- else
- {
- label+=QCString("\n")+s;
- }
- }
- if (count==maxLabels) label+="\n...";
- //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data());
- addClass(ccd->classDef,n,EdgeInfo::Orange2,label,0,
- 0,TRUE,distance);
- }
- }
- }
-
- // ---- Add template instantiation relations
-
- if (templateRelations)
- {
- if (base) // template relations for base classes
- {
- const ClassDef *templMaster=cd->templateMaster();
- if (templMaster)
- {
- QDictIterator<ClassDef> cli(*templMaster->getTemplateInstances());
- const ClassDef *templInstance;
- for (;(templInstance=cli.current());++cli)
- {
- if (templInstance==cd)
- {
- addClass(templMaster,n,EdgeInfo::Orange,cli.currentKey(),0,
- 0,TRUE,distance);
- }
- }
- }
- }
- else // template relations for super classes
- {
- const QDict<ClassDef> *templInstances = cd->getTemplateInstances();
- if (templInstances)
- {
- QDictIterator<ClassDef> cli(*templInstances);
- const ClassDef *templInstance;
- for (;(templInstance=cli.current());++cli)
- {
- addClass(templInstance,n,EdgeInfo::Orange,cli.currentKey(),0,
- 0,FALSE,distance);
- }
- }
- }
- }
-}
-
-DotClassGraph::DotClassGraph(const ClassDef *cd,DotNode::GraphType t)
-{
- //printf("--------------- DotClassGraph::DotClassGraph `%s'\n",cd->displayName().data());
- m_graphType = t;
- QCString tmp_url="";
- if (cd->isLinkable() && !cd->isHidden())
- {
- tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
- if (!cd->anchor().isEmpty())
- {
- tmp_url+="#"+cd->anchor();
- }
- }
- QCString className = cd->displayName();
- QCString tooltip = cd->briefDescriptionAsTooltip();
- m_startNode = new DotNode(getNextNodeNumber(),
- className,
- tooltip,
- tmp_url.data(),
- TRUE, // is a root node
- cd
- );
- m_startNode->setDistance(0);
- m_usedNodes = new QDict<DotNode>(1009);
- m_usedNodes->insert(className,m_startNode);
-
- //printf("Root node %s\n",cd->name().data());
- //if (m_recDepth>0)
- //{
- buildGraph(cd,m_startNode,TRUE,1);
- if (t==DotNode::Inheritance) buildGraph(cd,m_startNode,FALSE,1);
- //}
-
- static int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- //int directChildNodes = 1;
- //if (m_startNode->m_children!=0)
- // directChildNodes+=m_startNode->m_children->count();
- //if (t==DotNode::Inheritance && m_startNode->m_parents!=0)
- // directChildNodes+=m_startNode->m_parents->count();
- //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
- //openNodeQueue.append(m_startNode);
- m_lrRank = determineVisibleNodes(m_startNode,maxNodes,t==DotNode::Inheritance);
- QList<DotNode> openNodeQueue;
- openNodeQueue.append(m_startNode);
- determineTruncatedNodes(openNodeQueue,t==DotNode::Inheritance);
-
- m_collabFileName = cd->collaborationGraphFileName();
- m_inheritFileName = cd->inheritanceGraphFileName();
-}
-
-bool DotClassGraph::isTrivial() const
-{
- static bool umlLook = Config_getBool(UML_LOOK);
- if (m_graphType==DotNode::Inheritance)
- return m_startNode->m_children==0 && m_startNode->m_parents==0;
- else
- return !umlLook && m_startNode->m_children==0;
-}
-
-bool DotClassGraph::isTooBig() const
-{
- static int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int numNodes = 0;
- numNodes+= m_startNode->m_children ? m_startNode->m_children->count() : 0;
- if (m_graphType==DotNode::Inheritance)
- {
- numNodes+= m_startNode->m_parents ? m_startNode->m_parents->count() : 0;
- }
- return numNodes>=maxNodes;
-}
-
-DotClassGraph::~DotClassGraph()
-{
- deleteNodes(m_startNode);
- delete m_usedNodes;
-}
-
-/*! Computes a 16 byte md5 checksum for a given dot graph.
- * The md5 checksum is returned as a 32 character ASCII string.
- */
-QCString computeMd5Signature(DotNode *root,
- DotNode::GraphType gt,
- GraphOutputFormat format,
- const QCString &rank, // either "LR", "RL", or ""
- bool renderParents,
- bool backArrows,
- const QCString &title,
- QCString &graphStr
- )
-{
- //printf("computeMd5Signature\n");
- QGString buf;
- FTextStream md5stream(&buf);
- writeGraphHeader(md5stream,title);
- if (!rank.isEmpty())
- {
- md5stream << " rankdir=\"" << rank << "\";" << endl;
- }
- root->clearWriteFlag();
- root->write(md5stream,
- gt,
- format,
- gt!=DotNode::CallGraph && gt!=DotNode::Dependency,
- TRUE,
- backArrows);
- if (renderParents && root->m_parents)
- {
- QListIterator<DotNode> dnli(*root->m_parents);
- DotNode *pn;
- for (dnli.toFirst();(pn=dnli.current());++dnli)
- {
- if (pn->isVisible())
- {
- root->writeArrow(md5stream, // stream
- gt, // graph type
- format, // output format
- pn, // child node
- pn->m_edgeInfo->at(pn->m_children->findRef(root)), // edge info
- FALSE, // topDown?
- backArrows // point back?
- );
- }
- pn->write(md5stream, // stream
- gt, // graph type
- format, // output format
- TRUE, // topDown?
- FALSE, // toChildren?
- backArrows // backward pointing arrows?
- );
- }
- }
- writeGraphFooter(md5stream);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)buf.data(),buf.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),33);
- graphStr=buf.data();
- //printf("md5: %s | file: %s\n",sigStr,baseName.data());
- return sigStr;
-}
-
-static bool updateDotGraph(DotNode *root,
- DotNode::GraphType gt,
- const QCString &baseName,
- GraphOutputFormat format,
- const QCString &rank,
- bool renderParents,
- bool backArrows,
- const QCString &title=QCString()
- )
-{
- QCString theGraph;
- // TODO: write graph to theGraph, then compute md5 checksum
- QCString md5 = computeMd5Signature(
- root,gt,format,rank,renderParents,
- backArrows,title,theGraph);
- QFile f(baseName+".dot");
- if (f.open(IO_WriteOnly))
- {
- FTextStream t(&f);
- t << theGraph;
- }
- return checkAndUpdateMd5Signature(baseName,md5); // graph needs to be regenerated
-}
-
-QCString DotClassGraph::writeGraph(FTextStream &out,
- GraphOutputFormat graphFormat,
- EmbeddedOutputFormat textFormat,
- const char *path,
- const char *fileName,
- const char *relPath,
- bool /*isTBRank*/,
- bool generateImageMap,
- int graphId) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QCString baseName;
- QCString mapName;
- switch (m_graphType)
- {
- case DotNode::Collaboration:
- mapName="coll_map";
- baseName=m_collabFileName;
- break;
- case DotNode::Inheritance:
- mapName="inherit_map";
- baseName=m_inheritFileName;
- break;
- default:
- ASSERT(0);
- break;
- }
-
- // derive target file names from baseName
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString absBaseName = d.absPath().utf8()+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- QCString absImgName = absBaseName+"."+imgExt;
-
- bool regenerate = FALSE;
- if (updateDotGraph(m_startNode,
- m_graphType,
- absBaseName,
- graphFormat,
- m_lrRank ? "LR" : "",
- m_graphType==DotNode::Inheritance,
- TRUE,
- m_startNode->label()
- ) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
- if (graphFormat==GOF_BITMAP) // run dot to create a bitmap image
- {
- DotRunner *dotRun = new DotRunner(absDotName,
- d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
-
- }
- else if (graphFormat==GOF_EPS) // run dot to create a .eps image
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
- }
- }
- Doxygen::indexList->addImageFile(baseName+"."+imgExt);
-
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- out << "<para>" << endl;
- out << " <informalfigure>" << endl;
- out << " <mediaobject>" << endl;
- out << " <imageobject>" << endl;
- out << " <imagedata";
- out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- out << "</imagedata>" << endl;
- out << " </imageobject>" << endl;
- out << " </mediaobject>" << endl;
- out << " </informalfigure>" << endl;
- out << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && generateImageMap) // produce HTML to include the image
- {
- QCString mapLabel = escapeCharsInString(m_startNode->m_label,FALSE)+"_"+
- escapeCharsInString(mapName,FALSE);
- if (imgExt=="svg") // add link to SVG file without map file
- {
- out << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- out << "<!-- SVG " << mapId << " -->" << endl;
- }
- out << "</div>" << endl;
- }
- else // add link to bitmap file with image map
- {
- out << "<div class=\"center\">";
- out << "<img src=\"" << relPath << baseName << "."
- << imgExt << "\" border=\"0\" usemap=\"#"
- << mapLabel << "\" alt=\"";
- switch (m_graphType)
- {
- case DotNode::Collaboration:
- out << "Collaboration graph";
- break;
- case DotNode::Inheritance:
- out << "Inheritance graph";
- break;
- default:
- ASSERT(0);
- break;
- }
- out << "\"/>";
- out << "</div>" << endl;
- if (regenerate || !insertMapFile(out,absMapName,relPath,mapLabel))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- FALSE,QCString(),mapLabel);
- out << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- }
- else if (graphFormat==GOF_EPS) // produce tex to include the .eps image
- {
- if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE /*TRUE*/);
- out << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-//--------------------------------------------------------------------
-
-void DotClassGraph::writeXML(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeXML(t,TRUE);
- }
-}
-
-void DotClassGraph::writeDocbook(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeDocbook(t,TRUE);
- }
-}
-
-void DotClassGraph::writeDEF(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeDEF(t);
- }
-}
-
-//--------------------------------------------------------------------
-
-void DotInclDepGraph::buildGraph(DotNode *n,const FileDef *fd,int distance)
-{
- QList<IncludeInfo> *includeFiles =
- m_inverse ? fd->includedByFileList() : fd->includeFileList();
- if (includeFiles)
- {
- QListIterator<IncludeInfo> ili(*includeFiles);
- IncludeInfo *ii;
- for (;(ii=ili.current());++ili)
- {
- const FileDef *bfd = ii->fileDef;
- QCString in = ii->includeName;
- //printf(">>>> in=`%s' bfd=%p\n",ii->includeName.data(),bfd);
- bool doc=TRUE,src=FALSE;
- if (bfd)
- {
- in = bfd->absFilePath();
- doc = bfd->isLinkable() && !bfd->isHidden();
- src = bfd->generateSourceFile();
- }
- if (doc || src || !Config_getBool(HIDE_UNDOC_RELATIONS))
- {
- QCString url="";
- if (bfd) url=bfd->getOutputFileBase().copy();
- if (!doc && src)
- {
- url=bfd->getSourceFileBase();
- }
- DotNode *bn = m_usedNodes->find(in);
- if (bn) // file is already a node in the graph
- {
- n->addChild(bn,0,0,0);
- bn->addParent(n);
- bn->setDistance(distance);
- }
- else
- {
- QCString tmp_url;
- QCString tooltip;
- if (bfd)
- {
- tmp_url=doc || src ? bfd->getReference()+"$"+url : QCString();
- tooltip = bfd->briefDescriptionAsTooltip();
- }
- bn = new DotNode(
- getNextNodeNumber(),// n
- ii->includeName, // label
- tooltip, // tip
- tmp_url, // url
- FALSE, // rootNode
- 0 // cd
- );
- n->addChild(bn,0,0,0);
- bn->addParent(n);
- m_usedNodes->insert(in,bn);
- bn->setDistance(distance);
-
- if (bfd) buildGraph(bn,bfd,distance+1);
- }
- }
- }
- }
-}
-
-void DotInclDepGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes)
-{
- while (queue.count()>0 && maxNodes>0)
- {
- static int maxDistance = Config_getInt(MAX_DOT_GRAPH_DEPTH);
- DotNode *n = queue.take(0);
- if (!n->isVisible() && n->distance()<=maxDistance) // not yet processed
- {
- n->markAsVisible();
- maxNodes--;
- // add direct children
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- queue.append(dn);
- }
- }
- }
- }
-}
-
-void DotInclDepGraph::determineTruncatedNodes(QList<DotNode> &queue)
-{
- while (queue.count()>0)
- {
- DotNode *n = queue.take(0);
- if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
- {
- bool truncated = FALSE;
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- if (!dn->isVisible())
- truncated = TRUE;
- else
- queue.append(dn);
- }
- }
- n->markAsTruncated(truncated);
- }
- }
-}
-
-DotInclDepGraph::DotInclDepGraph(const FileDef *fd,bool inverse)
-{
- m_inverse = inverse;
- ASSERT(fd!=0);
- m_inclDepFileName = fd->includeDependencyGraphFileName();
- m_inclByDepFileName = fd->includedByDependencyGraphFileName();
- QCString tmp_url=fd->getReference()+"$"+fd->getOutputFileBase();
- QCString tooltip = fd->briefDescriptionAsTooltip();
- m_startNode = new DotNode(getNextNodeNumber(),
- fd->docName(),
- tooltip,
- tmp_url.data(),
- TRUE // root node
- );
- m_startNode->setDistance(0);
- m_usedNodes = new QDict<DotNode>(1009);
- m_usedNodes->insert(fd->absFilePath(),m_startNode);
- buildGraph(m_startNode,fd,1);
-
- static int nodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int maxNodes = nodes;
- //int directChildNodes = 1;
- //if (m_startNode->m_children!=0)
- // directChildNodes+=m_startNode->m_children->count();
- //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
- QList<DotNode> openNodeQueue;
- openNodeQueue.append(m_startNode);
- determineVisibleNodes(openNodeQueue,maxNodes);
- openNodeQueue.clear();
- openNodeQueue.append(m_startNode);
- determineTruncatedNodes(openNodeQueue);
-}
-
-DotInclDepGraph::~DotInclDepGraph()
-{
- deleteNodes(m_startNode);
- delete m_usedNodes;
-}
-
-QCString DotInclDepGraph::writeGraph(FTextStream &out,
- GraphOutputFormat graphFormat,
- EmbeddedOutputFormat textFormat,
- const char *path,
- const char *fileName,
- const char *relPath,
- bool generateImageMap,
- int graphId
- ) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QCString baseName;
- if (m_inverse)
- {
- baseName=m_inclByDepFileName;
- }
- else
- {
- baseName=m_inclDepFileName;
- }
- QCString mapName=escapeCharsInString(m_startNode->m_label,FALSE);
- if (m_inverse) mapName+="dep";
-
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString absBaseName = d.absPath().utf8()+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- QCString absImgName = absBaseName+"."+imgExt;
-
- bool regenerate = FALSE;
- if (updateDotGraph(m_startNode,
- DotNode::Dependency,
- absBaseName,
- graphFormat,
- "", // lrRank
- FALSE, // renderParents
- m_inverse, // backArrows
- m_startNode->label()
- ) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
- if (graphFormat==GOF_BITMAP)
- {
- // run dot to create a bitmap image
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
- }
- else if (graphFormat==GOF_EPS)
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
- }
- }
- Doxygen::indexList->addImageFile(baseName+"."+imgExt);
-
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- out << "<para>" << endl;
- out << " <informalfigure>" << endl;
- out << " <mediaobject>" << endl;
- out << " <imageobject>" << endl;
- out << " <imagedata";
- out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- out << "</imagedata>" << endl;
- out << " </imageobject>" << endl;
- out << " </mediaobject>" << endl;
- out << " </informalfigure>" << endl;
- out << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && generateImageMap)
- {
- if (imgExt=="svg") // Scalable vector graphics
- {
- out << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- out << "<!-- SVG " << mapId << " -->" << endl;
- }
- out << "</div>" << endl;
- }
- else // bitmap graphics
- {
- out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." << imgExt << "\" border=\"0\" usemap=\"#" << mapName << "\" alt=\"\"/>";
- out << "</div>" << endl;
-
- QCString absMapName = absBaseName+".map";
- if (regenerate || !insertMapFile(out,absMapName,relPath,mapName))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- FALSE,QCString(),mapName);
- out << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- }
- else if (graphFormat==GOF_EPS) // encapsulated postscript
- {
- if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
- out << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-bool DotInclDepGraph::isTrivial() const
-{
- return m_startNode->m_children==0;
-}
-
-bool DotInclDepGraph::isTooBig() const
-{
- static int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0;
- return numNodes>=maxNodes;
-}
-
-void DotInclDepGraph::writeXML(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeXML(t,FALSE);
- }
-}
-
-void DotInclDepGraph::writeDocbook(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeDocbook(t,FALSE);
- }
-}
-
-//-------------------------------------------------------------
-
-void DotCallGraph::buildGraph(DotNode *n,const MemberDef *md,int distance)
-{
- MemberSDict *refs = m_inverse ? md->getReferencedByMembers() : md->getReferencesMembers();
- if (refs)
- {
- refs->sort();
- MemberSDict::Iterator mri(*refs);
- MemberDef *rmd;
- for (;(rmd=mri.current());++mri)
- {
- if (rmd->showInCallGraph())
- {
- QCString uniqueId;
- uniqueId=rmd->getReference()+"$"+
- rmd->getOutputFileBase()+"#"+rmd->anchor();
- DotNode *bn = m_usedNodes->find(uniqueId);
- if (bn) // file is already a node in the graph
- {
- n->addChild(bn,0,0,0);
- bn->addParent(n);
- bn->setDistance(distance);
- }
- else
- {
- QCString name;
- if (Config_getBool(HIDE_SCOPE_NAMES))
- {
- name = rmd->getOuterScope()==m_scope ?
- rmd->name() : rmd->qualifiedName();
- }
- else
- {
- name = rmd->qualifiedName();
- }
- QCString tooltip = rmd->briefDescriptionAsTooltip();
- bn = new DotNode(
- getNextNodeNumber(),
- linkToText(rmd->getLanguage(),name,FALSE),
- tooltip,
- uniqueId,
- 0 //distance
- );
- n->addChild(bn,0,0,0);
- bn->addParent(n);
- bn->setDistance(distance);
- m_usedNodes->insert(uniqueId,bn);
-
- buildGraph(bn,rmd,distance+1);
- }
- }
- }
- }
-}
-
-void DotCallGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes)
-{
- while (queue.count()>0 && maxNodes>0)
- {
- static int maxDistance = Config_getInt(MAX_DOT_GRAPH_DEPTH);
- DotNode *n = queue.take(0);
- if (!n->isVisible() && n->distance()<=maxDistance) // not yet processed
- {
- n->markAsVisible();
- maxNodes--;
- // add direct children
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- queue.append(dn);
- }
- }
- }
- }
-}
-
-void DotCallGraph::determineTruncatedNodes(QList<DotNode> &queue)
-{
- while (queue.count()>0)
- {
- DotNode *n = queue.take(0);
- if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
- {
- bool truncated = FALSE;
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- if (!dn->isVisible())
- truncated = TRUE;
- else
- queue.append(dn);
- }
- }
- n->markAsTruncated(truncated);
- }
- }
-}
-
-DotCallGraph::DotCallGraph(const MemberDef *md,bool inverse)
-{
- m_inverse = inverse;
- m_diskName = md->getOutputFileBase()+"_"+md->anchor();
- m_scope = md->getOuterScope();
- QCString uniqueId;
- uniqueId = md->getReference()+"$"+
- md->getOutputFileBase()+"#"+md->anchor();
- QCString name;
- if (Config_getBool(HIDE_SCOPE_NAMES))
- {
- name = md->name();
- }
- else
- {
- name = md->qualifiedName();
- }
- QCString tooltip = md->briefDescriptionAsTooltip();
- m_startNode = new DotNode(getNextNodeNumber(),
- linkToText(md->getLanguage(),name,FALSE),
- tooltip,
- uniqueId.data(),
- TRUE // root node
- );
- m_startNode->setDistance(0);
- m_usedNodes = new QDict<DotNode>(1009);
- m_usedNodes->insert(uniqueId,m_startNode);
- buildGraph(m_startNode,md,1);
-
- static int nodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int maxNodes = nodes;
- //int directChildNodes = 1;
- //if (m_startNode->m_children!=0)
- // directChildNodes+=m_startNode->m_children->count();
- //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
- QList<DotNode> openNodeQueue;
- openNodeQueue.append(m_startNode);
- determineVisibleNodes(openNodeQueue,maxNodes);
- openNodeQueue.clear();
- openNodeQueue.append(m_startNode);
- determineTruncatedNodes(openNodeQueue);
-}
-
-DotCallGraph::~DotCallGraph()
-{
- deleteNodes(m_startNode);
- delete m_usedNodes;
-}
-
-QCString DotCallGraph::writeGraph(FTextStream &out, GraphOutputFormat graphFormat,
- EmbeddedOutputFormat textFormat,
- const char *path,const char *fileName,
- const char *relPath,bool generateImageMap,int
- graphId) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QCString baseName = m_diskName + (m_inverse ? "_icgraph" : "_cgraph");
- QCString mapName = baseName;
-
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString absBaseName = d.absPath().utf8()+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- QCString absImgName = absBaseName+"."+imgExt;
-
- bool regenerate = FALSE;
-
- if (updateDotGraph(m_startNode,
- DotNode::CallGraph,
- absBaseName,
- graphFormat,
- m_inverse ? "RL" : "LR", // lrRank
- FALSE, // renderParents
- m_inverse, // backArrows
- m_startNode->label()
- ) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
- if (graphFormat==GOF_BITMAP)
- {
- // run dot to create a bitmap image
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
-
- }
- else if (graphFormat==GOF_EPS)
- {
- // run dot to create a .eps image
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
-
- }
- }
- Doxygen::indexList->addImageFile(baseName+"."+imgExt);
-
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- out << "<para>" << endl;
- out << " <informalfigure>" << endl;
- out << " <mediaobject>" << endl;
- out << " <imageobject>" << endl;
- out << " <imagedata";
- out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- out << "</imagedata>" << endl;
- out << " </imageobject>" << endl;
- out << " </mediaobject>" << endl;
- out << " </informalfigure>" << endl;
- out << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && generateImageMap)
- {
- if (imgExt=="svg") // Scalable vector graphics
- {
- out << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- out << "<!-- SVG " << mapId << " -->" << endl;
- }
- out << "</div>" << endl;
- }
- else // bitmap graphics
- {
- out << "<div class=\"center\"><img src=\"" << relPath << baseName << "."
- << imgExt << "\" border=\"0\" usemap=\"#"
- << mapName << "\" alt=\"";
- out << "\"/>";
- out << "</div>" << endl;
-
- if (regenerate || !insertMapFile(out,absMapName,relPath,mapName))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- FALSE,QCString(),mapName);
- out << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- }
- else if (graphFormat==GOF_EPS) // encapsulated postscript
- {
- if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
- out << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-bool DotCallGraph::isTrivial() const
-{
- return m_startNode->m_children==0;
-}
-
-bool DotCallGraph::isTooBig() const
-{
- static int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0;
- return numNodes>=maxNodes;
-}
-
-//-------------------------------------------------------------
-static void writeDotDirDepGraph(FTextStream &t,const DirDef *dd,bool linkRelations);
-
-DotDirDeps::DotDirDeps(const DirDef *dir) : m_dir(dir)
-{
-}
-
-DotDirDeps::~DotDirDeps()
-{
-}
-
-QCString DotDirDeps::writeGraph(FTextStream &out,
- GraphOutputFormat graphFormat,
- EmbeddedOutputFormat textFormat,
- const char *path,
- const char *fileName,
- const char *relPath,
- bool generateImageMap,
- int graphId,
- bool linkRelations) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QCString baseName=m_dir->getOutputFileBase()+"_dep";
- QCString mapName=escapeCharsInString(baseName,FALSE);
-
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString absBaseName = d.absPath().utf8()+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- QCString absImgName = absBaseName+"."+imgExt;
-
- // compute md5 checksum of the graph were are about to generate
- QGString theGraph;
- FTextStream md5stream(&theGraph);
- //m_dir->writeDepGraph(md5stream);
- writeDotDirDepGraph(md5stream,m_dir,linkRelations);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),33);
- bool regenerate=FALSE;
- if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
-
- QFile f(absDotName);
- if (!f.open(IO_WriteOnly))
- {
- err("Cannot create file %s.dot for writing!\n",baseName.data());
- }
- FTextStream t(&f);
- t << theGraph.data();
- f.close();
-
- if (graphFormat==GOF_BITMAP)
- {
- // run dot to create a bitmap image
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
- }
- else if (graphFormat==GOF_EPS)
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
- }
- }
- Doxygen::indexList->addImageFile(baseName+"."+imgExt);
-
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- out << "<para>" << endl;
- out << " <informalfigure>" << endl;
- out << " <mediaobject>" << endl;
- out << " <imageobject>" << endl;
- out << " <imagedata";
- out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- out << "</imagedata>" << endl;
- out << " </imageobject>" << endl;
- out << " </mediaobject>" << endl;
- out << " </informalfigure>" << endl;
- out << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && generateImageMap)
- {
- if (imgExt=="svg") // Scalable vector graphics
- {
- out << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- out << "<!-- SVG " << mapId << " -->" << endl;
- }
- out << "</div>" << endl;
- }
- else // bitmap graphics
- {
- out << "<div class=\"center\"><img src=\"" << relPath << baseName << "."
- << imgExt << "\" border=\"0\" usemap=\"#"
- << mapName << "\" alt=\"";
- out << convertToXML(m_dir->displayName());
- out << "\"/>";
- out << "</div>" << endl;
-
- if (regenerate || !insertMapFile(out,absMapName,relPath,mapName))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- TRUE,QCString(),mapName);
- out << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- }
- else if (graphFormat==GOF_EPS)
- {
- if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
- out << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-bool DotDirDeps::isTrivial() const
-{
- return m_dir->depGraphIsTrivial();
-}
-
-//-------------------------------------------------------------
+ friend void generateGraphLegend(const char* path);
+};
void generateGraphLegend(const char *path)
{
QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
+ GraphLegendDotGraph dg;
+ FTextStream ts;
+ dg.writeGraph(ts, GOF_BITMAP, EOF_Html, path, "", "", FALSE, 0);
- QGString theGraph;
- FTextStream md5stream(&theGraph);
- writeGraphHeader(md5stream,theTranslator->trLegendTitle());
- md5stream << " Node9 [shape=\"box\",label=\"Inherited\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",fillcolor=\"grey75\",style=\"filled\" fontcolor=\"black\"];\n";
- md5stream << " Node10 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node10 [shape=\"box\",label=\"PublicBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPublicBase" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node11 -> Node10 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node11 [shape=\"box\",label=\"Truncated\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"red\",URL=\"$classTruncated" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node13 -> Node9 [dir=\"back\",color=\"darkgreen\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node13 [shape=\"box\",label=\"ProtectedBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classProtectedBase" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node14 -> Node9 [dir=\"back\",color=\"firebrick4\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node14 [shape=\"box\",label=\"PrivateBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPrivateBase" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node15 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node15 [shape=\"box\",label=\"Undocumented\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"grey75\"];\n";
- md5stream << " Node16 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node16 [shape=\"box\",label=\"Templ< int >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node17 -> Node16 [dir=\"back\",color=\"orange\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"< int >\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node17 [shape=\"box\",label=\"Templ< T >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node18 -> Node9 [dir=\"back\",color=\"darkorchid3\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"m_usedClass\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node18 [shape=\"box\",label=\"Used\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classUsed" << Doxygen::htmlFileExtension << "\"];\n";
- writeGraphFooter(md5stream);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),33);
- QCString absBaseName = (QCString)path+"/graph_legend";
- QCString absDotName = absBaseName+".dot";
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString imgName = "graph_legend."+imgExt;
- QCString absImgName = absBaseName+"."+imgExt;
- if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
- !checkDeliverables(absImgName))
- {
- QFile dotFile(absDotName);
- if (!dotFile.open(IO_WriteOnly))
- {
- err("Could not open file %s for writing\n",dotFile.name().data());
- return;
- }
-
- FTextStream dotText(&dotFile);
- dotText << theGraph;
- dotFile.close();
-
- // run dot to generate the a bitmap image from the graph
-
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- DotManager::instance()->addRun(dotRun);
- }
- else
- {
- removeDotGraph(absDotName);
- }
- Doxygen::indexList->addImageFile(imgName);
-
- if (imgExt=="svg")
+ if (getDotImageExtension()=="svg")
{
DotManager::instance()->addSVGObject(
- absBaseName+Config_getString(HTML_FILE_EXTENSION),
+ dg.absBaseName()+Config_getString(HTML_FILE_EXTENSION),
"graph_legend",
- absImgName,QCString());
+ dg.absImgName(),QCString());
}
}
@@ -4238,19 +493,20 @@ void writeDotGraphFromFile(const char *inFile,const char *outDir,
}
QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
QCString imgName = (QCString)outFile+"."+imgExt;
QCString absImgName = d.absPath().utf8()+"/"+imgName;
QCString absOutFile = d.absPath().utf8()+"/"+outFile;
- DotRunner dotRun(inFile,d.absPath().data(),FALSE,absImgName);
+ DotRunner dotRun(inFile, QCString());
if (format==GOF_BITMAP)
- dotRun.addJob(imgFmt,absImgName);
+ {
+ dotRun.addJob(Config_getEnum(DOT_IMAGE_FORMAT),absImgName);
+ }
else // format==GOF_EPS
{
if (Config_getBool(USE_PDFLATEX))
{
- dotRun.addJob("pdf",absOutFile+".pdf",absOutFile);
+ dotRun.addJob("pdf",absOutFile+".pdf");
}
else
{
@@ -4264,13 +520,10 @@ void writeDotGraphFromFile(const char *inFile,const char *outDir,
return;
}
- if (format==GOF_BITMAP) checkDotResult(getDotImageExtension(),absImgName);
-
Doxygen::indexList->addImageFile(imgName);
}
-
/*! Writes user defined image map to the output.
* \param t text stream to write to
* \param inFile just the basename part of the filename
@@ -4294,11 +547,10 @@ void writeDotImageMapFromFile(FTextStream &t,
QCString mapName = baseName+".map";
QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
QCString imgName = baseName+"."+imgExt;
QCString absOutFile = d.absPath().utf8()+"/"+mapName;
- DotRunner dotRun(inFile,d.absPath().data(),FALSE);
+ DotRunner dotRun(inFile, QCString());
dotRun.addJob(MAP_CMD,absOutFile);
dotRun.preventCleanUp();
if (!dotRun.run())
@@ -4333,612 +585,3 @@ void writeDotImageMapFromFile(FTextStream &t,
}
d.remove(absOutFile);
}
-
-//-------------------------------------------------------------
-
-DotGroupCollaboration::DotGroupCollaboration(const GroupDef* gd)
-{
- QCString tmp_url = gd->getReference()+"$"+gd->getOutputFileBase();
- m_usedNodes = new QDict<DotNode>(1009);
- QCString tooltip = gd->briefDescriptionAsTooltip();
- m_rootNode = new DotNode(getNextNodeNumber(), gd->groupTitle(), tooltip, tmp_url, TRUE );
- m_rootNode->markAsVisible();
- m_usedNodes->insert(gd->name(), m_rootNode );
- m_edges.setAutoDelete(TRUE);
-
- m_diskName = gd->getOutputFileBase();
-
- buildGraph( gd );
-}
-
-DotGroupCollaboration::~DotGroupCollaboration()
-{
- delete m_usedNodes;
-}
-
-void DotGroupCollaboration::buildGraph(const GroupDef* gd)
-{
- QCString tmp_url;
- //===========================
- // hierarchy.
-
- // Write parents
- const GroupList *groups = gd->partOfGroups();
- if ( groups )
- {
- GroupListIterator gli(*groups);
- const GroupDef *d;
- for (gli.toFirst();(d=gli.current());++gli)
- {
- DotNode* nnode = m_usedNodes->find(d->name());
- if ( !nnode )
- { // add node
- tmp_url = d->getReference()+"$"+d->getOutputFileBase();
- QCString tooltip = d->briefDescriptionAsTooltip();
- nnode = new DotNode(getNextNodeNumber(), d->groupTitle(), tooltip, tmp_url );
- nnode->markAsVisible();
- m_usedNodes->insert(d->name(), nnode );
- }
- tmp_url = "";
- addEdge( nnode, m_rootNode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
- }
- }
-
- // Add subgroups
- if ( gd->getSubGroups() && gd->getSubGroups()->count() )
- {
- QListIterator<GroupDef> defli(*gd->getSubGroups());
- const GroupDef *def;
- for (;(def=defli.current());++defli)
- {
- DotNode* nnode = m_usedNodes->find(def->name());
- if ( !nnode )
- { // add node
- tmp_url = def->getReference()+"$"+def->getOutputFileBase();
- QCString tooltip = def->briefDescriptionAsTooltip();
- nnode = new DotNode(getNextNodeNumber(), def->groupTitle(), tooltip, tmp_url );
- nnode->markAsVisible();
- m_usedNodes->insert(def->name(), nnode );
- }
- tmp_url = "";
- addEdge( m_rootNode, nnode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
- }
- }
-
- //=======================
- // Write collaboration
-
- // Add members
- addMemberList( gd->getMemberList(MemberListType_allMembersList) );
-
- // Add classes
- if ( gd->getClasses() && gd->getClasses()->count() )
- {
- ClassSDict::Iterator defli(*gd->getClasses());
- ClassDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- if (!def->anchor().isEmpty())
- {
- tmp_url+="#"+def->anchor();
- }
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tclass );
- }
- }
-
- // Add namespaces
- if ( gd->getNamespaces() && gd->getNamespaces()->count() )
- {
- NamespaceSDict::Iterator defli(*gd->getNamespaces());
- NamespaceDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tnamespace );
- }
- }
-
- // Add files
- if ( gd->getFiles() && gd->getFiles()->count() )
- {
- QListIterator<FileDef> defli(*gd->getFiles());
- const FileDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tfile );
- }
- }
-
- // Add pages
- if ( gd->getPages() && gd->getPages()->count() )
- {
- PageSDict::Iterator defli(*gd->getPages());
- PageDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tpages );
- }
- }
-
- // Add directories
- if ( gd->getDirs() && gd->getDirs()->count() )
- {
- QListIterator<DirDef> defli(*gd->getDirs());
- const DirDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tdir );
- }
- }
-}
-
-void DotGroupCollaboration::addMemberList( MemberList* ml )
-{
- if ( !( ml && ml->count()) ) return;
- MemberListIterator defli(*ml);
- MemberDef *def;
- for (;(def=defli.current());++defli)
- {
- QCString tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension
- +"#"+def->anchor();
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tmember );
- }
-}
-
-DotGroupCollaboration::Edge* DotGroupCollaboration::addEdge(
- DotNode* _pNStart, DotNode* _pNEnd, EdgeType _eType,
- const QCString& _label, const QCString& _url )
-{
- // search a existing link.
- QListIterator<Edge> lli(m_edges);
- Edge* newEdge = 0;
- for ( lli.toFirst(); (newEdge=lli.current()); ++lli)
- {
- if ( newEdge->pNStart==_pNStart &&
- newEdge->pNEnd==_pNEnd &&
- newEdge->eType==_eType
- )
- { // edge already found
- break;
- }
- }
- if ( newEdge==0 ) // new link
- {
- newEdge = new Edge(_pNStart,_pNEnd,_eType);
- m_edges.append( newEdge );
- }
-
- if (!_label.isEmpty())
- {
- newEdge->links.append(new Link(_label,_url));
- }
-
- return newEdge;
-}
-
-void DotGroupCollaboration::addCollaborationMember(
- const Definition* def, QCString& url, EdgeType eType )
-{
- // Create group nodes
- if ( !def->partOfGroups() )
- return;
- GroupListIterator gli(*def->partOfGroups());
- GroupDef *d;
- QCString tmp_str;
- for (;(d=gli.current());++gli)
- {
- DotNode* nnode = m_usedNodes->find(d->name());
- if ( nnode != m_rootNode )
- {
- if ( nnode==0 )
- { // add node
- tmp_str = d->getReference()+"$"+d->getOutputFileBase();
- QCString tooltip = d->briefDescriptionAsTooltip();
- nnode = new DotNode(getNextNodeNumber(), d->groupTitle(), tooltip, tmp_str );
- nnode->markAsVisible();
- m_usedNodes->insert(d->name(), nnode );
- }
- tmp_str = def->qualifiedName();
- addEdge( m_rootNode, nnode, eType, tmp_str, url );
- }
- }
-}
-
-
-QCString DotGroupCollaboration::writeGraph( FTextStream &t,
- GraphOutputFormat graphFormat, EmbeddedOutputFormat textFormat,
- const char *path, const char *fileName, const char *relPath,
- bool writeImageMap,int graphId) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QGString theGraph;
- FTextStream md5stream(&theGraph);
- writeGraphHeader(md5stream,m_rootNode->label());
-
- // clean write flags
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *pn;
- for (dni.toFirst();(pn=dni.current());++dni)
- {
- pn->clearWriteFlag();
- }
-
- // write other nodes.
- for (dni.toFirst();(pn=dni.current());++dni)
- {
- pn->write(md5stream,DotNode::Inheritance,graphFormat,TRUE,FALSE,FALSE);
- }
-
- // write edges
- QListIterator<Edge> eli(m_edges);
- Edge* edge;
- for (eli.toFirst();(edge=eli.current());++eli)
- {
- edge->write( md5stream );
- }
-
- writeGraphFooter(md5stream);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),33);
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString baseName = m_diskName;
- QCString imgName = baseName+"."+imgExt;
- QCString absPath = d.absPath().data();
- QCString absBaseName = absPath+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absImgName = absBaseName+"."+imgExt;
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- bool regenerate=FALSE;
- if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP /*&& generateImageMap*/ ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
-
- QFile dotfile(absDotName);
- if (dotfile.open(IO_WriteOnly))
- {
- FTextStream tdot(&dotfile);
- tdot << theGraph;
- dotfile.close();
- }
-
- if (graphFormat==GOF_BITMAP) // run dot to create a bitmap image
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- dotRun->addJob(imgFmt,absImgName);
- if (writeImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
-
- }
- else if (graphFormat==GOF_EPS)
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
- }
-
- }
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- t << "<para>" << endl;
- t << " <informalfigure>" << endl;
- t << " <mediaobject>" << endl;
- t << " <imageobject>" << endl;
- t << " <imagedata";
- t << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- t << "</imagedata>" << endl;
- t << " </imageobject>" << endl;
- t << " </mediaobject>" << endl;
- t << " </informalfigure>" << endl;
- t << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && writeImageMap)
- {
- QCString mapLabel = escapeCharsInString(baseName,FALSE);
- t << "<center><table><tr><td>";
-
- if (imgExt=="svg")
- {
- t << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(t,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- t << "<!-- SVG " << mapId << " -->" << endl;
- }
- t << "</div>" << endl;
- }
- else
- {
- t << "<img src=\"" << relPath << imgName
- << "\" border=\"0\" alt=\"\" usemap=\"#"
- << mapLabel << "\"/>" << endl;
- if (regenerate || !insertMapFile(t,absMapName,relPath,mapLabel))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- FALSE,QCString(),mapLabel);
- t << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- t << "</td></tr></table></center>" << endl;
- }
- else if (graphFormat==GOF_EPS)
- {
- if (regenerate || !writeVecGfxFigure(t,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
- t << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-void DotGroupCollaboration::Edge::write( FTextStream &t ) const
-{
- const char* linkTypeColor[] = {
- "darkorchid3"
- ,"orange"
- ,"blueviolet"
- ,"darkgreen"
- ,"firebrick4"
- ,"grey75"
- ,"midnightblue"
- };
- QCString arrowStyle = "dir=\"none\", style=\"dashed\"";
- t << " Node" << pNStart->number();
- t << "->";
- t << "Node" << pNEnd->number();
-
- t << " [shape=plaintext";
- if (links.count()>0) // there are links
- {
- t << ", ";
- // HTML-like edge labels crash on my Mac with Graphviz 2.0! and
- // are not supported by older version of dot.
- //
- //t << label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">";
- //QListIterator<Link> lli(links);
- //Link *link;
- //for( lli.toFirst(); (link=lli.current()); ++lli)
- //{
- // t << "<TR><TD";
- // if ( !link->url.isEmpty() )
- // t << " HREF=\"" << link->url << "\"";
- // t << ">" << link->label << "</TD></TR>";
- //}
- //t << "</TABLE>>";
-
- t << "label=\"";
- QListIterator<Link> lli(links);
- Link *link;
- bool first=TRUE;
- int count=0;
- const int maxLabels = 10;
- for( lli.toFirst(); (link=lli.current()) && count<maxLabels; ++lli,++count)
- {
- if (first) first=FALSE; else t << "\\n";
- t << convertLabel(link->label);
- }
- if (count==maxLabels) t << "\\n...";
- t << "\"";
-
- }
- switch( eType )
- {
- case thierarchy:
- arrowStyle = "dir=\"back\", style=\"solid\"";
- break;
- default:
- t << ", color=\"" << linkTypeColor[(int)eType] << "\"";
- break;
- }
- t << ", " << arrowStyle;
- t << "];" << endl;
-}
-
-bool DotGroupCollaboration::isTrivial() const
-{
- return m_usedNodes->count() <= 1;
-}
-
-void DotGroupCollaboration::writeGraphHeader(FTextStream &t,
- const QCString &title) const
-{
- t << "digraph ";
- if (title.isEmpty())
- {
- t << "\"Dot Graph\"";
- }
- else
- {
- t << "\"" << convertLabel(title) << "\"";
- }
- t << endl;
- t << "{" << endl;
- if (Config_getBool(DOT_TRANSPARENT))
- {
- t << " bgcolor=\"transparent\";" << endl;
- }
- t << " edge [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\","
- "labelfontname=\"" << FONTNAME << "\",labelfontsize=\"" << FONTSIZE << "\"];\n";
- t << " node [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\",shape=box];\n";
- t << " rankdir=LR;\n";
-}
-
-void writeDotDirDepGraph(FTextStream &t,const DirDef *dd,bool linkRelations)
-{
- t << "digraph \"" << dd->displayName() << "\" {\n";
- if (Config_getBool(DOT_TRANSPARENT))
- {
- t << " bgcolor=transparent;\n";
- }
- t << " compound=true\n";
- t << " node [ fontsize=\"" << FONTSIZE << "\", fontname=\"" << FONTNAME << "\"];\n";
- t << " edge [ labelfontsize=\"" << FONTSIZE << "\", labelfontname=\"" << FONTNAME << "\"];\n";
-
- QDict<DirDef> dirsInGraph(257);
-
- dirsInGraph.insert(dd->getOutputFileBase(),dd);
- if (dd->parent())
- {
- t << " subgraph cluster" << dd->parent()->getOutputFileBase() << " {\n";
- t << " graph [ bgcolor=\"#ddddee\", pencolor=\"black\", label=\""
- << dd->parent()->shortName()
- << "\" fontname=\"" << FONTNAME << "\", fontsize=\"" << FONTSIZE << "\", URL=\"";
- t << dd->parent()->getOutputFileBase() << Doxygen::htmlFileExtension;
- t << "\"]\n";
- }
- if (dd->isCluster())
- {
- t << " subgraph cluster" << dd->getOutputFileBase() << " {\n";
- t << " graph [ bgcolor=\"#eeeeff\", pencolor=\"black\", label=\"\""
- << " URL=\"" << dd->getOutputFileBase() << Doxygen::htmlFileExtension
- << "\"];\n";
- t << " " << dd->getOutputFileBase() << " [shape=plaintext label=\""
- << dd->shortName() << "\"];\n";
-
- // add nodes for sub directories
- QListIterator<DirDef> sdi(dd->subDirs());
- const DirDef *sdir;
- for (sdi.toFirst();(sdir=sdi.current());++sdi)
- {
- t << " " << sdir->getOutputFileBase() << " [shape=box label=\""
- << sdir->shortName() << "\"";
- if (sdir->isCluster())
- {
- t << " color=\"red\"";
- }
- else
- {
- t << " color=\"black\"";
- }
- t << " fillcolor=\"white\" style=\"filled\"";
- t << " URL=\"" << sdir->getOutputFileBase()
- << Doxygen::htmlFileExtension << "\"";
- t << "];\n";
- dirsInGraph.insert(sdir->getOutputFileBase(),sdir);
- }
- t << " }\n";
- }
- else
- {
- t << " " << dd->getOutputFileBase() << " [shape=box, label=\""
- << dd->shortName() << "\", style=\"filled\", fillcolor=\"#eeeeff\","
- << " pencolor=\"black\", URL=\"" << dd->getOutputFileBase()
- << Doxygen::htmlFileExtension << "\"];\n";
- }
- if (dd->parent())
- {
- t << " }\n";
- }
-
- // add nodes for other used directories
- QDictIterator<UsedDir> udi(*dd->usedDirs());
- UsedDir *udir;
- //printf("*** For dir %s\n",shortName().data());
- for (udi.toFirst();(udir=udi.current());++udi)
- // for each used dir (=directly used or a parent of a directly used dir)
- {
- const DirDef *usedDir=udir->dir();
- const DirDef *dir=dd;
- while (dir)
- {
- //printf("*** check relation %s->%s same_parent=%d !%s->isParentOf(%s)=%d\n",
- // dir->shortName().data(),usedDir->shortName().data(),
- // dir->parent()==usedDir->parent(),
- // usedDir->shortName().data(),
- // shortName().data(),
- // !usedDir->isParentOf(this)
- // );
- if (dir!=usedDir && dir->parent()==usedDir->parent() &&
- !usedDir->isParentOf(dd))
- // include if both have the same parent (or no parent)
- {
- t << " " << usedDir->getOutputFileBase() << " [shape=box label=\""
- << usedDir->shortName() << "\"";
- if (usedDir->isCluster())
- {
- if (!Config_getBool(DOT_TRANSPARENT))
- {
- t << " fillcolor=\"white\" style=\"filled\"";
- }
- t << " color=\"red\"";
- }
- t << " URL=\"" << usedDir->getOutputFileBase()
- << Doxygen::htmlFileExtension << "\"];\n";
- dirsInGraph.insert(usedDir->getOutputFileBase(),usedDir);
- break;
- }
- dir=dir->parent();
- }
- }
-
- // add relations between all selected directories
- const DirDef *dir;
- QDictIterator<DirDef> di(dirsInGraph);
- for (di.toFirst();(dir=di.current());++di) // foreach dir in the graph
- {
- QDictIterator<UsedDir> udi(*dir->usedDirs());
- UsedDir *udir;
- for (udi.toFirst();(udir=udi.current());++udi) // foreach used dir
- {
- const DirDef *usedDir=udir->dir();
- if ((dir!=dd || !udir->inherited()) && // only show direct dependendies for this dir
- (usedDir!=dd || !udir->inherited()) && // only show direct dependendies for this dir
- !usedDir->isParentOf(dir) && // don't point to own parent
- dirsInGraph.find(usedDir->getOutputFileBase())) // only point to nodes that are in the graph
- {
- QCString relationName;
- relationName.sprintf("dir_%06d_%06d",dir->dirCount(),usedDir->dirCount());
- if (Doxygen::dirRelations.find(relationName)==0)
- {
- // new relation
- Doxygen::dirRelations.append(relationName,
- new DirRelation(relationName,dir,udir));
- }
- int nrefs = udir->filePairs().count();
- t << " " << dir->getOutputFileBase() << "->"
- << usedDir->getOutputFileBase();
- t << " [headlabel=\"" << nrefs << "\", labeldistance=1.5";
- if (linkRelations)
- {
- t << " headhref=\"" << relationName << Doxygen::htmlFileExtension << "\"";
- }
- t << "];\n";
- }
- }
- }
-
- t << "}\n";
-}
diff --git a/src/dot.h b/src/dot.h
index d63dd05..124a32b 100644
--- a/src/dot.h
+++ b/src/dot.h
@@ -1,13 +1,10 @@
/******************************************************************************
*
- *
- *
- *
- * Copyright (C) 1997-2015 by Dimitri van Heesch.
+ * Copyright (C) 1997-2019 by Dimitri van Heesch.
*
* Permission to use, copy, modify, and distribute this software and its
- * documentation under the terms of the GNU General Public License is hereby
- * granted. No representations are made about the suitability of this software
+ * documentation under the terms of the GNU General Public License is hereby
+ * granted. No representations are made about the suitability of this software
* for any purpose. It is provided "as is" without express or implied warranty.
* See the GNU General Public License for more details.
*
@@ -16,8 +13,8 @@
*
*/
-#ifndef _DOT_H
-#define _DOT_H
+#ifndef DOT_H
+#define DOT_H
#include <qlist.h>
#include <qdict.h>
@@ -26,473 +23,24 @@
#include <qqueue.h>
#include <qthread.h>
#include "sortdict.h"
-#include "classdef.h"
+#include "qgstring.h"
+#include "qdir.h"
+#include "qcstring.h"
+#include "dotgraph.h"
+#include "dotfilepatcher.h"
+#include "dotrunner.h"
-class FileDef;
class FTextStream;
-class DotNodeList;
-class ClassSDict;
-class MemberDef;
-class Definition;
-class DirDef;
-class GroupDef;
-class DotGroupCollaboration;
+class DotRunner;
class DotRunnerQueue;
-
-enum GraphOutputFormat { GOF_BITMAP, GOF_EPS };
-enum EmbeddedOutputFormat { EOF_Html, EOF_LaTeX, EOF_Rtf, EOF_DocBook };
-
-// the graphicx LaTeX has a limitation of maximum size of 16384
-// To be on the save side we take it a little bit smaller i.e. 150 inch * 72 dpi
-// It is anyway hard to view these size of images
-#define MAX_LATEX_GRAPH_INCH 150
-#define MAX_LATEX_GRAPH_SIZE (MAX_LATEX_GRAPH_INCH * 72)
-
-/** Attributes of an edge of a dot graph */
-struct EdgeInfo
-{
- enum Colors { Blue=0, Green=1, Red=2, Purple=3, Grey=4, Orange=5, Orange2=6 };
- enum Styles { Solid=0, Dashed=1 };
- EdgeInfo() : m_color(0), m_style(0), m_labColor(0) {}
- ~EdgeInfo() {}
- int m_color;
- int m_style;
- QCString m_label;
- QCString m_url;
- int m_labColor;
-};
-
-/** A node in a dot graph */
-class DotNode
-{
- public:
- enum GraphType { Dependency, Inheritance, Collaboration, Hierarchy, CallGraph };
- enum TruncState { Unknown, Truncated, Untruncated };
- DotNode(int n,const char *lab,const char *tip,const char *url,
- bool rootNode=FALSE,const ClassDef *cd=0);
- ~DotNode();
- void addChild(DotNode *n,
- int edgeColor=EdgeInfo::Purple,
- int edgeStyle=EdgeInfo::Solid,
- const char *edgeLab=0,
- const char *edgeURL=0,
- int edgeLabCol=-1
- );
- void addParent(DotNode *n);
- void deleteNode(DotNodeList &deletedList,SDict<DotNode> *skipNodes=0);
- void removeChild(DotNode *n);
- void removeParent(DotNode *n);
- int findParent( DotNode *n );
- void write(FTextStream &t,GraphType gt,GraphOutputFormat f,
- bool topDown,bool toChildren,bool backArrows);
- int m_subgraphId;
- void clearWriteFlag();
- void writeXML(FTextStream &t,bool isClassGraph);
- void writeDocbook(FTextStream &t,bool isClassGraph);
- void writeDEF(FTextStream &t);
- QCString label() const { return m_label; }
- int number() const { return m_number; }
- bool isVisible() const { return m_visible; }
- TruncState isTruncated() const { return m_truncated; }
- int distance() const { return m_distance; }
- void renumberNodes(int &number);
-
- private:
- void colorConnectedNodes(int curColor);
- void writeBox(FTextStream &t,GraphType gt,GraphOutputFormat f,
- bool hasNonReachableChildren);
- void writeArrow(FTextStream &t,GraphType gt,GraphOutputFormat f,DotNode *cn,
- EdgeInfo *ei,bool topDown, bool pointBack=TRUE);
- void setDistance(int distance);
- const DotNode *findDocNode() const; // only works for acyclic graphs!
- void markAsVisible(bool b=TRUE) { m_visible=b; }
- void markAsTruncated(bool b=TRUE) { m_truncated=b ? Truncated : Untruncated; }
- int m_number;
- QCString m_label; //!< label text
- QCString m_tooltip; //!< node's tooltip
- QCString m_url; //!< url of the node (format: remote$local)
- QList<DotNode> *m_parents; //!< list of parent nodes (incoming arrows)
- QList<DotNode> *m_children; //!< list of child nodes (outgoing arrows)
- QList<EdgeInfo> *m_edgeInfo; //!< edge info for each child
- bool m_deleted; //!< used to mark a node as deleted
- bool m_written; //!< used to mark a node as written
- bool m_hasDoc; //!< used to mark a node as documented
- bool m_isRoot; //!< indicates if this is a root node
- const ClassDef * m_classDef; //!< class representing this node (can be 0)
- bool m_visible; //!< is the node visible in the output
- TruncState m_truncated; //!< does the node have non-visible children/parents
- int m_distance; //!< shortest path to the root node
- bool m_renumbered;//!< indicates if the node has been renumbered (to prevent endless loops)
-
- friend class DotGfxHierarchyTable;
- friend class DotClassGraph;
- friend class DotInclDepGraph;
- friend class DotNodeList;
- friend class DotCallGraph;
- friend class DotGroupCollaboration;
- friend class DotInheritanceGraph;
-
- friend QCString computeMd5Signature(
- DotNode *root, GraphType gt,
- GraphOutputFormat f,
- const QCString &rank,
- bool renderParents,
- bool backArrows,
- const QCString &title,
- QCString &graphStr
- );
-};
-
-/** Class representing a list of DotNode objects. */
-class DotNodeList : public QList<DotNode>
-{
- public:
- DotNodeList() : QList<DotNode>() {}
- ~DotNodeList() {}
- private:
- int compareValues(const DotNode *n1,const DotNode *n2) const;
-};
-
-/** A dot graph */
-class DotGraph
-{
- public:
- DotGraph() : m_curNodeNumber(0) {}
- virtual ~DotGraph() {}
-
- protected:
- int getNextNodeNumber() { return ++m_curNodeNumber; }
-
- private:
- DotGraph(const DotGraph &);
- DotGraph &operator=(const DotGraph &);
- int m_curNodeNumber;
-};
-
-/** Represents a graphical class hierarchy */
-class DotGfxHierarchyTable : public DotGraph
-{
- public:
- DotGfxHierarchyTable(const char *prefix="",ClassDef::CompoundType ct=ClassDef::Class);
- ~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,const ClassDef *cd,bool hide);
- void addClassList(const ClassSDict *cl);
-
- QCString m_prefix;
- ClassDef::CompoundType m_classType;
- QList<DotNode> *m_rootNodes;
- QDict<DotNode> *m_usedNodes;
- DotNodeList *m_rootSubgraphs;
-};
-
-/** Representation of a class inheritance or dependency graph */
-class DotClassGraph : public DotGraph
-{
- public:
- DotClassGraph(const ClassDef *cd,DotNode::GraphType t);
- ~DotClassGraph();
- bool isTrivial() const;
- bool isTooBig() const;
- QCString writeGraph(FTextStream &t,GraphOutputFormat gf,EmbeddedOutputFormat ef,
- const char *path, const char *fileName, const char *relPath,
- bool TBRank=TRUE,bool imageMap=TRUE,int graphId=-1) const;
-
- void writeXML(FTextStream &t);
- void writeDocbook(FTextStream &t);
- void writeDEF(FTextStream &t);
-
- private:
- void buildGraph(const ClassDef *cd,DotNode *n,bool base,int distance);
- bool determineVisibleNodes(DotNode *rootNode,int maxNodes,bool includeParents);
- void determineTruncatedNodes(QList<DotNode> &queue,bool includeParents);
- void addClass(const ClassDef *cd,DotNode *n,int prot,const char *label,
- const char *usedName,const char *templSpec,
- bool base,int distance);
-
- DotNode * m_startNode;
- QDict<DotNode> * m_usedNodes;
- DotNode::GraphType m_graphType;
- QCString m_collabFileName;
- QCString m_inheritFileName;
- bool m_lrRank;
-};
-
-/** Representation of an include dependency graph */
-class DotInclDepGraph : public DotGraph
-{
- public:
- DotInclDepGraph(const FileDef *fd,bool inverse);
- ~DotInclDepGraph();
- QCString writeGraph(FTextStream &t, GraphOutputFormat gf, EmbeddedOutputFormat ef,
- const char *path,const char *fileName,const char *relPath,
- bool writeImageMap=TRUE,int graphId=-1) const;
- bool isTrivial() const;
- bool isTooBig() const;
- QCString diskName() const;
- void writeXML(FTextStream &t);
- void writeDocbook(FTextStream &t);
-
- private:
- void buildGraph(DotNode *n,const FileDef *fd,int distance);
- void determineVisibleNodes(QList<DotNode> &queue,int &maxNodes);
- void determineTruncatedNodes(QList<DotNode> &queue);
-
- DotNode *m_startNode;
- QDict<DotNode> *m_usedNodes;
- QCString m_inclDepFileName;
- QCString m_inclByDepFileName;
- bool m_inverse;
-};
-
-/** Representation of an call graph */
-class DotCallGraph : public DotGraph
-{
- public:
- DotCallGraph(const MemberDef *md,bool inverse);
- ~DotCallGraph();
- QCString writeGraph(FTextStream &t, GraphOutputFormat gf, EmbeddedOutputFormat ef,
- const char *path,const char *fileName,
- const char *relPath,bool writeImageMap=TRUE,
- int graphId=-1) const;
- void buildGraph(DotNode *n,const MemberDef *md,int distance);
- bool isTrivial() const;
- bool isTooBig() const;
- void determineVisibleNodes(QList<DotNode> &queue, int &maxNodes);
- void determineTruncatedNodes(QList<DotNode> &queue);
-
- private:
- DotNode *m_startNode;
- QDict<DotNode> *m_usedNodes;
- bool m_inverse;
- QCString m_diskName;
- const Definition * m_scope;
-};
-
-/** Representation of an directory dependency graph */
-class DotDirDeps : public DotGraph
-{
- public:
- DotDirDeps(const DirDef *dir);
- ~DotDirDeps();
- bool isTrivial() const;
- QCString writeGraph(FTextStream &out,
- GraphOutputFormat gf,
- EmbeddedOutputFormat ef,
- const char *path,
- const char *fileName,
- const char *relPath,
- bool writeImageMap=TRUE,
- int graphId=-1,
- bool linkRelations=TRUE) const;
- private:
- const DirDef *m_dir;
-};
-
-/** Representation of a group collaboration graph */
-class DotGroupCollaboration : public DotGraph
-{
- public :
- enum EdgeType
- { tmember = 0,
- tclass,
- tnamespace,
- tfile,
- tpages,
- tdir,
- thierarchy
- };
-
- class Link
- {
- public:
- Link(const QCString lab,const QCString &u) : label(lab), url(u) {}
- QCString label;
- QCString url;
- };
-
- class Edge
- {
- public :
- Edge(DotNode *start,DotNode *end,EdgeType type)
- : pNStart(start), pNEnd(end), eType(type)
- { links.setAutoDelete(TRUE); }
-
- DotNode* pNStart;
- DotNode* pNEnd;
- EdgeType eType;
-
- QList<Link> links;
- void write( FTextStream &t ) const;
- };
-
- DotGroupCollaboration(const GroupDef* gd);
- ~DotGroupCollaboration();
- QCString writeGraph(FTextStream &t, GraphOutputFormat gf,EmbeddedOutputFormat ef,
- const char *path,const char *fileName,const char *relPath,
- bool writeImageMap=TRUE,int graphId=-1) const;
- void buildGraph(const GroupDef* gd);
- bool isTrivial() const;
-
- private :
- void addCollaborationMember(const Definition* def, QCString& url, EdgeType eType );
- void addMemberList( class MemberList* ml );
- void writeGraphHeader(FTextStream &t,const QCString &title) const;
- Edge* addEdge( DotNode* _pNStart, DotNode* _pNEnd, EdgeType _eType,
- const QCString& _label, const QCString& _url );
-
- DotNode *m_rootNode;
- QDict<DotNode> *m_usedNodes;
- QCString m_diskName;
- QList<Edge> m_edges;
-};
-
-/** Minimal constant string class that is thread safe, once initialized. */
-class DotConstString
-{
- public:
- DotConstString() { m_str=0; m_pdfstr=0;}
- ~DotConstString() { delete[] m_str; delete[] m_pdfstr;}
- DotConstString(const QCString &s, const QCString &p = NULL) : m_str(0), m_pdfstr(0) { set(s); setpdf(p);}
- DotConstString(const DotConstString &s) : m_str(0), m_pdfstr(0) { set(s.data()); }
- const char *data() const { return m_str; }
- const char *pdfData() const { return m_pdfstr; }
- bool isEmpty() const { return m_str==0 || m_str[0]=='\0'; }
- void set(const QCString &s)
- {
- delete[] m_str;
- m_str=0;
- if (!s.isEmpty())
- {
- m_str=new char[s.length()+1];
- qstrcpy(m_str,s.data());
- }
- }
- void setpdf(const QCString &p)
- {
- delete[] m_pdfstr;
- m_pdfstr=0;
- if (!p.isEmpty())
- {
- m_pdfstr=new char[p.length()+1];
- qstrcpy(m_pdfstr,p.data());
- }
- }
- private:
- DotConstString &operator=(const DotConstString &);
- char *m_str;
- char *m_pdfstr;
-};
-
-/** Helper class to run dot from doxygen.
- */
-class DotRunner
-{
- public:
- struct CleanupItem
- {
- DotConstString path;
- DotConstString file;
- };
-
- /** Creates a runner for a dot \a file. */
- DotRunner(const QCString &file,const QCString &fontPath,bool checkResult,
- const QCString &imageName = QCString());
-
- /** Adds an additional job to the run.
- * Performing multiple jobs one file can be faster.
- */
- void addJob(const char *format,const char *output, const char *base = NULL);
-
- void addPostProcessing(const char *cmd,const char *args);
-
- void preventCleanUp() { m_cleanUp = FALSE; }
-
- /** Runs dot for all jobs added. */
- bool run();
- const CleanupItem &cleanup() const { return m_cleanupItem; }
-
- private:
- DotConstString m_dotExe;
- bool m_multiTargets;
- QList<DotConstString> m_jobs;
- DotConstString m_postArgs;
- DotConstString m_postCmd;
- DotConstString m_file;
- DotConstString m_path;
- bool m_checkResult;
- DotConstString m_imageName;
- DotConstString m_imgExt;
- bool m_cleanUp;
- CleanupItem m_cleanupItem;
-};
-
-/** Helper class to insert a set of map file into an output file */
-class DotFilePatcher
-{
- public:
- struct Map
- {
- QCString mapFile;
- QCString relPath;
- bool urlOnly;
- QCString context;
- QCString label;
- bool zoomable;
- int graphId;
- };
- DotFilePatcher(const char *patchFile);
- int addMap(const QCString &mapFile,const QCString &relPath,
- bool urlOnly,const QCString &context,const QCString &label);
- int addFigure(const QCString &baseName,
- const QCString &figureName,bool heightCheck);
- int addSVGConversion(const QCString &relPath,bool urlOnly,
- const QCString &context,bool zoomable,int graphId);
- int addSVGObject(const QCString &baseName, const QCString &figureName,
- const QCString &relPath);
- bool run();
- QCString file() const;
-
- private:
- QList<Map> m_maps;
- QCString m_patchFile;
-};
-
-/** Queue of dot jobs to run. */
-class DotRunnerQueue
-{
- public:
- void enqueue(DotRunner *runner);
- DotRunner *dequeue();
- uint count() const;
- private:
- QWaitCondition m_bufferNotEmpty;
- QQueue<DotRunner> m_queue;
- mutable QMutex m_mutex;
-};
-
-/** Worker thread to execute a dot run */
-class DotWorkerThread : public QThread
-{
- public:
- DotWorkerThread(DotRunnerQueue *queue);
- void run();
- void cleanup();
- private:
- DotRunnerQueue *m_queue;
- QList<DotRunner::CleanupItem> m_cleanupItems;
-};
+class DotWorkerThread;
/** Singleton that manages dot relation actions */
class DotManager
{
public:
static DotManager *instance();
- void addRun(DotRunner *run);
+ DotRunner* createRunner(const QCString& absDotName, const QCString& md5Hash);
int addMap(const QCString &file,const QCString &mapFile,
const QCString &relPath,bool urlOnly,
const QCString &context,const QCString &label);
@@ -507,13 +55,15 @@ class DotManager
private:
DotManager();
virtual ~DotManager();
- QList<DotRunner> m_dotRuns;
+
+ QDict<DotRunner> m_runners;
SDict<DotFilePatcher> m_dotMaps;
static DotManager *m_theInstance;
DotRunnerQueue *m_queue;
QList<DotWorkerThread> m_workers;
};
+void initDot();
/** Generated a graphs legend page */
void generateGraphLegend(const char *path);
@@ -524,5 +74,10 @@ void writeDotImageMapFromFile(FTextStream &t,
const QCString& inFile, const QCString& outDir,
const QCString& relPath,const QCString& baseName,
const QCString& context,int graphId=-1);
+bool writeSVGFigureLink(FTextStream &out,const QCString &relPath,
+ const QCString &baseName,const QCString &absImgName);
+bool convertMapFile(FTextStream &t,const char *mapName,
+ const QCString relPath, bool urlOnly=FALSE,
+ const QCString &context=QCString());
#endif
diff --git a/src/dotcallgraph.cpp b/src/dotcallgraph.cpp
new file mode 100644
index 0000000..15d408a
--- /dev/null
+++ b/src/dotcallgraph.cpp
@@ -0,0 +1,217 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "dotcallgraph.h"
+
+#include "dotnode.h"
+#include "memberlist.h"
+#include "config.h"
+#include "util.h"
+
+#define HIDE_SCOPE_NAMES Config_getBool(HIDE_SCOPE_NAMES)
+#define DOT_GRAPH_MAX_NODES Config_getInt(DOT_GRAPH_MAX_NODES)
+#define MAX_DOT_GRAPH_DEPTH Config_getInt(MAX_DOT_GRAPH_DEPTH)
+
+void DotCallGraph::buildGraph(DotNode *n,const MemberDef *md,int distance)
+{
+ MemberSDict *refs = m_inverse ? md->getReferencedByMembers() : md->getReferencesMembers();
+ if (refs)
+ {
+ refs->sort();
+ MemberSDict::Iterator mri(*refs);
+ MemberDef *rmd;
+ for (;(rmd=mri.current());++mri)
+ {
+ if (rmd->showInCallGraph())
+ {
+ QCString uniqueId;
+ uniqueId=rmd->getReference()+"$"+
+ rmd->getOutputFileBase()+"#"+rmd->anchor();
+ DotNode *bn = m_usedNodes->find(uniqueId);
+ if (bn) // file is already a node in the graph
+ {
+ n->addChild(bn,0,0,0);
+ bn->addParent(n);
+ bn->setDistance(distance);
+ }
+ else
+ {
+ QCString name;
+ if (HIDE_SCOPE_NAMES)
+ {
+ name = rmd->getOuterScope()==m_scope ?
+ rmd->name() : rmd->qualifiedName();
+ }
+ else
+ {
+ name = rmd->qualifiedName();
+ }
+ QCString tooltip = rmd->briefDescriptionAsTooltip();
+ bn = new DotNode(
+ getNextNodeNumber(),
+ linkToText(rmd->getLanguage(),name,FALSE),
+ tooltip,
+ uniqueId,
+ 0 //distance
+ );
+ n->addChild(bn,0,0,0);
+ bn->addParent(n);
+ bn->setDistance(distance);
+ m_usedNodes->insert(uniqueId,bn);
+
+ buildGraph(bn,rmd,distance+1);
+ }
+ }
+ }
+ }
+}
+
+void DotCallGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes)
+{
+ while (queue.count()>0 && maxNodes>0)
+ {
+ DotNode *n = queue.take(0);
+ if (!n->isVisible() && n->distance()<=MAX_DOT_GRAPH_DEPTH) // not yet processed
+ {
+ n->markAsVisible();
+ maxNodes--;
+ // add direct children
+ if (n->children())
+ {
+ QListIterator<DotNode> li(*n->children());
+ DotNode *dn;
+ for (li.toFirst();(dn=li.current());++li)
+ {
+ queue.append(dn);
+ }
+ }
+ }
+ }
+}
+
+void DotCallGraph::determineTruncatedNodes(QList<DotNode> &queue)
+{
+ while (queue.count()>0)
+ {
+ DotNode *n = queue.take(0);
+ if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
+ {
+ bool truncated = FALSE;
+ if (n->children())
+ {
+ QListIterator<DotNode> li(*n->children());
+ const DotNode *dn;
+ for (li.toFirst();(dn=li.current());++li)
+ {
+ if (!dn->isVisible())
+ truncated = TRUE;
+ else
+ queue.append(dn);
+ }
+ }
+ n->markAsTruncated(truncated);
+ }
+ }
+}
+
+DotCallGraph::DotCallGraph(const MemberDef *md,bool inverse)
+{
+ m_inverse = inverse;
+ m_diskName = md->getOutputFileBase()+"_"+md->anchor();
+ m_scope = md->getOuterScope();
+ QCString uniqueId;
+ uniqueId = md->getReference()+"$"+
+ md->getOutputFileBase()+"#"+md->anchor();
+ QCString name;
+ if (HIDE_SCOPE_NAMES)
+ {
+ name = md->name();
+ }
+ else
+ {
+ name = md->qualifiedName();
+ }
+ QCString tooltip = md->briefDescriptionAsTooltip();
+ m_startNode = new DotNode(getNextNodeNumber(),
+ linkToText(md->getLanguage(),name,FALSE),
+ tooltip,
+ uniqueId.data(),
+ TRUE // root node
+ );
+ m_startNode->setDistance(0);
+ m_usedNodes = new QDict<DotNode>(1009);
+ m_usedNodes->insert(uniqueId,m_startNode);
+ buildGraph(m_startNode,md,1);
+
+ int maxNodes = DOT_GRAPH_MAX_NODES;
+ QList<DotNode> openNodeQueue;
+ openNodeQueue.append(m_startNode);
+ determineVisibleNodes(openNodeQueue,maxNodes);
+ openNodeQueue.clear();
+ openNodeQueue.append(m_startNode);
+ determineTruncatedNodes(openNodeQueue);
+}
+
+DotCallGraph::~DotCallGraph()
+{
+ DotNode::deleteNodes(m_startNode);
+ delete m_usedNodes;
+}
+
+QCString DotCallGraph::getBaseName() const
+{
+ return m_diskName + (m_inverse ? "_icgraph" : "_cgraph");
+}
+
+void DotCallGraph::computeTheGraph()
+{
+ computeGraph(
+ m_startNode,
+ CallGraph,
+ m_graphFormat,
+ m_inverse ? "RL" : "LR",
+ FALSE,
+ m_inverse,
+ m_startNode->label(),
+ m_theGraph);
+}
+
+QCString DotCallGraph::getMapLabel() const
+{
+ return m_baseName;
+}
+
+QCString DotCallGraph::writeGraph(
+ FTextStream &out,
+ GraphOutputFormat graphFormat,
+ EmbeddedOutputFormat textFormat,
+ const char *path,
+ const char *fileName,
+ const char *relPath,bool generateImageMap,
+ int graphId)
+{
+ return DotGraph::writeGraph(out, graphFormat, textFormat, path, fileName, relPath, generateImageMap, graphId);
+}
+
+bool DotCallGraph::isTrivial() const
+{
+ return m_startNode->children()==0;
+}
+
+bool DotCallGraph::isTooBig() const
+{
+ int numNodes = m_startNode->children() ? m_startNode->children()->count() : 0;
+ return numNodes>=DOT_GRAPH_MAX_NODES;
+}
diff --git a/src/dotcallgraph.h b/src/dotcallgraph.h
new file mode 100644
index 0000000..c96b9cf
--- /dev/null
+++ b/src/dotcallgraph.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#ifndef DOTCALLGRAPH_H
+#define DOTCALLGRAPH_H
+
+#include "dotgraph.h"
+#include "ftextstream.h"
+#include "memberdef.h"
+
+/** Representation of an call graph */
+class DotCallGraph : public DotGraph
+{
+ public:
+ DotCallGraph(const MemberDef *md,bool inverse);
+ ~DotCallGraph();
+ bool isTrivial() const;
+ bool isTooBig() const;
+ QCString writeGraph(FTextStream &t, GraphOutputFormat gf, EmbeddedOutputFormat ef,
+ const char *path,const char *fileName,
+ const char *relPath,bool writeImageMap=TRUE,
+ int graphId=-1);
+
+ protected:
+ virtual QCString getBaseName() const;
+ virtual QCString getMapLabel() const;
+ virtual void computeTheGraph();
+
+ private:
+ void buildGraph(DotNode *n,const MemberDef *md,int distance);
+ void determineVisibleNodes(QList<DotNode> &queue, int &maxNodes);
+ void determineTruncatedNodes(QList<DotNode> &queue);
+ DotNode *m_startNode;
+ QDict<DotNode> *m_usedNodes;
+ bool m_inverse;
+ QCString m_diskName;
+ const Definition * m_scope;
+};
+
+#endif
diff --git a/src/dotclassgraph.cpp b/src/dotclassgraph.cpp
new file mode 100644
index 0000000..308be4b
--- /dev/null
+++ b/src/dotclassgraph.cpp
@@ -0,0 +1,548 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "dotclassgraph.h"
+#include "dotnode.h"
+
+#include "config.h"
+#include "util.h"
+
+#define HIDE_SCOPE_NAMES Config_getBool(HIDE_SCOPE_NAMES)
+#define MAX_DOT_GRAPH_DEPTH Config_getInt(MAX_DOT_GRAPH_DEPTH)
+#define UML_LOOK Config_getBool(UML_LOOK)
+#define TEMPLATE_RELATIONS Config_getBool(TEMPLATE_RELATIONS)
+#define DOT_GRAPH_MAX_NODES Config_getInt(DOT_GRAPH_MAX_NODES)
+
+void DotClassGraph::addClass(const ClassDef *cd,DotNode *n,int prot,
+ const char *label,const char *usedName,const char *templSpec,bool base,int distance)
+{
+ if (Config_getBool(HIDE_UNDOC_CLASSES) && !cd->isLinkable()) return;
+
+ int edgeStyle = (label || prot==EdgeInfo::Orange || prot==EdgeInfo::Orange2) ? EdgeInfo::Dashed : EdgeInfo::Solid;
+ QCString className;
+ if (cd->isAnonymous())
+ {
+ className="anonymous:";
+ className+=label;
+ }
+ else if (usedName) // name is a typedef
+ {
+ className=usedName;
+ }
+ else if (templSpec) // name has a template part
+ {
+ className=insertTemplateSpecifierInScope(cd->name(),templSpec);
+ }
+ else // just a normal name
+ {
+ className=cd->displayName();
+ }
+ //printf("DotClassGraph::addClass(class=`%s',parent=%s,prot=%d,label=%s,dist=%d,usedName=%s,templSpec=%s,base=%d)\n",
+ // className.data(),n->label().data(),prot,label,distance,usedName,templSpec,base);
+ DotNode *bn = m_usedNodes->find(className);
+ if (bn) // class already inserted
+ {
+ if (base)
+ {
+ n->addChild(bn,prot,edgeStyle,label);
+ bn->addParent(n);
+ }
+ else
+ {
+ bn->addChild(n,prot,edgeStyle,label);
+ n->addParent(bn);
+ }
+ bn->setDistance(distance);
+ //printf(" add exiting node %s of %s\n",bn->label().data(),n->label().data());
+ }
+ else // new class
+ {
+ QCString displayName=className;
+ if (HIDE_SCOPE_NAMES) displayName=stripScope(displayName);
+ QCString tmp_url;
+ if (cd->isLinkable() && !cd->isHidden())
+ {
+ tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
+ if (!cd->anchor().isEmpty())
+ {
+ tmp_url+="#"+cd->anchor();
+ }
+ }
+ QCString tooltip = cd->briefDescriptionAsTooltip();
+ bn = new DotNode(getNextNodeNumber(),
+ displayName,
+ tooltip,
+ tmp_url.data(),
+ FALSE, // rootNode
+ cd
+ );
+ if (base)
+ {
+ n->addChild(bn,prot,edgeStyle,label);
+ bn->addParent(n);
+ }
+ else
+ {
+ bn->addChild(n,prot,edgeStyle,label);
+ n->addParent(bn);
+ }
+ bn->setDistance(distance);
+ m_usedNodes->insert(className,bn);
+ //printf(" add new child node `%s' to %s hidden=%d url=%s\n",
+ // className.data(),n->label().data(),cd->isHidden(),tmp_url.data());
+
+ buildGraph(cd,bn,base,distance+1);
+ }
+}
+
+void DotClassGraph::determineTruncatedNodes(QList<DotNode> &queue,bool includeParents)
+{
+ while (queue.count()>0)
+ {
+ DotNode *n = queue.take(0);
+ if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
+ {
+ bool truncated = FALSE;
+ if (n->children())
+ {
+ QListIterator<DotNode> li(*n->children());
+ const DotNode *dn;
+ for (li.toFirst();(dn=li.current());++li)
+ {
+ if (!dn->isVisible())
+ truncated = TRUE;
+ else
+ queue.append(dn);
+ }
+ }
+ if (n->parents() && includeParents)
+ {
+ QListIterator<DotNode> li(*n->parents());
+ const DotNode *dn;
+ for (li.toFirst();(dn=li.current());++li)
+ {
+ if (!dn->isVisible())
+ truncated = TRUE;
+ else
+ queue.append(dn);
+ }
+ }
+ n->markAsTruncated(truncated);
+ }
+ }
+}
+
+bool DotClassGraph::determineVisibleNodes(DotNode *rootNode,
+ int maxNodes,bool includeParents)
+{
+ QList<DotNode> childQueue;
+ QList<DotNode> parentQueue;
+ QArray<int> childTreeWidth;
+ QArray<int> parentTreeWidth;
+ childQueue.append(rootNode);
+ if (includeParents) parentQueue.append(rootNode);
+ bool firstNode=TRUE; // flag to force reprocessing rootNode in the parent loop
+ // despite being marked visible in the child loop
+ while ((childQueue.count()>0 || parentQueue.count()>0) && maxNodes>0)
+ {
+ if (childQueue.count()>0)
+ {
+ DotNode *n = childQueue.take(0);
+ int distance = n->distance();
+ if (!n->isVisible() && distance<=MAX_DOT_GRAPH_DEPTH) // not yet processed
+ {
+ if (distance>0)
+ {
+ int oldSize=(int)childTreeWidth.size();
+ if (distance>oldSize)
+ {
+ childTreeWidth.resize(QMAX(childTreeWidth.size(),(uint)distance));
+ int i; for (i=oldSize;i<distance;i++) childTreeWidth[i]=0;
+ }
+ childTreeWidth[distance-1]+=n->label().length();
+ }
+ n->markAsVisible();
+ maxNodes--;
+ // add direct children
+ if (n->children())
+ {
+ QListIterator<DotNode> li(*n->children());
+ const DotNode *dn;
+ for (li.toFirst();(dn=li.current());++li)
+ {
+ childQueue.append(dn);
+ }
+ }
+ }
+ }
+ if (includeParents && parentQueue.count()>0)
+ {
+ DotNode *n = parentQueue.take(0);
+ if ((!n->isVisible() || firstNode) && n->distance()<=MAX_DOT_GRAPH_DEPTH) // not yet processed
+ {
+ firstNode=FALSE;
+ int distance = n->distance();
+ if (distance>0)
+ {
+ int oldSize = (int)parentTreeWidth.size();
+ if (distance>oldSize)
+ {
+ parentTreeWidth.resize(QMAX(parentTreeWidth.size(),(uint)distance));
+ int i; for (i=oldSize;i<distance;i++) parentTreeWidth[i]=0;
+ }
+ parentTreeWidth[distance-1]+=n->label().length();
+ }
+ n->markAsVisible();
+ maxNodes--;
+ // add direct parents
+ if (n->parents())
+ {
+ QListIterator<DotNode> li(*n->parents());
+ const DotNode *dn;
+ for (li.toFirst();(dn=li.current());++li)
+ {
+ parentQueue.append(dn);
+ }
+ }
+ }
+ }
+ }
+ if (UML_LOOK) return FALSE; // UML graph are always top to bottom
+ int maxWidth=0;
+ int maxHeight=(int)QMAX(childTreeWidth.size(),parentTreeWidth.size());
+ uint i;
+ for (i=0;i<childTreeWidth.size();i++)
+ {
+ if (childTreeWidth.at(i)>maxWidth) maxWidth=childTreeWidth.at(i);
+ }
+ for (i=0;i<parentTreeWidth.size();i++)
+ {
+ if (parentTreeWidth.at(i)>maxWidth) maxWidth=parentTreeWidth.at(i);
+ }
+ //printf("max tree width=%d, max tree height=%d\n",maxWidth,maxHeight);
+ return maxWidth>80 && maxHeight<12; // used metric to decide to render the tree
+ // from left to right instead of top to bottom,
+ // with the idea to render very wide trees in
+ // left to right order.
+}
+
+void DotClassGraph::buildGraph(const ClassDef *cd,DotNode *n,bool base,int distance)
+{
+ //printf("DocClassGraph::buildGraph(%s,distance=%d,base=%d)\n",
+ // cd->name().data(),distance,base);
+ // ---- Add inheritance relations
+
+ if (m_graphType == Inheritance || m_graphType==Collaboration)
+ {
+ BaseClassList *bcl = base ? cd->baseClasses() : cd->subClasses();
+ if (bcl)
+ {
+ BaseClassListIterator bcli(*bcl);
+ BaseClassDef *bcd;
+ for ( ; (bcd=bcli.current()) ; ++bcli )
+ {
+ //printf("-------- inheritance relation %s->%s templ=`%s'\n",
+ // cd->name().data(),bcd->classDef->name().data(),bcd->templSpecifiers.data());
+ addClass(bcd->classDef,n,bcd->prot,0,bcd->usedName,
+ bcd->templSpecifiers,base,distance);
+ }
+ }
+ }
+ if (m_graphType == Collaboration)
+ {
+ // ---- Add usage relations
+
+ UsesClassDict *dict =
+ base ? cd->usedImplementationClasses() :
+ cd->usedByImplementationClasses()
+ ;
+ if (dict)
+ {
+ UsesClassDictIterator ucdi(*dict);
+ UsesClassDef *ucd;
+ for (;(ucd=ucdi.current());++ucdi)
+ {
+ QCString label;
+ QDictIterator<void> dvi(*ucd->accessors);
+ const char *s;
+ bool first=TRUE;
+ int count=0;
+ int maxLabels=10;
+ for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count)
+ {
+ if (first)
+ {
+ label=s;
+ first=FALSE;
+ }
+ else
+ {
+ label+=QCString("\n")+s;
+ }
+ }
+ if (count==maxLabels) label+="\n...";
+ //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data());
+ addClass(ucd->classDef,n,EdgeInfo::Purple,label,0,
+ ucd->templSpecifiers,base,distance);
+ }
+ }
+ }
+ if (TEMPLATE_RELATIONS && base)
+ {
+ ConstraintClassDict *dict = cd->templateTypeConstraints();
+ if (dict)
+ {
+ ConstraintClassDictIterator ccdi(*dict);
+ ConstraintClassDef *ccd;
+ for (;(ccd=ccdi.current());++ccdi)
+ {
+ QCString label;
+ QDictIterator<void> dvi(*ccd->accessors);
+ const char *s;
+ bool first=TRUE;
+ int count=0;
+ int maxLabels=10;
+ for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count)
+ {
+ if (first)
+ {
+ label=s;
+ first=FALSE;
+ }
+ else
+ {
+ label+=QCString("\n")+s;
+ }
+ }
+ if (count==maxLabels) label+="\n...";
+ //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data());
+ addClass(ccd->classDef,n,EdgeInfo::Orange2,label,0,
+ 0,TRUE,distance);
+ }
+ }
+ }
+
+ // ---- Add template instantiation relations
+
+ if (TEMPLATE_RELATIONS)
+ {
+ if (base) // template relations for base classes
+ {
+ const ClassDef *templMaster=cd->templateMaster();
+ if (templMaster)
+ {
+ QDictIterator<ClassDef> cli(*templMaster->getTemplateInstances());
+ const ClassDef *templInstance;
+ for (;(templInstance=cli.current());++cli)
+ {
+ if (templInstance==cd)
+ {
+ addClass(templMaster,n,EdgeInfo::Orange,cli.currentKey(),0,
+ 0,TRUE,distance);
+ }
+ }
+ }
+ }
+ else // template relations for super classes
+ {
+ const QDict<ClassDef> *templInstances = cd->getTemplateInstances();
+ if (templInstances)
+ {
+ QDictIterator<ClassDef> cli(*templInstances);
+ const ClassDef *templInstance;
+ for (;(templInstance=cli.current());++cli)
+ {
+ addClass(templInstance,n,EdgeInfo::Orange,cli.currentKey(),0,
+ 0,FALSE,distance);
+ }
+ }
+ }
+ }
+}
+
+DotClassGraph::DotClassGraph(const ClassDef *cd,GraphType t)
+{
+ //printf("--------------- DotClassGraph::DotClassGraph `%s'\n",cd->displayName().data());
+ m_graphType = t;
+ QCString tmp_url="";
+ if (cd->isLinkable() && !cd->isHidden())
+ {
+ tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
+ if (!cd->anchor().isEmpty())
+ {
+ tmp_url+="#"+cd->anchor();
+ }
+ }
+ QCString className = cd->displayName();
+ QCString tooltip = cd->briefDescriptionAsTooltip();
+ m_startNode = new DotNode(getNextNodeNumber(),
+ className,
+ tooltip,
+ tmp_url.data(),
+ TRUE, // is a root node
+ cd
+ );
+ m_startNode->setDistance(0);
+ m_usedNodes = new QDict<DotNode>(1009);
+ m_usedNodes->insert(className,m_startNode);
+
+ buildGraph(cd,m_startNode,TRUE,1);
+ if (t==Inheritance) buildGraph(cd,m_startNode,FALSE,1);
+
+ m_lrRank = determineVisibleNodes(m_startNode,DOT_GRAPH_MAX_NODES,t==Inheritance);
+ QList<DotNode> openNodeQueue;
+ openNodeQueue.append(m_startNode);
+ determineTruncatedNodes(openNodeQueue,t==Inheritance);
+
+ m_collabFileName = cd->collaborationGraphFileName();
+ m_inheritFileName = cd->inheritanceGraphFileName();
+}
+
+bool DotClassGraph::isTrivial() const
+{
+ if (m_graphType==Inheritance)
+ return m_startNode->children()==0 && m_startNode->parents()==0;
+ else
+ return !UML_LOOK && m_startNode->children()==0;
+}
+
+bool DotClassGraph::isTooBig() const
+{
+ int numNodes = 0;
+ numNodes+= m_startNode->children() ? m_startNode->children()->count() : 0;
+ if (m_graphType==Inheritance)
+ {
+ numNodes+= m_startNode->parents() ? m_startNode->parents()->count() : 0;
+ }
+ return numNodes>=DOT_GRAPH_MAX_NODES;
+}
+
+DotClassGraph::~DotClassGraph()
+{
+ DotNode::deleteNodes(m_startNode);
+ delete m_usedNodes;
+}
+
+QCString DotClassGraph::getBaseName() const
+{
+ switch (m_graphType)
+ {
+ case Collaboration:
+ return m_collabFileName;
+ break;
+ case Inheritance:
+ return m_inheritFileName;
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ return "";
+}
+
+void DotClassGraph::computeTheGraph()
+{
+ computeGraph(
+ m_startNode,
+ m_graphType,
+ m_graphFormat,
+ m_lrRank ? "LR" : "",
+ m_graphType == Inheritance,
+ TRUE,
+ m_startNode->label(),
+ m_theGraph
+ );
+}
+
+QCString DotClassGraph::getMapLabel() const
+{
+ QCString mapName;
+ switch (m_graphType)
+ {
+ case Collaboration:
+ mapName="coll_map";
+ break;
+ case Inheritance:
+ mapName="inherit_map";
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ return escapeCharsInString(m_startNode->label(),FALSE)+"_"+escapeCharsInString(mapName,FALSE);
+}
+
+QCString DotClassGraph::getImgAltText() const
+{
+ switch (m_graphType)
+ {
+ case Collaboration:
+ return "Collaboration graph";
+ break;
+ case Inheritance:
+ return "Inheritance graph";
+ break;
+ default:
+ ASSERT(0);
+ break;
+ }
+ return "";
+}
+
+QCString DotClassGraph::writeGraph(FTextStream &out,
+ GraphOutputFormat graphFormat,
+ EmbeddedOutputFormat textFormat,
+ const char *path,
+ const char *fileName,
+ const char *relPath,
+ bool /*isTBRank*/,
+ bool generateImageMap,
+ int graphId)
+{
+ return DotGraph::writeGraph(out, graphFormat, textFormat, path, fileName, relPath, generateImageMap, graphId);
+}
+
+//--------------------------------------------------------------------
+
+void DotClassGraph::writeXML(FTextStream &t)
+{
+ QDictIterator<DotNode> dni(*m_usedNodes);
+ DotNode *node;
+ for (;(node=dni.current());++dni)
+ {
+ node->writeXML(t,TRUE);
+ }
+}
+
+void DotClassGraph::writeDocbook(FTextStream &t)
+{
+ QDictIterator<DotNode> dni(*m_usedNodes);
+ DotNode *node;
+ for (;(node=dni.current());++dni)
+ {
+ node->writeDocbook(t,TRUE);
+ }
+}
+
+void DotClassGraph::writeDEF(FTextStream &t)
+{
+ QDictIterator<DotNode> dni(*m_usedNodes);
+ DotNode *node;
+ for (;(node=dni.current());++dni)
+ {
+ node->writeDEF(t);
+ }
+}
diff --git a/src/dotclassgraph.h b/src/dotclassgraph.h
new file mode 100644
index 0000000..b3b9291
--- /dev/null
+++ b/src/dotclassgraph.h
@@ -0,0 +1,62 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#ifndef DOTCLASSGRAPH_H
+#define DOTCLASSGRAPH_H
+
+#include "classdef.h"
+
+#include "dotgraph.h"
+
+/** Representation of a class inheritance or dependency graph */
+class DotClassGraph : public DotGraph
+{
+public:
+ DotClassGraph(const ClassDef *cd,GraphType t);
+ ~DotClassGraph();
+ bool isTrivial() const;
+ bool isTooBig() const;
+ QCString writeGraph(FTextStream &t,GraphOutputFormat gf,EmbeddedOutputFormat ef,
+ const char *path, const char *fileName, const char *relPath,
+ bool TBRank=TRUE,bool imageMap=TRUE,int graphId=-1);
+
+ void writeXML(FTextStream &t);
+ void writeDocbook(FTextStream &t);
+ void writeDEF(FTextStream &t);
+
+protected:
+ virtual QCString getBaseName() const;
+ virtual QCString getMapLabel() const;
+ virtual void computeTheGraph();
+ virtual QCString getImgAltText() const;
+
+private:
+ void buildGraph(const ClassDef *cd,DotNode *n,bool base,int distance);
+ bool determineVisibleNodes(DotNode *rootNode,int maxNodes,bool includeParents);
+ void determineTruncatedNodes(QList<DotNode> &queue,bool includeParents);
+ void addClass(const ClassDef *cd,DotNode *n,int prot,const char *label,
+ const char *usedName,const char *templSpec,
+ bool base,int distance);
+
+ DotNode * m_startNode;
+ QDict<DotNode> * m_usedNodes;
+ GraphType m_graphType;
+ QCString m_collabFileName;
+ QCString m_inheritFileName;
+ bool m_lrRank;
+};
+
+
+#endif
diff --git a/src/dotdirdeps.cpp b/src/dotdirdeps.cpp
new file mode 100644
index 0000000..85906d1
--- /dev/null
+++ b/src/dotdirdeps.cpp
@@ -0,0 +1,220 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "dotdirdeps.h"
+
+#include "ftextstream.h"
+#include "util.h"
+#include "doxygen.h"
+#include "config.h"
+
+void writeDotDirDepGraph(FTextStream &t,const DirDef *dd,bool linkRelations)
+{
+ t << "digraph \"" << dd->displayName() << "\" {\n";
+ if (Config_getBool(DOT_TRANSPARENT))
+ {
+ t << " bgcolor=transparent;\n";
+ }
+ t << " compound=true\n";
+ t << " node [ fontsize=\"" << DotGraph::DOT_FONTSIZE << "\", fontname=\"" << DotGraph::DOT_FONTNAME << "\"];\n";
+ t << " edge [ labelfontsize=\"" << DotGraph::DOT_FONTSIZE << "\", labelfontname=\"" << DotGraph::DOT_FONTNAME << "\"];\n";
+
+ QDict<DirDef> dirsInGraph(257);
+
+ dirsInGraph.insert(dd->getOutputFileBase(),dd);
+ if (dd->parent())
+ {
+ t << " subgraph cluster" << dd->parent()->getOutputFileBase() << " {\n";
+ t << " graph [ bgcolor=\"#ddddee\", pencolor=\"black\", label=\""
+ << dd->parent()->shortName()
+ << "\" fontname=\"" << DotGraph::DOT_FONTNAME << "\", fontsize=\"" << DotGraph::DOT_FONTSIZE << "\", URL=\"";
+ t << dd->parent()->getOutputFileBase() << Doxygen::htmlFileExtension;
+ t << "\"]\n";
+ }
+ if (dd->isCluster())
+ {
+ t << " subgraph cluster" << dd->getOutputFileBase() << " {\n";
+ t << " graph [ bgcolor=\"#eeeeff\", pencolor=\"black\", label=\"\""
+ << " URL=\"" << dd->getOutputFileBase() << Doxygen::htmlFileExtension
+ << "\"];\n";
+ t << " " << dd->getOutputFileBase() << " [shape=plaintext label=\""
+ << dd->shortName() << "\"];\n";
+
+ // add nodes for sub directories
+ QListIterator<DirDef> sdi(dd->subDirs());
+ const DirDef *sdir;
+ for (sdi.toFirst();(sdir=sdi.current());++sdi)
+ {
+ t << " " << sdir->getOutputFileBase() << " [shape=box label=\""
+ << sdir->shortName() << "\"";
+ if (sdir->isCluster())
+ {
+ t << " color=\"red\"";
+ }
+ else
+ {
+ t << " color=\"black\"";
+ }
+ t << " fillcolor=\"white\" style=\"filled\"";
+ t << " URL=\"" << sdir->getOutputFileBase()
+ << Doxygen::htmlFileExtension << "\"";
+ t << "];\n";
+ dirsInGraph.insert(sdir->getOutputFileBase(),sdir);
+ }
+ t << " }\n";
+ }
+ else
+ {
+ t << " " << dd->getOutputFileBase() << " [shape=box, label=\""
+ << dd->shortName() << "\", style=\"filled\", fillcolor=\"#eeeeff\","
+ << " pencolor=\"black\", URL=\"" << dd->getOutputFileBase()
+ << Doxygen::htmlFileExtension << "\"];\n";
+ }
+ if (dd->parent())
+ {
+ t << " }\n";
+ }
+
+ // add nodes for other used directories
+ QDictIterator<UsedDir> udi(*dd->usedDirs());
+ UsedDir *udir;
+ //printf("*** For dir %s\n",shortName().data());
+ for (udi.toFirst();(udir=udi.current());++udi)
+ // for each used dir (=directly used or a parent of a directly used dir)
+ {
+ const DirDef *usedDir=udir->dir();
+ const DirDef *dir=dd;
+ while (dir)
+ {
+ //printf("*** check relation %s->%s same_parent=%d !%s->isParentOf(%s)=%d\n",
+ // dir->shortName().data(),usedDir->shortName().data(),
+ // dir->parent()==usedDir->parent(),
+ // usedDir->shortName().data(),
+ // shortName().data(),
+ // !usedDir->isParentOf(this)
+ // );
+ if (dir!=usedDir && dir->parent()==usedDir->parent() &&
+ !usedDir->isParentOf(dd))
+ // include if both have the same parent (or no parent)
+ {
+ t << " " << usedDir->getOutputFileBase() << " [shape=box label=\""
+ << usedDir->shortName() << "\"";
+ if (usedDir->isCluster())
+ {
+ if (!Config_getBool(DOT_TRANSPARENT))
+ {
+ t << " fillcolor=\"white\" style=\"filled\"";
+ }
+ t << " color=\"red\"";
+ }
+ t << " URL=\"" << usedDir->getOutputFileBase()
+ << Doxygen::htmlFileExtension << "\"];\n";
+ dirsInGraph.insert(usedDir->getOutputFileBase(),usedDir);
+ break;
+ }
+ dir=dir->parent();
+ }
+ }
+
+ // add relations between all selected directories
+ const DirDef *dir;
+ QDictIterator<DirDef> di(dirsInGraph);
+ for (di.toFirst();(dir=di.current());++di) // foreach dir in the graph
+ {
+ QDictIterator<UsedDir> udi(*dir->usedDirs());
+ UsedDir *udir;
+ for (udi.toFirst();(udir=udi.current());++udi) // foreach used dir
+ {
+ const DirDef *usedDir=udir->dir();
+ if ((dir!=dd || !udir->inherited()) && // only show direct dependendies for this dir
+ (usedDir!=dd || !udir->inherited()) && // only show direct dependendies for this dir
+ !usedDir->isParentOf(dir) && // don't point to own parent
+ dirsInGraph.find(usedDir->getOutputFileBase())) // only point to nodes that are in the graph
+ {
+ QCString relationName;
+ relationName.sprintf("dir_%06d_%06d",dir->dirCount(),usedDir->dirCount());
+ if (Doxygen::dirRelations.find(relationName)==0)
+ {
+ // new relation
+ Doxygen::dirRelations.append(relationName,
+ new DirRelation(relationName,dir,udir));
+ }
+ int nrefs = udir->filePairs().count();
+ t << " " << dir->getOutputFileBase() << "->"
+ << usedDir->getOutputFileBase();
+ t << " [headlabel=\"" << nrefs << "\", labeldistance=1.5";
+ if (linkRelations)
+ {
+ t << " headhref=\"" << relationName << Doxygen::htmlFileExtension << "\"";
+ }
+ t << "];\n";
+ }
+ }
+ }
+
+ t << "}\n";
+}
+
+DotDirDeps::DotDirDeps(const DirDef *dir) : m_dir(dir)
+{
+}
+
+DotDirDeps::~DotDirDeps()
+{
+}
+
+QCString DotDirDeps::getBaseName() const
+{
+ return m_dir->getOutputFileBase()+"_dep";
+
+}
+
+void DotDirDeps::computeTheGraph()
+{
+ // compute md5 checksum of the graph were are about to generate
+ FTextStream md5stream(&m_theGraph);
+ //m_dir->writeDepGraph(md5stream);
+ writeDotDirDepGraph(md5stream,m_dir,m_linkRelations);
+}
+
+QCString DotDirDeps::getMapLabel() const
+{
+ return escapeCharsInString(m_baseName,FALSE);
+}
+
+QCString DotDirDeps::getImgAltText() const
+{
+ return convertToXML(m_dir->displayName());
+}
+
+QCString DotDirDeps::writeGraph(FTextStream &out,
+ GraphOutputFormat graphFormat,
+ EmbeddedOutputFormat textFormat,
+ const char *path,
+ const char *fileName,
+ const char *relPath,
+ bool generateImageMap,
+ int graphId,
+ bool linkRelations)
+{
+ m_linkRelations = linkRelations;
+ m_urlOnly = TRUE;
+ return DotGraph::writeGraph(out, graphFormat, textFormat, path, fileName, relPath, generateImageMap, graphId);
+}
+
+bool DotDirDeps::isTrivial() const
+{
+ return m_dir->depGraphIsTrivial();
+}
diff --git a/src/dotdirdeps.h b/src/dotdirdeps.h
new file mode 100644
index 0000000..f5eef65
--- /dev/null
+++ b/src/dotdirdeps.h
@@ -0,0 +1,51 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#ifndef DOTDIRDEPS_H
+#define DOTDIRDEPS_H
+
+#include "dotgraph.h"
+#include "dirdef.h"
+
+/** Representation of an directory dependency graph */
+class DotDirDeps : public DotGraph
+{
+ public:
+ DotDirDeps(const DirDef *dir);
+ ~DotDirDeps();
+ bool isTrivial() const;
+ QCString writeGraph(FTextStream &out,
+ GraphOutputFormat gf,
+ EmbeddedOutputFormat ef,
+ const char *path,
+ const char *fileName,
+ const char *relPath,
+ bool writeImageMap=TRUE,
+ int graphId=-1,
+ bool linkRelations=TRUE);
+
+ protected:
+ virtual QCString getBaseName() const;
+ virtual QCString getMapLabel() const;
+ virtual void computeTheGraph();
+ virtual QCString getImgAltText() const;
+
+ private:
+ const DirDef *m_dir;
+
+ bool m_linkRelations;
+};
+
+#endif
diff --git a/src/dotfilepatcher.cpp b/src/dotfilepatcher.cpp
new file mode 100644
index 0000000..91b7c78
--- /dev/null
+++ b/src/dotfilepatcher.cpp
@@ -0,0 +1,539 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "dotfilepatcher.h"
+
+#include "qstring.h"
+#include "config.h"
+#include "qdir.h"
+#include "message.h"
+#include "ftextstream.h"
+#include "docparser.h"
+#include "doxygen.h"
+#include "util.h"
+#include "dot.h"
+
+static const char svgZoomHeader[] =
+"<svg id=\"main\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:space=\"preserve\" onload=\"init(evt)\">\n"
+"<style type=\"text/css\"><![CDATA[\n"
+".edge:hover path { stroke: red; }\n"
+".edge:hover polygon { stroke: red; fill: red; }\n"
+"]]></style>\n"
+"<script type=\"text/javascript\"><![CDATA[\n"
+"var edges = document.getElementsByTagName('g');\n"
+"if (edges && edges.length) {\n"
+" for (var i=0;i<edges.length;i++) {\n"
+" if (edges[i].id.substr(0,4)=='edge') {\n"
+" edges[i].setAttribute('class','edge');\n"
+" }\n"
+" }\n"
+"}\n"
+"]]></script>\n"
+" <defs>\n"
+" <circle id=\"rim\" cx=\"0\" cy=\"0\" r=\"7\"/>\n"
+" <circle id=\"rim2\" cx=\"0\" cy=\"0\" r=\"3.5\"/>\n"
+" <g id=\"zoomPlus\">\n"
+" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
+" <set attributeName=\"fill\" to=\"#808080\" begin=\"zoomplus.mouseover\" end=\"zoomplus.mouseout\"/>\n"
+" </use>\n"
+" <path d=\"M-4,0h8M0,-4v8\" fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" pointer-events=\"none\"/>\n"
+" </g>\n"
+" <g id=\"zoomMin\">\n"
+" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
+" <set attributeName=\"fill\" to=\"#808080\" begin=\"zoomminus.mouseover\" end=\"zoomminus.mouseout\"/>\n"
+" </use>\n"
+" <path d=\"M-4,0h8\" fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" pointer-events=\"none\"/>\n"
+" </g>\n"
+" <g id=\"dirArrow\">\n"
+" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
+" </g>\n"
+" <g id=\"resetDef\">\n"
+" <use xlink:href=\"#rim2\" fill=\"#404040\">\n"
+" <set attributeName=\"fill\" to=\"#808080\" begin=\"reset.mouseover\" end=\"reset.mouseout\"/>\n"
+" </use>\n"
+" </g>\n"
+" </defs>\n"
+"\n"
+"<script type=\"text/javascript\">\n"
+;
+
+static const char svgZoomFooter[] =
+// navigation panel
+" <g id=\"navigator\" transform=\"translate(0 0)\" fill=\"#404254\">\n"
+" <rect fill=\"#f2f5e9\" fill-opacity=\"0.5\" stroke=\"#606060\" stroke-width=\".5\" x=\"0\" y=\"0\" width=\"60\" height=\"60\"/>\n"
+// zoom in
+" <use id=\"zoomplus\" xlink:href=\"#zoomPlus\" x=\"17\" y=\"9\" onmousedown=\"handleZoom(evt,'in')\"/>\n"
+// zoom out
+" <use id=\"zoomminus\" xlink:href=\"#zoomMin\" x=\"42\" y=\"9\" onmousedown=\"handleZoom(evt,'out')\"/>\n"
+// reset zoom
+" <use id=\"reset\" xlink:href=\"#resetDef\" x=\"30\" y=\"36\" onmousedown=\"handleReset()\"/>\n"
+// arrow up
+" <g id=\"arrowUp\" xlink:href=\"#dirArrow\" transform=\"translate(30 24)\" onmousedown=\"handlePan(0,-1)\">\n"
+" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
+" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowUp.mouseover\" end=\"arrowUp.mouseout\"/>\n"
+" </use>\n"
+" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
+" </g>\n"
+// arrow right
+" <g id=\"arrowRight\" xlink:href=\"#dirArrow\" transform=\"rotate(90) translate(36 -43)\" onmousedown=\"handlePan(1,0)\">\n"
+" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
+" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowRight.mouseover\" end=\"arrowRight.mouseout\"/>\n"
+" </use>\n"
+" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
+" </g>\n"
+// arrow down
+" <g id=\"arrowDown\" xlink:href=\"#dirArrow\" transform=\"rotate(180) translate(-30 -48)\" onmousedown=\"handlePan(0,1)\">\n"
+" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
+" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowDown.mouseover\" end=\"arrowDown.mouseout\"/>\n"
+" </use>\n"
+" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
+" </g>\n"
+// arrow left
+" <g id=\"arrowLeft\" xlink:href=\"#dirArrow\" transform=\"rotate(270) translate(-36 17)\" onmousedown=\"handlePan(-1,0)\">\n"
+" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
+" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowLeft.mouseover\" end=\"arrowLeft.mouseout\"/>\n"
+" </use>\n"
+" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
+" </g>\n"
+" </g>\n"
+// link to original SVG
+" <svg viewBox=\"0 0 15 15\" width=\"100%\" height=\"30px\" preserveAspectRatio=\"xMaxYMin meet\">\n"
+" <g id=\"arrow_out\" transform=\"scale(0.3 0.3)\">\n"
+" <a xlink:href=\"$orgname\" target=\"_base\">\n"
+" <rect id=\"button\" ry=\"5\" rx=\"5\" y=\"6\" x=\"6\" height=\"38\" width=\"38\"\n"
+" fill=\"#f2f5e9\" fill-opacity=\"0.5\" stroke=\"#606060\" stroke-width=\"1.0\"/>\n"
+" <path id=\"arrow\"\n"
+" d=\"M 11.500037,31.436501 C 11.940474,20.09759 22.043105,11.32322 32.158766,21.979434 L 37.068811,17.246167 C 37.068811,17.246167 37.088388,32 37.088388,32 L 22.160133,31.978069 C 22.160133,31.978069 26.997745,27.140456 26.997745,27.140456 C 18.528582,18.264221 13.291696,25.230495 11.500037,31.436501 z\"\n"
+" style=\"fill:#404040;\"/>\n"
+" </a>\n"
+" </g>\n"
+" </svg>\n"
+"</svg>\n"
+;
+
+static QCString replaceRef(const QCString &buf,const QCString relPath,
+ bool urlOnly,const QCString &context,const QCString &target=QCString())
+{
+ // search for href="...", store ... part in link
+ QCString href = "href";
+ //bool isXLink=FALSE;
+ int len = 6;
+ int indexS = buf.find("href=\""), indexE;
+ bool setTarget = FALSE;
+ if (indexS>5 && buf.find("xlink:href=\"")!=-1) // XLink href (for SVG)
+ {
+ indexS-=6;
+ len+=6;
+ href.prepend("xlink:");
+ //isXLink=TRUE;
+ }
+ if (indexS>=0 && (indexE=buf.find('"',indexS+len))!=-1)
+ {
+ QCString link = buf.mid(indexS+len,indexE-indexS-len);
+ QCString result;
+ if (urlOnly) // for user defined dot graphs
+ {
+ if (link.left(5)=="\\ref " || link.left(5)=="@ref ") // \ref url
+ {
+ result=href+"=\"";
+ // fake ref node to resolve the url
+ DocRef *df = new DocRef( (DocNode*) 0, link.mid(5), context );
+ result+=externalRef(relPath,df->ref(),TRUE);
+ if (!df->file().isEmpty())
+ result += df->file().data() + Doxygen::htmlFileExtension;
+ if (!df->anchor().isEmpty())
+ result += "#" + df->anchor();
+ delete df;
+ result += "\"";
+ }
+ else
+ {
+ result = href+"=\"" + link + "\"";
+ }
+ }
+ else // ref$url (external ref via tag file), or $url (local ref)
+ {
+ int marker = link.find('$');
+ if (marker!=-1)
+ {
+ QCString ref = link.left(marker);
+ QCString url = link.mid(marker+1);
+ if (!ref.isEmpty())
+ {
+ result = externalLinkTarget();
+ if (result != "") setTarget = TRUE;
+ }
+ result+= href+"=\"";
+ result+=externalRef(relPath,ref,TRUE);
+ result+= url + "\"";
+ }
+ else // should not happen, but handle properly anyway
+ {
+ result = href+"=\"" + link + "\"";
+ }
+ }
+ if (!target.isEmpty() && !setTarget)
+ {
+ result+=" target=\""+target+"\"";
+ }
+ QCString leftPart = buf.left(indexS);
+ QCString rightPart = buf.mid(indexE+1);
+ return leftPart + result + rightPart;
+ }
+ else
+ {
+ return buf;
+ }
+}
+
+/*! converts the rectangles in a client site image map into a stream
+* \param t the stream to which the result is written.
+* \param mapName the name of the map file.
+* \param relPath the relative path to the root of the output directory
+* (used in case CREATE_SUBDIRS is enabled).
+* \param urlOnly if FALSE the url field in the map contains an external
+* references followed by a $ and then the URL.
+* \param context the context (file, class, or namespace) in which the
+* map file was found
+* \returns TRUE if successful.
+*/
+bool convertMapFile(FTextStream &t,const char *mapName,
+ const QCString relPath, bool urlOnly,
+ const QCString &context)
+{
+ QFile f(mapName);
+ if (!f.open(IO_ReadOnly))
+ {
+ err("problems opening map file %s for inclusion in the docs!\n"
+ "If you installed Graphviz/dot after a previous failing run, \n"
+ "try deleting the output directory and rerun doxygen.\n",mapName);
+ return FALSE;
+ }
+ const int maxLineLen=10240;
+ while (!f.atEnd()) // foreach line
+ {
+ QCString buf(maxLineLen);
+ int numBytes = f.readLine(buf.rawData(),maxLineLen);
+ if (numBytes>0)
+ {
+ buf.resize(numBytes+1);
+
+ if (buf.left(5)=="<area")
+ {
+ QCString replBuf = replaceRef(buf,relPath,urlOnly,context);
+ // strip id="..." from replBuf since the id's are not needed and not unique.
+ int indexS = replBuf.find("id=\""), indexE;
+ if (indexS>0 && (indexE=replBuf.find('"',indexS+4))!=-1)
+ {
+ t << replBuf.left(indexS-1) << replBuf.right(replBuf.length() - indexE - 1);
+ }
+ else
+ {
+ t << replBuf;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+DotFilePatcher::DotFilePatcher(const char *patchFile)
+ : m_patchFile(patchFile)
+{
+ m_maps.setAutoDelete(TRUE);
+}
+
+QCString DotFilePatcher::file() const
+{
+ return m_patchFile;
+}
+
+int DotFilePatcher::addMap(const QCString &mapFile,const QCString &relPath,
+ bool urlOnly,const QCString &context,const QCString &label)
+{
+ int id = m_maps.count();
+ Map *map = new Map;
+ map->mapFile = mapFile;
+ map->relPath = relPath;
+ map->urlOnly = urlOnly;
+ map->context = context;
+ map->label = label;
+ map->zoomable = FALSE;
+ map->graphId = -1;
+ m_maps.append(map);
+ return id;
+}
+
+int DotFilePatcher::addFigure(const QCString &baseName,
+ const QCString &figureName,bool heightCheck)
+{
+ int id = m_maps.count();
+ Map *map = new Map;
+ map->mapFile = figureName;
+ map->urlOnly = heightCheck;
+ map->label = baseName;
+ map->zoomable = FALSE;
+ map->graphId = -1;
+ m_maps.append(map);
+ return id;
+}
+
+int DotFilePatcher::addSVGConversion(const QCString &relPath,bool urlOnly,
+ const QCString &context,bool zoomable,
+ int graphId)
+{
+ int id = m_maps.count();
+ Map *map = new Map;
+ map->relPath = relPath;
+ map->urlOnly = urlOnly;
+ map->context = context;
+ map->zoomable = zoomable;
+ map->graphId = graphId;
+ m_maps.append(map);
+ return id;
+}
+
+int DotFilePatcher::addSVGObject(const QCString &baseName,
+ const QCString &absImgName,
+ const QCString &relPath)
+{
+ int id = m_maps.count();
+ Map *map = new Map;
+ map->mapFile = absImgName;
+ map->relPath = relPath;
+ map->label = baseName;
+ map->zoomable = FALSE;
+ map->graphId = -1;
+ m_maps.append(map);
+ return id;
+}
+
+bool DotFilePatcher::run()
+{
+ //printf("DotFilePatcher::run(): %s\n",m_patchFile.data());
+ bool interactiveSVG_local = Config_getBool(INTERACTIVE_SVG);
+ bool isSVGFile = m_patchFile.right(4)==".svg";
+ int graphId = -1;
+ QCString relPath;
+ if (isSVGFile)
+ {
+ Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
+ interactiveSVG_local = interactiveSVG_local && map->zoomable;
+ graphId = map->graphId;
+ relPath = map->relPath;
+ //printf("DotFilePatcher::addSVGConversion: file=%s zoomable=%d\n",
+ // m_patchFile.data(),map->zoomable);
+ }
+ QString tmpName = QString::fromUtf8(m_patchFile+".tmp");
+ QString patchFile = QString::fromUtf8(m_patchFile);
+ if (!QDir::current().rename(patchFile,tmpName))
+ {
+ err("Failed to rename file %s to %s!\n",m_patchFile.data(),tmpName.data());
+ return FALSE;
+ }
+ QFile fi(tmpName);
+ QFile fo(patchFile);
+ if (!fi.open(IO_ReadOnly))
+ {
+ err("problem opening file %s for patching!\n",tmpName.data());
+ QDir::current().rename(tmpName,patchFile);
+ return FALSE;
+ }
+ if (!fo.open(IO_WriteOnly))
+ {
+ err("problem opening file %s for patching!\n",m_patchFile.data());
+ QDir::current().rename(tmpName,patchFile);
+ return FALSE;
+ }
+ FTextStream t(&fo);
+ const int maxLineLen=100*1024;
+ int lineNr=1;
+ int width,height;
+ bool insideHeader=FALSE;
+ bool replacedHeader=FALSE;
+ bool foundSize=FALSE;
+ while (!fi.atEnd()) // foreach line
+ {
+ QCString line(maxLineLen);
+ int numBytes = fi.readLine(line.rawData(),maxLineLen);
+ if (numBytes<=0)
+ {
+ break;
+ }
+ line.resize(numBytes+1);
+
+ //printf("line=[%s]\n",line.stripWhiteSpace().data());
+ int i;
+ ASSERT(numBytes<maxLineLen);
+ if (isSVGFile)
+ {
+ if (interactiveSVG_local)
+ {
+ if (line.find("<svg")!=-1 && !replacedHeader)
+ {
+ int count;
+ count = sscanf(line.data(),"<svg width=\"%dpt\" height=\"%dpt\"",&width,&height);
+ //printf("width=%d height=%d\n",width,height);
+ foundSize = count==2 && (width>500 || height>450);
+ if (foundSize) insideHeader=TRUE;
+ }
+ else if (insideHeader && !replacedHeader && line.find("<title>")!=-1)
+ {
+ if (foundSize)
+ {
+ // insert special replacement header for interactive SVGs
+ t << "<!--zoomable " << height << " -->\n";
+ t << svgZoomHeader;
+ t << "var viewWidth = " << width << ";\n";
+ t << "var viewHeight = " << height << ";\n";
+ if (graphId>=0)
+ {
+ t << "var sectionId = 'dynsection-" << graphId << "';\n";
+ }
+ t << "</script>\n";
+ t << "<script xlink:href=\"" << relPath << "svgpan.js\"/>\n";
+ t << "<svg id=\"graph\" class=\"graph\">\n";
+ t << "<g id=\"viewport\">\n";
+ }
+ insideHeader=FALSE;
+ replacedHeader=TRUE;
+ }
+ }
+ if (!insideHeader || !foundSize) // copy SVG and replace refs,
+ // unless we are inside the header of the SVG.
+ // Then we replace it with another header.
+ {
+ Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
+ t << replaceRef(line,map->relPath,map->urlOnly,map->context,"_top");
+ }
+ }
+ else if ((i=line.find("<!-- SVG"))!=-1 || (i=line.find("[!-- SVG"))!=-1)
+ {
+ //printf("Found marker at %d\n",i);
+ int mapId=-1;
+ t << line.left(i);
+ int n = sscanf(line.data()+i+1,"!-- SVG %d",&mapId);
+ if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
+ {
+ int e = QMAX(line.find("--]"),line.find("-->"));
+ Map *map = m_maps.at(mapId);
+ //printf("DotFilePatcher::writeSVGFigure: file=%s zoomable=%d\n",
+ // m_patchFile.data(),map->zoomable);
+ if (!writeSVGFigureLink(t,map->relPath,map->label,map->mapFile))
+ {
+ err("Problem extracting size from SVG file %s\n",map->mapFile.data());
+ }
+ if (e!=-1) t << line.mid(e+3);
+ }
+ else // error invalid map id!
+ {
+ err("Found invalid SVG id in file %s!\n",m_patchFile.data());
+ t << line.mid(i);
+ }
+ }
+ else if ((i=line.find("<!-- MAP"))!=-1)
+ {
+ int mapId=-1;
+ t << line.left(i);
+ int n = sscanf(line.data()+i,"<!-- MAP %d",&mapId);
+ if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
+ {
+ QGString result;
+ FTextStream tt(&result);
+ Map *map = m_maps.at(mapId);
+ //printf("patching MAP %d in file %s with contents of %s\n",
+ // mapId,m_patchFile.data(),map->mapFile.data());
+ convertMapFile(tt,map->mapFile,map->relPath,map->urlOnly,map->context);
+ if (!result.isEmpty())
+ {
+ t << "<map name=\"" << map->label << "\" id=\"" << map->label << "\">" << endl;
+ t << result;
+ t << "</map>" << endl;
+ }
+ }
+ else // error invalid map id!
+ {
+ err("Found invalid MAP id in file %s!\n",m_patchFile.data());
+ t << line.mid(i);
+ }
+ }
+ else if ((i=line.find("% FIG"))!=-1)
+ {
+ int mapId=-1;
+ int n = sscanf(line.data()+i+2,"FIG %d",&mapId);
+ //printf("line='%s' n=%d\n",line.data()+i,n);
+ if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
+ {
+ Map *map = m_maps.at(mapId);
+ //printf("patching FIG %d in file %s with contents of %s\n",
+ // mapId,m_patchFile.data(),map->mapFile.data());
+ if (!DotGraph::writeVecGfxFigure(t,map->label,map->mapFile))
+ {
+ err("problem writing FIG %d figure!\n",mapId);
+ return FALSE;
+ }
+ }
+ else // error invalid map id!
+ {
+ err("Found invalid bounding FIG %d in file %s!\n",mapId,m_patchFile.data());
+ t << line;
+ }
+ }
+ else
+ {
+ t << line;
+ }
+ lineNr++;
+ }
+ fi.close();
+ if (isSVGFile && interactiveSVG_local && replacedHeader)
+ {
+ QCString orgName=m_patchFile.left(m_patchFile.length()-4)+"_org.svg";
+ t << substitute(svgZoomFooter,"$orgname",stripPath(orgName));
+ fo.close();
+ // keep original SVG file so we can refer to it, we do need to replace
+ // dummy link by real ones
+ QFile fi(tmpName);
+ QFile fo(orgName);
+ if (!fi.open(IO_ReadOnly))
+ {
+ err("problem opening file %s for reading!\n",tmpName.data());
+ return FALSE;
+ }
+ if (!fo.open(IO_WriteOnly))
+ {
+ err("problem opening file %s for writing!\n",orgName.data());
+ return FALSE;
+ }
+ FTextStream t(&fo);
+ while (!fi.atEnd()) // foreach line
+ {
+ QCString line(maxLineLen);
+ int numBytes = fi.readLine(line.rawData(),maxLineLen);
+ if (numBytes<=0)
+ {
+ break;
+ }
+ line.resize(numBytes+1);
+ Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
+ t << replaceRef(line,map->relPath,map->urlOnly,map->context,"_top");
+ }
+ fi.close();
+ fo.close();
+ }
+ // remove temporary file
+ QDir::current().remove(tmpName);
+ return TRUE;
+}
diff --git a/src/dotfilepatcher.h b/src/dotfilepatcher.h
new file mode 100644
index 0000000..dd5c511
--- /dev/null
+++ b/src/dotfilepatcher.h
@@ -0,0 +1,53 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#ifndef DOTFILEPATCHER_H
+#define DOTFILEPATCHER_H
+
+#include "qcstring.h"
+#include "qlist.h"
+
+/** Helper class to insert a set of map file into an output file */
+class DotFilePatcher
+{
+ public:
+ DotFilePatcher(const char *patchFile);
+ int addMap(const QCString &mapFile,const QCString &relPath,
+ bool urlOnly,const QCString &context,const QCString &label);
+ int addFigure(const QCString &baseName,
+ const QCString &figureName,bool heightCheck);
+ int addSVGConversion(const QCString &relPath,bool urlOnly,
+ const QCString &context,bool zoomable,int graphId);
+ int addSVGObject(const QCString &baseName, const QCString &figureName,
+ const QCString &relPath);
+ bool run();
+ QCString file() const;
+
+ private:
+ struct Map
+ {
+ QCString mapFile;
+ QCString relPath;
+ bool urlOnly;
+ QCString context;
+ QCString label;
+ bool zoomable;
+ int graphId;
+ };
+ QList<Map> m_maps;
+ QCString m_patchFile;
+};
+
+#endif
diff --git a/src/dotgfxhierarchytable.cpp b/src/dotgfxhierarchytable.cpp
new file mode 100644
index 0000000..0082b7e
--- /dev/null
+++ b/src/dotgfxhierarchytable.cpp
@@ -0,0 +1,302 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "dotgfxhierarchytable.h"
+
+#include "language.h"
+#include "util.h"
+#include "message.h"
+#include "doxygen.h"
+#include "classlist.h"
+
+#define OPTIMIZE_OUTPUT_SLICE Config_getBool(OPTIMIZE_OUTPUT_SLICE)
+
+QCString DotGfxHierarchyTable::getBaseName() const
+{
+ QCString baseName;
+ if (m_prefix.isEmpty())
+ baseName.sprintf("inherit_graph_%d", m_graphId);
+ else
+ baseName.sprintf("%sinherit_graph_%d",m_prefix.data(), m_graphId);
+ return baseName;
+}
+
+void DotGfxHierarchyTable::computeTheGraph()
+{
+ QListIterator<DotNode> dnli2(*m_rootNodes);
+ DotNode *node;
+
+ FTextStream md5stream(&m_theGraph);
+ writeGraphHeader(md5stream,theTranslator->trGraphicalHierarchy());
+ md5stream << " rankdir=\"LR\";" << endl;
+ for (dnli2.toFirst();(node=dnli2.current());++dnli2)
+ {
+ if (node->subgraphId()==m_rootSubgraphNode->subgraphId())
+ {
+ node->clearWriteFlag();
+ }
+ }
+ for (dnli2.toFirst();(node=dnli2.current());++dnli2)
+ {
+ if (node->subgraphId()==m_rootSubgraphNode->subgraphId())
+ {
+ node->write(md5stream,Hierarchy,GOF_BITMAP,FALSE,TRUE,TRUE);
+ }
+ }
+ writeGraphFooter(md5stream);
+
+}
+
+QCString DotGfxHierarchyTable::getMapLabel() const
+{
+ return escapeCharsInString(m_rootSubgraphNode->label(),FALSE);
+}
+
+void DotGfxHierarchyTable::createGraph(DotNode *n,FTextStream &out,
+ const char *path,const char *fileName,int id)
+{
+ m_rootSubgraphNode = n;
+ m_graphId = id;
+ m_noDivTag = TRUE;
+ m_zoomable = FALSE;
+ DotGraph::writeGraph(out, GOF_BITMAP, EOF_Html, path, fileName, "", TRUE, 0);
+}
+
+void DotGfxHierarchyTable::writeGraph(FTextStream &out,
+ const char *path,const char *fileName)
+{
+ //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);
+ // store the original directory
+ if (!d.exists())
+ {
+ err("Output dir %s does not exist!\n",path); exit(1);
+ }
+
+ // put each connected subgraph of the hierarchy in a row of the HTML output
+ out << "<table border=\"0\" cellspacing=\"10\" cellpadding=\"0\">" << endl;
+
+ QListIterator<DotNode> dnli(*m_rootSubgraphs);
+ DotNode *n;
+ int count=0;
+ for (dnli.toFirst();(n=dnli.current());++dnli)
+ {
+ out << "<tr><td>";
+ createGraph(n,out,path,fileName,count++);
+ out << "</td></tr>" << endl;
+ }
+ out << "</table>" << endl;
+}
+
+void DotGfxHierarchyTable::addHierarchy(DotNode *n,const ClassDef *cd,bool hideSuper)
+{
+ //printf("addHierarchy `%s' baseClasses=%d\n",cd->name().data(),cd->baseClasses()->count());
+ if (cd->subClasses())
+ {
+ BaseClassListIterator bcli(*cd->subClasses());
+ BaseClassDef *bcd;
+ for ( ; (bcd=bcli.current()) ; ++bcli )
+ {
+ ClassDef *bClass=bcd->classDef;
+ //printf(" Trying sub class=`%s' usedNodes=%d\n",bClass->name().data(),m_usedNodes->count());
+ if (bClass->isVisibleInHierarchy() && hasVisibleRoot(bClass->baseClasses()))
+ {
+ DotNode *bn;
+ //printf(" Node `%s' Found visible class=`%s'\n",n->label().data(),
+ // bClass->name().data());
+ if ((bn=m_usedNodes->find(bClass->name()))) // node already present
+ {
+ if (n->children()==0 || n->children()->findRef(bn)==-1) // no arrow yet
+ {
+ n->addChild(bn,bcd->prot);
+ bn->addParent(n);
+ //printf(" Adding node %s to existing base node %s (c=%d,p=%d)\n",
+ // n->label().data(),
+ // bn->label().data(),
+ // bn->children() ? bn->children()->count() : 0,
+ // bn->parents() ? bn->parents()->count() : 0
+ // );
+ }
+ //else
+ //{
+ // printf(" Class already has an arrow!\n");
+ //}
+ }
+ else
+ {
+ QCString tmp_url="";
+ if (bClass->isLinkable() && !bClass->isHidden())
+ {
+ tmp_url=bClass->getReference()+"$"+bClass->getOutputFileBase();
+ if (!bClass->anchor().isEmpty())
+ {
+ tmp_url+="#"+bClass->anchor();
+ }
+ }
+ QCString tooltip = bClass->briefDescriptionAsTooltip();
+ bn = new DotNode(getNextNodeNumber(),
+ bClass->displayName(),
+ tooltip,
+ tmp_url.data()
+ );
+ n->addChild(bn,bcd->prot);
+ bn->addParent(n);
+ //printf(" Adding node %s to new base node %s (c=%d,p=%d)\n",
+ // n->label().data(),
+ // bn->label().data(),
+ // bn->children() ? bn->children()->count() : 0,
+ // bn->parents() ? bn->parents()->count() : 0
+ // );
+ //printf(" inserting %s (%p)\n",bClass->name().data(),bn);
+ m_usedNodes->insert(bClass->name(),bn); // add node to the used list
+ }
+ if (!bClass->isVisited() && !hideSuper && bClass->subClasses())
+ {
+ bool wasVisited=bClass->isVisited();
+ bClass->setVisited(TRUE);
+ addHierarchy(bn,bClass,wasVisited);
+ }
+ }
+ }
+ }
+ //printf("end addHierarchy\n");
+}
+
+void DotGfxHierarchyTable::addClassList(const ClassSDict *cl)
+{
+ ClassSDict::Iterator cli(*cl);
+ ClassDef *cd;
+ for (cli.toLast();(cd=cli.current());--cli)
+ {
+ //printf("Trying %s subClasses=%d\n",cd->name().data(),cd->subClasses()->count());
+ if (cd->getLanguage()==SrcLangExt_VHDL &&
+ (VhdlDocGen::VhdlClasses)cd->protection()!=VhdlDocGen::ENTITYCLASS
+ )
+ {
+ continue;
+ }
+ if (OPTIMIZE_OUTPUT_SLICE && cd->compoundType() != m_classType)
+ {
+ continue;
+ }
+ if (!hasVisibleRoot(cd->baseClasses()) &&
+ cd->isVisibleInHierarchy()
+ ) // root node in the forest
+ {
+ QCString tmp_url="";
+ if (cd->isLinkable() && !cd->isHidden())
+ {
+ tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
+ if (!cd->anchor().isEmpty())
+ {
+ tmp_url+="#"+cd->anchor();
+ }
+ }
+ //printf("Inserting root class %s\n",cd->name().data());
+ QCString tooltip = cd->briefDescriptionAsTooltip();
+ DotNode *n = new DotNode(getNextNodeNumber(),
+ cd->displayName(),
+ tooltip,
+ tmp_url.data());
+
+ //m_usedNodes->clear();
+ m_usedNodes->insert(cd->name(),n);
+ m_rootNodes->insert(0,n);
+ if (!cd->isVisited() && cd->subClasses())
+ {
+ addHierarchy(n,cd,cd->isVisited());
+ cd->setVisited(TRUE);
+ }
+ }
+ }
+}
+
+DotGfxHierarchyTable::DotGfxHierarchyTable(const char *prefix,ClassDef::CompoundType ct)
+ : m_prefix(prefix)
+ , m_classType(ct)
+{
+ m_rootNodes = new QList<DotNode>;
+ m_usedNodes = new QDict<DotNode>(1009);
+ m_usedNodes->setAutoDelete(TRUE);
+ m_rootSubgraphs = new DotNodeList;
+
+ // build a graph with each class as a node and the inheritance relations
+ // as edges
+ initClassHierarchy(Doxygen::classSDict);
+ initClassHierarchy(Doxygen::hiddenClasses);
+ addClassList(Doxygen::classSDict);
+ addClassList(Doxygen::hiddenClasses);
+ // m_usedNodes now contains all nodes in the graph
+
+ // color the graph into a set of independent subgraphs
+ bool done=FALSE;
+ int curColor=0;
+ QListIterator<DotNode> dnli(*m_rootNodes);
+ while (!done) // there are still nodes to color
+ {
+ DotNode *n;
+ done=TRUE; // we are done unless there are still uncolored nodes
+ for (dnli.toLast();(n=dnli.current());--dnli)
+ {
+ if (n->subgraphId()==-1) // not yet colored
+ {
+ //printf("Starting at node %s (%p): %d\n",n->label().data(),n,curColor);
+ done=FALSE; // still uncolored nodes
+ n->setSubgraphId(curColor);
+ n->markAsVisible();
+ n->colorConnectedNodes(curColor);
+ curColor++;
+ const DotNode *dn=n->findDocNode();
+ if (dn!=0)
+ m_rootSubgraphs->inSort(dn);
+ else
+ m_rootSubgraphs->inSort(n);
+ }
+ }
+ }
+
+ //printf("Number of independent subgraphs: %d\n",curColor);
+ QListIterator<DotNode> dnli2(*m_rootSubgraphs);
+ DotNode *n;
+ for (dnli2.toFirst();(n=dnli2.current());++dnli2)
+ {
+ //printf("Node %s color=%d (c=%d,p=%d)\n",
+ // n->label().data(),n->m_subgraphId,
+ // n->children()?n->children()->count():0,
+ // n->parents()?n->parents()->count():0);
+ int number=0;
+ n->renumberNodes(number);
+ }
+}
+
+DotGfxHierarchyTable::~DotGfxHierarchyTable()
+{
+ //printf("DotGfxHierarchyTable::~DotGfxHierarchyTable\n");
+
+ //QDictIterator<DotNode> di(*m_usedNodes);
+ //DotNode *n;
+ //for (;(n=di.current());++di)
+ //{
+ // printf("Node %p: %s\n",n,n->label().data());
+ //}
+
+ delete m_rootNodes;
+ delete m_usedNodes;
+ delete m_rootSubgraphs;
+}
diff --git a/src/dotgfxhierarchytable.h b/src/dotgfxhierarchytable.h
new file mode 100644
index 0000000..5a5bcad
--- /dev/null
+++ b/src/dotgfxhierarchytable.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#ifndef DOTGFXHIERARCHYTABLE_H
+#define DOTGFXHIERARCHYTABLE_H
+
+#include "classdef.h"
+#include "ftextstream.h"
+
+#include "dotgraph.h"
+#include "dotnode.h"
+
+/** Represents a graphical class hierarchy */
+class DotGfxHierarchyTable : public DotGraph
+{
+ public:
+ DotGfxHierarchyTable(const char *prefix="",ClassDef::CompoundType ct=ClassDef::Class);
+ ~DotGfxHierarchyTable();
+ void createGraph(DotNode *rootNode,FTextStream &t,const char *path,
+ const char *fileName,int id);
+ void writeGraph(FTextStream &t,const char *path, const char *fileName);
+ const DotNodeList *subGraphs() const { return m_rootSubgraphs; }
+
+ protected:
+ virtual QCString getBaseName() const;
+ virtual QCString getMapLabel() const;
+ virtual void computeTheGraph();
+
+ private:
+ void addHierarchy(DotNode *n,const ClassDef *cd,bool hide);
+ void addClassList(const ClassSDict *cl);
+
+ int m_graphId;
+ QCString m_prefix;
+ ClassDef::CompoundType m_classType;
+ QList<DotNode> *m_rootNodes;
+ QDict<DotNode> *m_usedNodes;
+ DotNodeList *m_rootSubgraphs;
+ DotNode * m_rootSubgraphNode;
+};
+
+
+#endif
diff --git a/src/dotgraph.cpp b/src/dotgraph.cpp
new file mode 100644
index 0000000..ca6bcca
--- /dev/null
+++ b/src/dotgraph.cpp
@@ -0,0 +1,400 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "config.h"
+#include "doxygen.h"
+#include "index.h"
+#include "md5.h"
+#include "message.h"
+#include "util.h"
+
+#include "dot.h"
+#include "dotrunner.h"
+#include "dotgraph.h"
+#include "dotnode.h"
+
+#define MAP_CMD "cmapx"
+
+QCString DotGraph::DOT_FONTNAME; // will be initialized in initDot
+int DotGraph::DOT_FONTSIZE; // will be initialized in initDot
+
+/*! Checks if a file "baseName".md5 exists. If so the contents
+* are compared with \a md5. If equal FALSE is returned.
+* The .md5 is created or updated after successful creation of the output file.
+*/
+static bool checkMd5Signature(const QCString &baseName,
+ const QCString &md5)
+{
+ QFile f(baseName+".md5");
+ if (f.open(IO_ReadOnly))
+ {
+ // read checksum
+ QCString md5stored(33);
+ int bytesRead=f.readBlock(md5stored.rawData(),32);
+ md5stored[32]='\0';
+ // compare checksum
+ if (bytesRead==32 && md5==md5stored)
+ {
+ // bail out if equal
+ return FALSE;
+ }
+ }
+ f.close();
+ return TRUE;
+}
+
+static bool checkDeliverables(const QCString &file1,
+ const QCString &file2=QCString())
+{
+ bool file1Ok = TRUE;
+ bool file2Ok = TRUE;
+ if (!file1.isEmpty())
+ {
+ QFileInfo fi(file1);
+ file1Ok = (fi.exists() && fi.size()>0);
+ }
+ if (!file2.isEmpty())
+ {
+ QFileInfo fi(file2);
+ file2Ok = (fi.exists() && fi.size()>0);
+ }
+ return file1Ok && file2Ok;
+}
+
+static void removeDotGraph(const QCString &dotName)
+{
+ if (Config_getBool(DOT_CLEANUP))
+ {
+ QDir d;
+ d.remove(dotName);
+ }
+}
+
+static bool insertMapFile(FTextStream &out,const QCString &mapFile,
+ const QCString &relPath,const QCString &mapLabel)
+{
+ QFileInfo fi(mapFile);
+ if (fi.exists() && fi.size()>0) // reuse existing map file
+ {
+ QGString tmpstr;
+ FTextStream tmpout(&tmpstr);
+ convertMapFile(tmpout,mapFile,relPath,FALSE);
+ if (!tmpstr.isEmpty())
+ {
+ out << "<map name=\"" << mapLabel << "\" id=\"" << mapLabel << "\">" << endl;
+ out << tmpstr;
+ out << "</map>" << endl;
+ }
+ return TRUE;
+ }
+ return FALSE; // no map file yet, need to generate it
+}
+
+//--------------------------------------------------------------------
+
+QCString DotGraph::IMG_EXT;
+
+QCString DotGraph::imgName() const
+{
+ return m_baseName + ((m_graphFormat == GOF_BITMAP) ?
+ ("." + IMG_EXT) : (Config_getBool(USE_PDFLATEX) ? ".pdf" : ".eps"));
+}
+
+QCString DotGraph::writeGraph(
+ FTextStream& t, // output stream for the code file (html, ...)
+ GraphOutputFormat gf, // bitmap(png/svg) or ps(eps/pdf)
+ EmbeddedOutputFormat ef, // html, latex, ...
+ const char* path, // output folder
+ const char* fileName, // name of the code file (for code patcher)
+ const char* relPath, // output folder relativ to code file
+ bool generateImageMap, // in case of bitmap, shall there be code generated?
+ int graphId) // number of this graph in the current code, used in svg code
+{
+ m_graphFormat = gf;
+ m_textFormat = ef;
+ m_dir = QDir(path);
+ m_fileName = fileName;
+ m_relPath = relPath;
+ m_generateImageMap = generateImageMap;
+ m_graphId = graphId;
+
+ m_absPath = QCString(m_dir.absPath().data()) + "/";
+ m_baseName = getBaseName();
+
+ computeTheGraph();
+
+ m_regenerate = prepareDotFile();
+
+ if (!m_doNotAddImageToIndex) Doxygen::indexList->addImageFile(imgName());
+
+ generateCode(t);
+
+ return m_baseName;
+}
+
+bool DotGraph::prepareDotFile()
+{
+ if (!m_dir.exists())
+ {
+ err("Output dir %s does not exist!\n", m_dir.path().data()); exit(1);
+ }
+
+ QCString sigStr(33);
+ uchar md5_sig[16];
+ // calculate md5
+ MD5Buffer((const unsigned char*)m_theGraph.data(), m_theGraph.length(), md5_sig);
+ // convert result to a string
+ MD5SigToString(md5_sig, sigStr.rawData(), 33);
+
+ // already queued files are processed again in case the output format has changed
+
+ if (!checkMd5Signature(absBaseName(), sigStr) &&
+ checkDeliverables(absImgName(),
+ m_graphFormat == GOF_BITMAP && m_generateImageMap ? absMapName() : QCString()
+ )
+ )
+ {
+ // all needed files are there
+ removeDotGraph(absDotName());
+ return FALSE;
+ }
+
+ // need to rebuild the image
+
+ // write .dot file because image was new or has changed
+ QFile f(absDotName());
+ if (!f.open(IO_WriteOnly))
+ {
+ err("Could not open file %s for writing\n",f.name().data());
+ return TRUE;
+ }
+ FTextStream t(&f);
+ t << m_theGraph;
+ f.close();
+
+ if (m_graphFormat == GOF_BITMAP)
+ {
+ // run dot to create a bitmap image
+ DotRunner * dotRun = DotManager::instance()->createRunner(absDotName(), sigStr);
+ dotRun->addJob(Config_getEnum(DOT_IMAGE_FORMAT), absImgName());
+ if (m_generateImageMap) dotRun->addJob(MAP_CMD, absMapName());
+ }
+ else if (m_graphFormat == GOF_EPS)
+ {
+ // run dot to create a .eps image
+ DotRunner *dotRun = DotManager::instance()->createRunner(absDotName(), sigStr);
+ if (Config_getBool(USE_PDFLATEX))
+ {
+ dotRun->addJob("pdf",absImgName());
+ }
+ else
+ {
+ dotRun->addJob("ps",absImgName());
+ }
+ }
+ return TRUE;
+}
+
+void DotGraph::generateCode(FTextStream &t)
+{
+ if (m_graphFormat==GOF_BITMAP && m_textFormat==EOF_DocBook)
+ {
+ t << "<para>" << endl;
+ t << " <informalfigure>" << endl;
+ t << " <mediaobject>" << endl;
+ t << " <imageobject>" << endl;
+ t << " <imagedata";
+ t << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << m_relPath << m_baseName << "." << IMG_EXT << "\">";
+ t << "</imagedata>" << endl;
+ t << " </imageobject>" << endl;
+ t << " </mediaobject>" << endl;
+ t << " </informalfigure>" << endl;
+ t << "</para>" << endl;
+ }
+ else if (m_graphFormat==GOF_BITMAP && m_generateImageMap) // produce HTML to include the image
+ {
+ if (IMG_EXT=="svg") // add link to SVG file without map file
+ {
+ if (!m_noDivTag) t << "<div class=\"center\">";
+ if (m_regenerate || !writeSVGFigureLink(t,m_relPath,m_baseName,absImgName())) // need to patch the links in the generated SVG file
+ {
+ if (m_regenerate)
+ {
+ DotManager::instance()->addSVGConversion(absImgName(),m_relPath,FALSE,QCString(),m_zoomable,m_graphId);
+ }
+ int mapId = DotManager::instance()->addSVGObject(m_fileName,m_baseName,absImgName(),m_relPath);
+ t << "<!-- SVG " << mapId << " -->" << endl;
+ }
+ if (!m_noDivTag) t << "</div>" << endl;
+ }
+ else // add link to bitmap file with image map
+ {
+ if (!m_noDivTag) t << "<div class=\"center\">";
+ t << "<img src=\"" << relImgName() << "\" border=\"0\" usemap=\"#" << getMapLabel() << "\" alt=\"" << getImgAltText() << "\"/>";
+ if (!m_noDivTag) t << "</div>";
+ t << endl;
+ if (m_regenerate || !insertMapFile(t, absMapName(), m_relPath, getMapLabel()))
+ {
+ int mapId = DotManager::instance()->addMap(m_fileName, absMapName(), m_relPath, m_urlOnly, QCString(), getMapLabel());
+ t << "<!-- MAP " << mapId << " -->" << endl;
+ }
+ }
+ }
+ else if (m_graphFormat==GOF_EPS) // produce tex to include the .eps image
+ {
+ if (m_regenerate || !writeVecGfxFigure(t,m_baseName,absBaseName()))
+ {
+ int figId = DotManager::instance()->addFigure(m_fileName,m_baseName,absBaseName(),FALSE /*TRUE*/);
+ t << endl << "% FIG " << figId << endl;
+ }
+ }
+}
+
+void DotGraph::writeGraphHeader(FTextStream &t,const QCString &title)
+{
+ t << "digraph ";
+ if (title.isEmpty())
+ {
+ t << "\"Dot Graph\"";
+ }
+ else
+ {
+ t << "\"" << convertToXML(title) << "\"";
+ }
+ t << endl << "{" << endl;
+ if (Config_getBool(INTERACTIVE_SVG)) // insert a comment to force regeneration when this
+ // option is toggled
+ {
+ t << " // INTERACTIVE_SVG=YES\n";
+ }
+ t << " // LATEX_PDF_SIZE\n"; // write placeholder for LaTeX PDF bounding box size repacement
+ if (Config_getBool(DOT_TRANSPARENT))
+ {
+ t << " bgcolor=\"transparent\";" << endl;
+ }
+ t << " edge [fontname=\"" << DOT_FONTNAME << "\","
+ "fontsize=\"" << DOT_FONTSIZE << "\","
+ "labelfontname=\"" << DOT_FONTNAME << "\","
+ "labelfontsize=\"" << DOT_FONTSIZE << "\"];\n";
+ t << " node [fontname=\"" << DOT_FONTNAME << "\","
+ "fontsize=\"" << DOT_FONTSIZE << "\",shape=record];\n";
+}
+
+void DotGraph::writeGraphFooter(FTextStream &t)
+{
+ t << "}" << endl;
+}
+
+void DotGraph::computeGraph(DotNode *root,
+ GraphType gt,
+ GraphOutputFormat format,
+ const QCString &rank, // either "LR", "RL", or ""
+ bool renderParents,
+ bool backArrows,
+ const QCString &title,
+ QGString &graphStr)
+{
+ //printf("computeMd5Signature\n");
+ QGString buf;
+ FTextStream md5stream(&buf);
+ writeGraphHeader(md5stream,title);
+ if (!rank.isEmpty())
+ {
+ md5stream << " rankdir=\"" << rank << "\";" << endl;
+ }
+ root->clearWriteFlag();
+ root->write(md5stream, gt, format, gt!=CallGraph && gt!=Dependency, TRUE, backArrows);
+ if (renderParents && root->parents())
+ {
+ QListIterator<DotNode> dnli(*root->parents());
+ const DotNode *pn;
+ for (dnli.toFirst();(pn=dnli.current());++dnli)
+ {
+ if (pn->isVisible())
+ {
+ root->writeArrow(md5stream, // stream
+ gt, // graph type
+ format, // output format
+ pn, // child node
+ pn->edgeInfo()->at(pn->children()->findRef(root)), // edge info
+ FALSE, // topDown?
+ backArrows // point back?
+ );
+ }
+ pn->write(md5stream, // stream
+ gt, // graph type
+ format, // output format
+ TRUE, // topDown?
+ FALSE, // toChildren?
+ backArrows // backward pointing arrows?
+ );
+ }
+ }
+ writeGraphFooter(md5stream);
+
+ graphStr=buf.data();
+}
+
+bool DotGraph::writeVecGfxFigure(FTextStream &out,const QCString &baseName,
+ const QCString &figureName)
+{
+ int width=400,height=550;
+ if (Config_getBool(USE_PDFLATEX))
+ {
+ if (!DotRunner::readBoundingBox(figureName+".pdf",&width,&height,FALSE))
+ {
+ //printf("writeVecGfxFigure()=0\n");
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!DotRunner::readBoundingBox(figureName+".eps",&width,&height,TRUE))
+ {
+ //printf("writeVecGfxFigure()=0\n");
+ return FALSE;
+ }
+ }
+ //printf("Got PDF/EPS size %d,%d\n",width,height);
+ int maxWidth = 350; /* approx. page width in points, excl. margins */
+ int maxHeight = 550; /* approx. page height in points, excl. margins */
+ out << "\\nopagebreak\n"
+ "\\begin{figure}[H]\n"
+ "\\begin{center}\n"
+ "\\leavevmode\n";
+ if (width>maxWidth || height>maxHeight) // figure too big for page
+ {
+ // c*width/maxWidth > c*height/maxHeight, where c=maxWidth*maxHeight>0
+ if (width*maxHeight>height*maxWidth)
+ {
+ out << "\\includegraphics[width=" << maxWidth << "pt]";
+ }
+ else
+ {
+ out << "\\includegraphics[height=" << maxHeight << "pt]";
+ }
+ }
+ else
+ {
+ out << "\\includegraphics[width=" << width << "pt]";
+ }
+
+ out << "{" << baseName << "}\n"
+ "\\end{center}\n"
+ "\\end{figure}\n";
+
+ //printf("writeVecGfxFigure()=1\n");
+ return TRUE;
+}
diff --git a/src/dotgraph.h b/src/dotgraph.h
new file mode 100644
index 0000000..27d6938
--- /dev/null
+++ b/src/dotgraph.h
@@ -0,0 +1,113 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1997-2019 by Dimitri van Heesch.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation under the terms of the GNU General Public License is hereby
+ * granted. No representations are made about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied warranty.
+ * See the GNU General Public License for more details.
+ *
+ * Documents produced by Doxygen are derivative works derived from the
+ * input used in their production; they are not affected by this license.
+ *
+ */
+
+#ifndef DOTGRAPH_H
+#define DOTGRAPH_H
+
+#include <qcstring.h>
+#include <qgstring.h>
+#include <qdir.h>
+
+class FTextStream;
+class DotNode;
+
+enum GraphOutputFormat { GOF_BITMAP, GOF_EPS };
+enum EmbeddedOutputFormat { EOF_Html, EOF_LaTeX, EOF_Rtf, EOF_DocBook };
+enum GraphType { Dependency, Inheritance, Collaboration, Hierarchy, CallGraph };
+
+/** A dot graph */
+class DotGraph
+{
+ public:
+ DotGraph() : m_curNodeNumber(0), m_doNotAddImageToIndex(FALSE), m_noDivTag(FALSE), m_zoomable(TRUE), m_urlOnly(FALSE) {}
+ virtual ~DotGraph() {}
+
+ static QCString DOT_FONTNAME; // will be initialized in initDot
+ static int DOT_FONTSIZE; // will be initialized in initDot
+
+ static bool writeVecGfxFigure(FTextStream& out, const QCString& baseName, const QCString& figureName);
+
+ protected:
+ /** returns node numbers. The Counter is reset by the constructor */
+ int getNextNodeNumber() { return ++m_curNodeNumber; }
+
+ QCString writeGraph(FTextStream &t,
+ GraphOutputFormat gf,
+ EmbeddedOutputFormat ef,
+ const char *path,
+ const char *fileName,
+ const char *relPath,
+ bool writeImageMap=TRUE,
+ int graphId=-1
+ );
+
+ static void writeGraphHeader(FTextStream& t, const QCString& title = QCString());
+ static void writeGraphFooter(FTextStream& t);
+ static void computeGraph(DotNode* root,
+ GraphType gt,
+ GraphOutputFormat format,
+ const QCString& rank, // either "LR", "RL", or ""
+ bool renderParents,
+ bool backArrows,
+ const QCString& title,
+ QGString& graphStr
+ );
+
+ virtual QCString getBaseName() const = 0;
+ virtual QCString absMapName() const { return m_absPath + m_baseName + ".map"; }
+ virtual QCString getMapLabel() const = 0;
+ virtual QCString getImgAltText() const { return ""; }
+
+ virtual void computeTheGraph() = 0;
+
+ static QCString IMG_EXT;
+
+ friend void initDot();
+
+ QCString absBaseName() const { return m_absPath + m_baseName; }
+ QCString absDotName() const { return m_absPath + m_baseName + ".dot"; }
+ QCString imgName() const;
+ QCString absImgName() const { return m_absPath + imgName(); }
+ QCString relImgName() const { return m_relPath + imgName(); }
+
+ // the following variables are used while writing the graph to a .dot file
+ GraphOutputFormat m_graphFormat;
+ EmbeddedOutputFormat m_textFormat;
+ QDir m_dir;
+ QCString m_fileName;
+ QCString m_relPath;
+ bool m_generateImageMap;
+ int m_graphId;
+
+ QCString m_absPath;
+ QCString m_baseName;
+ QGString m_theGraph;
+ bool m_regenerate;
+ bool m_doNotAddImageToIndex;
+ bool m_noDivTag;
+ bool m_zoomable;
+ bool m_urlOnly;
+
+ private:
+ DotGraph(const DotGraph &);
+ DotGraph &operator=(const DotGraph &);
+
+ bool prepareDotFile();
+ void generateCode(FTextStream &t);
+
+ int m_curNodeNumber;
+};
+
+#endif
diff --git a/src/dotgroupcollaboration.cpp b/src/dotgroupcollaboration.cpp
new file mode 100644
index 0000000..be55ac0
--- /dev/null
+++ b/src/dotgroupcollaboration.cpp
@@ -0,0 +1,381 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "dotgroupcollaboration.h"
+
+#include "dotnode.h"
+#include "classlist.h"
+#include "doxygen.h"
+#include "namespacedef.h"
+#include "pagedef.h"
+#include "util.h"
+#include "config.h"
+
+#define DOT_TRANSPARENT Config_getBool(DOT_TRANSPARENT)
+
+DotGroupCollaboration::DotGroupCollaboration(const GroupDef* gd)
+{
+ QCString tmp_url = gd->getReference()+"$"+gd->getOutputFileBase();
+ m_usedNodes = new QDict<DotNode>(1009);
+ QCString tooltip = gd->briefDescriptionAsTooltip();
+ m_rootNode = new DotNode(getNextNodeNumber(), gd->groupTitle(), tooltip, tmp_url, TRUE );
+ m_rootNode->markAsVisible();
+ m_usedNodes->insert(gd->name(), m_rootNode );
+ m_edges.setAutoDelete(TRUE);
+
+ m_diskName = gd->getOutputFileBase();
+
+ buildGraph( gd );
+}
+
+DotGroupCollaboration::~DotGroupCollaboration()
+{
+ delete m_usedNodes;
+}
+
+void DotGroupCollaboration::buildGraph(const GroupDef* gd)
+{
+ QCString tmp_url;
+ //===========================
+ // hierarchy.
+
+ // Write parents
+ const GroupList *groups = gd->partOfGroups();
+ if ( groups )
+ {
+ GroupListIterator gli(*groups);
+ const GroupDef *d;
+ for (gli.toFirst();(d=gli.current());++gli)
+ {
+ DotNode* nnode = m_usedNodes->find(d->name());
+ if ( !nnode )
+ { // add node
+ tmp_url = d->getReference()+"$"+d->getOutputFileBase();
+ QCString tooltip = d->briefDescriptionAsTooltip();
+ nnode = new DotNode(getNextNodeNumber(), d->groupTitle(), tooltip, tmp_url );
+ nnode->markAsVisible();
+ m_usedNodes->insert(d->name(), nnode );
+ }
+ tmp_url = "";
+ addEdge( nnode, m_rootNode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
+ }
+ }
+
+ // Add subgroups
+ if ( gd->getSubGroups() && gd->getSubGroups()->count() )
+ {
+ QListIterator<GroupDef> defli(*gd->getSubGroups());
+ const GroupDef *def;
+ for (;(def=defli.current());++defli)
+ {
+ DotNode* nnode = m_usedNodes->find(def->name());
+ if ( !nnode )
+ { // add node
+ tmp_url = def->getReference()+"$"+def->getOutputFileBase();
+ QCString tooltip = def->briefDescriptionAsTooltip();
+ nnode = new DotNode(getNextNodeNumber(), def->groupTitle(), tooltip, tmp_url );
+ nnode->markAsVisible();
+ m_usedNodes->insert(def->name(), nnode );
+ }
+ tmp_url = "";
+ addEdge( m_rootNode, nnode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
+ }
+ }
+
+ //=======================
+ // Write collaboration
+
+ // Add members
+ addMemberList( gd->getMemberList(MemberListType_allMembersList) );
+
+ // Add classes
+ if ( gd->getClasses() && gd->getClasses()->count() )
+ {
+ ClassSDict::Iterator defli(*gd->getClasses());
+ ClassDef *def;
+ for (;(def=defli.current());++defli)
+ {
+ tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
+ if (!def->anchor().isEmpty())
+ {
+ tmp_url+="#"+def->anchor();
+ }
+ addCollaborationMember( def, tmp_url, DotGroupCollaboration::tclass );
+ }
+ }
+
+ // Add namespaces
+ if ( gd->getNamespaces() && gd->getNamespaces()->count() )
+ {
+ NamespaceSDict::Iterator defli(*gd->getNamespaces());
+ NamespaceDef *def;
+ for (;(def=defli.current());++defli)
+ {
+ tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
+ addCollaborationMember( def, tmp_url, DotGroupCollaboration::tnamespace );
+ }
+ }
+
+ // Add files
+ if ( gd->getFiles() && gd->getFiles()->count() )
+ {
+ QListIterator<FileDef> defli(*gd->getFiles());
+ const FileDef *def;
+ for (;(def=defli.current());++defli)
+ {
+ tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
+ addCollaborationMember( def, tmp_url, DotGroupCollaboration::tfile );
+ }
+ }
+
+ // Add pages
+ if ( gd->getPages() && gd->getPages()->count() )
+ {
+ PageSDict::Iterator defli(*gd->getPages());
+ PageDef *def;
+ for (;(def=defli.current());++defli)
+ {
+ tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
+ addCollaborationMember( def, tmp_url, DotGroupCollaboration::tpages );
+ }
+ }
+
+ // Add directories
+ if ( gd->getDirs() && gd->getDirs()->count() )
+ {
+ QListIterator<DirDef> defli(*gd->getDirs());
+ const DirDef *def;
+ for (;(def=defli.current());++defli)
+ {
+ tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
+ addCollaborationMember( def, tmp_url, DotGroupCollaboration::tdir );
+ }
+ }
+}
+
+void DotGroupCollaboration::addMemberList( MemberList* ml )
+{
+ if ( !( ml && ml->count()) ) return;
+ MemberListIterator defli(*ml);
+ MemberDef *def;
+ for (;(def=defli.current());++defli)
+ {
+ QCString tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension
+ +"#"+def->anchor();
+ addCollaborationMember( def, tmp_url, DotGroupCollaboration::tmember );
+ }
+}
+
+DotGroupCollaboration::Edge* DotGroupCollaboration::addEdge(
+ DotNode* _pNStart, DotNode* _pNEnd, EdgeType _eType,
+ const QCString& _label, const QCString& _url )
+{
+ // search a existing link.
+ QListIterator<Edge> lli(m_edges);
+ Edge* newEdge = 0;
+ for ( lli.toFirst(); (newEdge=lli.current()); ++lli)
+ {
+ if ( newEdge->pNStart==_pNStart &&
+ newEdge->pNEnd==_pNEnd &&
+ newEdge->eType==_eType
+ )
+ { // edge already found
+ break;
+ }
+ }
+ if ( newEdge==0 ) // new link
+ {
+ newEdge = new Edge(_pNStart,_pNEnd,_eType);
+ m_edges.append( newEdge );
+ }
+
+ if (!_label.isEmpty())
+ {
+ newEdge->links.append(new Link(_label,_url));
+ }
+
+ return newEdge;
+}
+
+void DotGroupCollaboration::addCollaborationMember(
+ const Definition* def, QCString& url, EdgeType eType )
+{
+ // Create group nodes
+ if ( !def->partOfGroups() )
+ return;
+ GroupListIterator gli(*def->partOfGroups());
+ GroupDef *d;
+ QCString tmp_str;
+ for (;(d=gli.current());++gli)
+ {
+ DotNode* nnode = m_usedNodes->find(d->name());
+ if ( nnode != m_rootNode )
+ {
+ if ( nnode==0 )
+ { // add node
+ tmp_str = d->getReference()+"$"+d->getOutputFileBase();
+ QCString tooltip = d->briefDescriptionAsTooltip();
+ nnode = new DotNode(getNextNodeNumber(), d->groupTitle(), tooltip, tmp_str );
+ nnode->markAsVisible();
+ m_usedNodes->insert(d->name(), nnode );
+ }
+ tmp_str = def->qualifiedName();
+ addEdge( m_rootNode, nnode, eType, tmp_str, url );
+ }
+ }
+}
+
+QCString DotGroupCollaboration::getBaseName() const
+{
+ return m_diskName;
+}
+
+void DotGroupCollaboration::computeTheGraph()
+{
+ FTextStream md5stream(&m_theGraph);
+ writeGraphHeader(md5stream,m_rootNode->label());
+
+ // clean write flags
+ QDictIterator<DotNode> dni(*m_usedNodes);
+ DotNode *pn;
+ for (dni.toFirst();(pn=dni.current());++dni)
+ {
+ pn->clearWriteFlag();
+ }
+
+ // write other nodes.
+ for (dni.toFirst();(pn=dni.current());++dni)
+ {
+ pn->write(md5stream,Inheritance,m_graphFormat,TRUE,FALSE,FALSE);
+ }
+
+ // write edges
+ QListIterator<Edge> eli(m_edges);
+ Edge* edge;
+ for (eli.toFirst();(edge=eli.current());++eli)
+ {
+ edge->write( md5stream );
+ }
+
+ writeGraphFooter(md5stream);
+
+}
+
+QCString DotGroupCollaboration::getMapLabel() const
+{
+ return escapeCharsInString(m_baseName, FALSE);
+}
+
+QCString DotGroupCollaboration::writeGraph( FTextStream &t,
+ GraphOutputFormat graphFormat, EmbeddedOutputFormat textFormat,
+ const char *path, const char *fileName, const char *relPath,
+ bool generateImageMap,int graphId)
+{
+ m_doNotAddImageToIndex = TRUE;
+
+ return DotGraph::writeGraph(t, graphFormat, textFormat, path, fileName, relPath, generateImageMap, graphId);
+}
+
+void DotGroupCollaboration::Edge::write( FTextStream &t ) const
+{
+ const char* linkTypeColor[] = {
+ "darkorchid3"
+ ,"orange"
+ ,"blueviolet"
+ ,"darkgreen"
+ ,"firebrick4"
+ ,"grey75"
+ ,"midnightblue"
+ };
+ QCString arrowStyle = "dir=\"none\", style=\"dashed\"";
+ t << " Node" << pNStart->number();
+ t << "->";
+ t << "Node" << pNEnd->number();
+
+ t << " [shape=plaintext";
+ if (links.count()>0) // there are links
+ {
+ t << ", ";
+ // HTML-like edge labels crash on my Mac with Graphviz 2.0! and
+ // are not supported by older version of dot.
+ //
+ //t << label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">";
+ //QListIterator<Link> lli(links);
+ //Link *link;
+ //for( lli.toFirst(); (link=lli.current()); ++lli)
+ //{
+ // t << "<TR><TD";
+ // if ( !link->url.isEmpty() )
+ // t << " HREF=\"" << link->url << "\"";
+ // t << ">" << link->label << "</TD></TR>";
+ //}
+ //t << "</TABLE>>";
+
+ t << "label=\"";
+ QListIterator<Link> lli(links);
+ Link *link;
+ bool first=TRUE;
+ int count=0;
+ const int maxLabels = 10;
+ for( lli.toFirst(); (link=lli.current()) && count<maxLabels; ++lli,++count)
+ {
+ if (first) first=FALSE; else t << "\\n";
+ t << DotNode::convertLabel(link->label);
+ }
+ if (count==maxLabels) t << "\\n...";
+ t << "\"";
+
+ }
+ switch( eType )
+ {
+ case thierarchy:
+ arrowStyle = "dir=\"back\", style=\"solid\"";
+ break;
+ default:
+ t << ", color=\"" << linkTypeColor[(int)eType] << "\"";
+ break;
+ }
+ t << ", " << arrowStyle;
+ t << "];" << endl;
+}
+
+bool DotGroupCollaboration::isTrivial() const
+{
+ return m_usedNodes->count() <= 1;
+}
+
+void DotGroupCollaboration::writeGraphHeader(FTextStream &t,
+ const QCString &title) const
+{
+ t << "digraph ";
+ if (title.isEmpty())
+ {
+ t << "\"Dot Graph\"";
+ }
+ else
+ {
+ t << "\"" << convertToXML(title) << "\"";
+ }
+ t << endl;
+ t << "{" << endl;
+ if (DOT_TRANSPARENT)
+ {
+ t << " bgcolor=\"transparent\";" << endl;
+ }
+ t << " edge [fontname=\"" << DOT_FONTNAME << "\",fontsize=\"" << DOT_FONTSIZE << "\","
+ "labelfontname=\"" << DOT_FONTNAME << "\",labelfontsize=\"" << DOT_FONTSIZE << "\"];\n";
+ t << " node [fontname=\"" << DOT_FONTNAME << "\",fontsize=\"" << DOT_FONTSIZE << "\",shape=box];\n";
+ t << " rankdir=LR;\n";
+}
diff --git a/src/dotgroupcollaboration.h b/src/dotgroupcollaboration.h
new file mode 100644
index 0000000..539637f
--- /dev/null
+++ b/src/dotgroupcollaboration.h
@@ -0,0 +1,85 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#ifndef DOTGROUPCOLLABORATION_H
+#define DOTGROUPCOLLABORATION_H
+
+#include "dotgraph.h"
+#include "qlist.h"
+#include "groupdef.h"
+
+/** Representation of a group collaboration graph */
+class DotGroupCollaboration : public DotGraph
+{
+ public :
+ DotGroupCollaboration(const GroupDef* gd);
+ ~DotGroupCollaboration();
+ QCString writeGraph(FTextStream &t, GraphOutputFormat gf,EmbeddedOutputFormat ef,
+ const char *path,const char *fileName,const char *relPath,
+ bool writeImageMap=TRUE,int graphId=-1);
+ bool isTrivial() const;
+
+ protected:
+ virtual QCString getBaseName() const;
+ virtual QCString getMapLabel() const;
+ virtual void computeTheGraph();
+
+ private :
+ enum EdgeType
+ {
+ tmember = 0,
+ tclass,
+ tnamespace,
+ tfile,
+ tpages,
+ tdir,
+ thierarchy
+ };
+
+ struct Link
+ {
+ Link(const QCString lab,const QCString &u) : label(lab), url(u) {}
+ QCString label;
+ QCString url;
+ };
+
+ struct Edge
+ {
+ Edge(DotNode *start,DotNode *end,EdgeType type)
+ : pNStart(start), pNEnd(end), eType(type)
+ { links.setAutoDelete(TRUE); }
+
+ DotNode* pNStart;
+ DotNode* pNEnd;
+ EdgeType eType;
+
+ QList<Link> links;
+ void write( FTextStream &t ) const;
+ };
+
+ void buildGraph(const GroupDef* gd);
+ void addCollaborationMember(const Definition* def, QCString& url, EdgeType eType );
+ void addMemberList( class MemberList* ml );
+ void writeGraphHeader(FTextStream &t,const QCString &title) const;
+ Edge* addEdge( DotNode* _pNStart, DotNode* _pNEnd, EdgeType _eType,
+ const QCString& _label, const QCString& _url );
+
+ DotNode *m_rootNode;
+ QDict<DotNode> *m_usedNodes;
+ QCString m_diskName;
+ QList<Edge> m_edges;
+};
+
+#endif
diff --git a/src/dotincldepgraph.cpp b/src/dotincldepgraph.cpp
new file mode 100644
index 0000000..c968b68
--- /dev/null
+++ b/src/dotincldepgraph.cpp
@@ -0,0 +1,238 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "dotincldepgraph.h"
+#include "dotnode.h"
+#include "util.h"
+#include "config.h"
+
+void DotInclDepGraph::buildGraph(DotNode *n,const FileDef *fd,int distance)
+{
+ QList<IncludeInfo> *includeFiles = m_inverse ? fd->includedByFileList() : fd->includeFileList();
+ if (includeFiles)
+ {
+ QListIterator<IncludeInfo> ili(*includeFiles);
+ IncludeInfo *ii;
+ for (;(ii=ili.current());++ili)
+ {
+ const FileDef *bfd = ii->fileDef;
+ QCString in = ii->includeName;
+ //printf(">>>> in=`%s' bfd=%p\n",ii->includeName.data(),bfd);
+ bool doc=TRUE,src=FALSE;
+ if (bfd)
+ {
+ in = bfd->absFilePath();
+ doc = bfd->isLinkable() && !bfd->isHidden();
+ src = bfd->generateSourceFile();
+ }
+ if (doc || src || !Config_getBool(HIDE_UNDOC_RELATIONS))
+ {
+ QCString url="";
+ if (bfd) url=bfd->getOutputFileBase().copy();
+ if (!doc && src)
+ {
+ url=bfd->getSourceFileBase();
+ }
+ DotNode *bn = m_usedNodes->find(in);
+ if (bn) // file is already a node in the graph
+ {
+ n->addChild(bn,0,0,0);
+ bn->addParent(n);
+ bn->setDistance(distance);
+ }
+ else
+ {
+ QCString tmp_url;
+ QCString tooltip;
+ if (bfd)
+ {
+ tmp_url=doc || src ? bfd->getReference()+"$"+url : QCString();
+ tooltip = bfd->briefDescriptionAsTooltip();
+ }
+ bn = new DotNode(getNextNodeNumber(),// n
+ ii->includeName, // label
+ tooltip, // tip
+ tmp_url, // url
+ FALSE, // rootNode
+ 0); // cd
+ n->addChild(bn,0,0,0);
+ bn->addParent(n);
+ m_usedNodes->insert(in,bn);
+ bn->setDistance(distance);
+
+ if (bfd) buildGraph(bn,bfd,distance+1);
+ }
+ }
+ }
+ }
+}
+
+void DotInclDepGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes)
+{
+ while (queue.count()>0 && maxNodes>0)
+ {
+ DotNode *n = queue.take(0);
+ if (!n->isVisible() && n->distance()<=Config_getInt(MAX_DOT_GRAPH_DEPTH)) // not yet processed
+ {
+ n->markAsVisible();
+ maxNodes--;
+ // add direct children
+ if (n->children())
+ {
+ QListIterator<DotNode> li(*n->children());
+ const DotNode *dn;
+ for (li.toFirst();(dn=li.current());++li)
+ {
+ queue.append(dn);
+ }
+ }
+ }
+ }
+}
+
+void DotInclDepGraph::determineTruncatedNodes(QList<DotNode> &queue)
+{
+ while (queue.count()>0)
+ {
+ DotNode *n = queue.take(0);
+ if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
+ {
+ bool truncated = FALSE;
+ if (n->children())
+ {
+ QListIterator<DotNode> li(*n->children());
+ const DotNode *dn;
+ for (li.toFirst();(dn=li.current());++li)
+ {
+ if (!dn->isVisible())
+ {
+ truncated = TRUE;
+ }
+ else
+ {
+ queue.append(dn);
+ }
+ }
+ }
+ n->markAsTruncated(truncated);
+ }
+ }
+}
+
+DotInclDepGraph::DotInclDepGraph(const FileDef *fd,bool inverse)
+{
+ m_inverse = inverse;
+ ASSERT(fd!=0);
+ m_inclDepFileName = fd->includeDependencyGraphFileName();
+ m_inclByDepFileName = fd->includedByDependencyGraphFileName();
+ QCString tmp_url=fd->getReference()+"$"+fd->getOutputFileBase();
+ QCString tooltip = fd->briefDescriptionAsTooltip();
+ m_startNode = new DotNode(getNextNodeNumber(),
+ fd->docName(),
+ tooltip,
+ tmp_url.data(),
+ TRUE); // root node
+ m_startNode->setDistance(0);
+ m_usedNodes = new QDict<DotNode>(1009);
+ m_usedNodes->insert(fd->absFilePath(),m_startNode);
+ buildGraph(m_startNode,fd,1);
+
+ int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES);
+ QList<DotNode> openNodeQueue;
+ openNodeQueue.append(m_startNode);
+ determineVisibleNodes(openNodeQueue,maxNodes);
+ openNodeQueue.clear();
+ openNodeQueue.append(m_startNode);
+ determineTruncatedNodes(openNodeQueue);
+}
+
+DotInclDepGraph::~DotInclDepGraph()
+{
+ DotNode::deleteNodes(m_startNode);
+ delete m_usedNodes;
+}
+
+QCString DotInclDepGraph::getBaseName() const
+{
+ if (m_inverse)
+ {
+ return m_inclByDepFileName;
+ }
+ else
+ {
+ return m_inclDepFileName;
+ }
+}
+
+void DotInclDepGraph::computeTheGraph()
+{
+ computeGraph(m_startNode, Dependency, m_graphFormat, "", FALSE,
+ m_inverse, m_startNode->label(), m_theGraph);
+}
+
+QCString DotInclDepGraph::getMapLabel() const
+{
+ if (m_inverse)
+ {
+ return escapeCharsInString(m_startNode->label(),FALSE) + "dep";
+ }
+ else
+ {
+ return escapeCharsInString(m_startNode->label(),FALSE);
+ }
+}
+
+QCString DotInclDepGraph::writeGraph(FTextStream &out,
+ GraphOutputFormat graphFormat,
+ EmbeddedOutputFormat textFormat,
+ const char *path,
+ const char *fileName,
+ const char *relPath,
+ bool generateImageMap,
+ int graphId)
+{
+ return DotGraph::writeGraph(out, graphFormat, textFormat, path, fileName, relPath, generateImageMap, graphId);
+}
+
+bool DotInclDepGraph::isTrivial() const
+{
+ return m_startNode->children()==0;
+}
+
+bool DotInclDepGraph::isTooBig() const
+{
+ int numNodes = m_startNode->children() ? m_startNode->children()->count() : 0;
+ return numNodes>=Config_getInt(DOT_GRAPH_MAX_NODES);
+}
+
+void DotInclDepGraph::writeXML(FTextStream &t)
+{
+ QDictIterator<DotNode> dni(*m_usedNodes);
+ DotNode *node;
+ for (;(node=dni.current());++dni)
+ {
+ node->writeXML(t,FALSE);
+ }
+}
+
+void DotInclDepGraph::writeDocbook(FTextStream &t)
+{
+ QDictIterator<DotNode> dni(*m_usedNodes);
+ DotNode *node;
+ for (;(node=dni.current());++dni)
+ {
+ node->writeDocbook(t,FALSE);
+ }
+}
diff --git a/src/dotincldepgraph.h b/src/dotincldepgraph.h
new file mode 100644
index 0000000..b664ccb
--- /dev/null
+++ b/src/dotincldepgraph.h
@@ -0,0 +1,56 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#ifndef DOTINCLDEPGRAPH_H
+#define DOTINCLDEPGRAPH_H
+
+#include "qcstring.h"
+#include "filedef.h"
+
+#include "dotgraph.h"
+
+/** Representation of an include dependency graph */
+class DotInclDepGraph : public DotGraph
+{
+ public:
+ DotInclDepGraph(const FileDef *fd,bool inverse);
+ ~DotInclDepGraph();
+ QCString writeGraph(FTextStream &t, GraphOutputFormat gf, EmbeddedOutputFormat ef,
+ const char *path,const char *fileName,const char *relPath,
+ bool writeImageMap=TRUE,int graphId=-1);
+ bool isTrivial() const;
+ bool isTooBig() const;
+ void writeXML(FTextStream &t);
+ void writeDocbook(FTextStream &t);
+
+ protected:
+ virtual QCString getBaseName() const;
+ virtual QCString getMapLabel() const;
+ virtual void computeTheGraph();
+
+ private:
+ QCString diskName() const;
+ void buildGraph(DotNode *n,const FileDef *fd,int distance);
+ void determineVisibleNodes(QList<DotNode> &queue,int &maxNodes);
+ void determineTruncatedNodes(QList<DotNode> &queue);
+
+ DotNode *m_startNode;
+ QDict<DotNode> *m_usedNodes;
+ QCString m_inclDepFileName;
+ QCString m_inclByDepFileName;
+ bool m_inverse;
+};
+
+#endif
diff --git a/src/dotnode.cpp b/src/dotnode.cpp
new file mode 100644
index 0000000..41d5f06
--- /dev/null
+++ b/src/dotnode.cpp
@@ -0,0 +1,950 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "dotnode.h"
+
+#include "ftextstream.h"
+#include "classdef.h"
+#include "config.h"
+#include "memberlist.h"
+#include "membergroup.h"
+#include "language.h"
+#include "doxygen.h"
+#include "util.h"
+
+/** Helper struct holding the properties of a edge in a dot graph. */
+struct EdgeProperties
+{
+ const char * const *edgeColorMap;
+ const char * const *arrowStyleMap;
+ const char * const *edgeStyleMap;
+};
+
+/*! mapping from protection levels to color names */
+static const char *normalEdgeColorMap[] =
+{
+ "midnightblue", // Public
+ "darkgreen", // Protected
+ "firebrick4", // Private
+ "darkorchid3", // "use" relation
+ "grey75", // Undocumented
+ "orange", // template relation
+ "orange" // type constraint
+};
+
+static const char *normalArrowStyleMap[] =
+{
+ "empty", // Public
+ "empty", // Protected
+ "empty", // Private
+ "open", // "use" relation
+ 0, // Undocumented
+ 0 // template relation
+};
+
+static const char *normalEdgeStyleMap[] =
+{
+ "solid", // inheritance
+ "dashed" // usage
+};
+
+static const char *umlEdgeColorMap[] =
+{
+ "midnightblue", // Public
+ "darkgreen", // Protected
+ "firebrick4", // Private
+ "grey25", // "use" relation
+ "grey75", // Undocumented
+ "orange", // template relation
+ "orange" // type constraint
+};
+
+static const char *umlArrowStyleMap[] =
+{
+ "onormal", // Public
+ "onormal", // Protected
+ "onormal", // Private
+ "odiamond", // "use" relation
+ 0, // Undocumented
+ 0 // template relation
+};
+
+static const char *umlEdgeStyleMap[] =
+{
+ "solid", // inheritance
+ "solid" // usage
+};
+
+static EdgeProperties normalEdgeProps =
+{
+ normalEdgeColorMap, normalArrowStyleMap, normalEdgeStyleMap
+};
+
+static EdgeProperties umlEdgeProps =
+{
+ umlEdgeColorMap, umlArrowStyleMap, umlEdgeStyleMap
+};
+
+static QCString escapeTooltip(const QCString &tooltip)
+{
+ QCString result;
+ const char *p=tooltip.data();
+ if (p==0) return result;
+ char c;
+ while ((c=*p++))
+ {
+ switch(c)
+ {
+ case '"': result+="\\\""; break;
+ case '\\': result+="\\\\"; break;
+ default: result+=c; break;
+ }
+ }
+ return result;
+}
+
+static void writeBoxMemberList(FTextStream &t,
+ char prot,MemberList *ml,const ClassDef *scope,
+ bool isStatic=FALSE,const QDict<void> *skipNames=0)
+{
+ (void)isStatic;
+ if (ml)
+ {
+ MemberListIterator mlia(*ml);
+ MemberDef *mma;
+ int totalCount=0;
+ for (mlia.toFirst();(mma = mlia.current());++mlia)
+ {
+ if (mma->getClassDef()==scope &&
+ (skipNames==0 || skipNames->find(mma->name())==0))
+ {
+ totalCount++;
+ }
+ }
+
+ int count=0;
+ for (mlia.toFirst();(mma = mlia.current());++mlia)
+ {
+ if (mma->getClassDef() == scope &&
+ (skipNames==0 || skipNames->find(mma->name())==0))
+ {
+ int numFields = Config_getInt(UML_LIMIT_NUM_FIELDS);
+ if (numFields>0 && (totalCount>numFields*3/2 && count>=numFields))
+ {
+ t << theTranslator->trAndMore(QCString().sprintf("%d",totalCount-count)) << "\\l";
+ break;
+ }
+ else
+ {
+ t << prot << " ";
+ t << DotNode::convertLabel(mma->name());
+ if (!mma->isObjCMethod() &&
+ (mma->isFunction() || mma->isSlot() || mma->isSignal())) t << "()";
+ t << "\\l";
+ count++;
+ }
+ }
+ }
+ // write member groups within the memberlist
+ MemberGroupList *mgl = ml->getMemberGroupList();
+ if (mgl)
+ {
+ MemberGroupListIterator mgli(*mgl);
+ MemberGroup *mg;
+ for (mgli.toFirst();(mg=mgli.current());++mgli)
+ {
+ if (mg->members())
+ {
+ writeBoxMemberList(t,prot,mg->members(),scope,isStatic,skipNames);
+ }
+ }
+ }
+ }
+}
+
+QCString DotNode::convertLabel(const QCString &l)
+{
+ QString bBefore("\\_/<({[: =-+@%#~?$"); // break before character set
+ QString bAfter(">]),:;|"); // break after character set
+ QString p(l);
+ if (p.isEmpty()) return QCString();
+ QString result;
+ QChar c,pc=0;
+ uint idx = 0;
+ int len=p.length();
+ int charsLeft=len;
+ int sinceLast=0;
+ int foldLen=17; // ideal text length
+ while (idx < p.length())
+ {
+ c = p[idx++];
+ QString replacement;
+ switch(c)
+ {
+ case '\\': replacement="\\\\"; break;
+ case '\n': replacement="\\n"; break;
+ case '<': replacement="\\<"; break;
+ case '>': replacement="\\>"; break;
+ case '|': replacement="\\|"; break;
+ case '{': replacement="\\{"; break;
+ case '}': replacement="\\}"; break;
+ case '"': replacement="\\\""; break;
+ default: replacement=c; break;
+ }
+ // Some heuristics to insert newlines to prevent too long
+ // boxes and at the same time prevent ugly breaks
+ if (c=='\n')
+ {
+ result+=replacement;
+ foldLen = (3*foldLen+sinceLast+2)/4;
+ sinceLast=1;
+ }
+ else if ((pc!=':' || c!=':') && charsLeft>foldLen/3 && sinceLast>foldLen && bBefore.contains(c))
+ {
+ result+="\\l";
+ result+=replacement;
+ foldLen = (foldLen+sinceLast+1)/2;
+ sinceLast=1;
+ }
+ else if (charsLeft>1+foldLen/4 && sinceLast>foldLen+foldLen/3 &&
+ !isupper(c) && p[idx].category()==QChar::Letter_Uppercase)
+ {
+ result+=replacement;
+ result+="\\l";
+ foldLen = (foldLen+sinceLast+1)/2;
+ sinceLast=0;
+ }
+ else if (charsLeft>foldLen/3 && sinceLast>foldLen && bAfter.contains(c) && (c!=':' || p[idx]!=':'))
+ {
+ result+=replacement;
+ result+="\\l";
+ foldLen = (foldLen+sinceLast+1)/2;
+ sinceLast=0;
+ }
+ else
+ {
+ result+=replacement;
+ sinceLast++;
+ }
+ charsLeft--;
+ pc=c;
+ }
+ return result.utf8();
+}
+
+static QCString stripProtectionPrefix(const QCString &s)
+{
+ if (!s.isEmpty() && (s[0]=='-' || s[0]=='+' || s[0]=='~' || s[0]=='#'))
+ {
+ return s.mid(1);
+ }
+ else
+ {
+ return s;
+ }
+}
+
+DotNode::DotNode(int n,const char *lab,const char *tip, const char *url,
+ bool isRoot,const ClassDef *cd)
+ : m_subgraphId(-1)
+ , m_number(n)
+ , m_label(lab)
+ , m_tooltip(tip)
+ , m_url(url)
+ , m_parents(0)
+ , m_children(0)
+ , m_edgeInfo(0)
+ , m_deleted(FALSE)
+ , m_written(FALSE)
+ , m_hasDoc(FALSE)
+ , m_isRoot(isRoot)
+ , m_classDef(cd)
+ , m_visible(FALSE)
+ , m_truncated(Unknown)
+ , m_distance(1000)
+ , m_renumbered(false)
+{
+}
+
+DotNode::~DotNode()
+{
+ delete m_children;
+ delete m_parents;
+ delete m_edgeInfo;
+}
+
+void DotNode::addChild(DotNode *n,
+ int edgeColor,
+ int edgeStyle,
+ const char *edgeLab,
+ const char *edgeURL,
+ int edgeLabCol
+)
+{
+ if (m_children==0)
+ {
+ m_children = new QList<DotNode>;
+ m_edgeInfo = new QList<EdgeInfo>;
+ m_edgeInfo->setAutoDelete(TRUE);
+ }
+ m_children->append(n);
+ EdgeInfo *ei = new EdgeInfo(
+ edgeColor,
+ edgeStyle,
+ edgeLab,
+ edgeURL,
+ edgeLabCol==-1 ? edgeColor : edgeLabCol);
+ m_edgeInfo->append(ei);
+}
+
+void DotNode::addParent(DotNode *n)
+{
+ if (m_parents==0)
+ {
+ m_parents = new QList<DotNode>;
+ }
+ m_parents->append(n);
+}
+
+void DotNode::removeChild(DotNode *n)
+{
+ if (m_children) m_children->remove(n);
+}
+
+void DotNode::removeParent(DotNode *n)
+{
+ if (m_parents) m_parents->remove(n);
+}
+
+void DotNode::deleteNode(DotNodeList &deletedList,SDict<DotNode> *skipNodes)
+{
+ if (m_deleted) return; // avoid recursive loops in case the graph has cycles
+ m_deleted=TRUE;
+ if (m_parents!=0) // delete all parent nodes of this node
+ {
+ QListIterator<DotNode> dnlip(*m_parents);
+ DotNode *pn;
+ for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
+ {
+ //pn->removeChild(this);
+ pn->deleteNode(deletedList,skipNodes);
+ }
+ }
+ if (m_children!=0) // delete all child nodes of this node
+ {
+ QListIterator<DotNode> dnlic(*m_children);
+ DotNode *cn;
+ for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
+ {
+ //cn->removeParent(this);
+ cn->deleteNode(deletedList,skipNodes);
+ }
+ }
+ // add this node to the list of deleted nodes.
+ //printf("skipNodes=%p find(%p)=%p\n",skipNodes,this,skipNodes ? skipNodes->find((int)this) : 0);
+ if (skipNodes==0 || skipNodes->find((char*)this)==0)
+ {
+ //printf("deleting\n");
+ deletedList.append(this);
+ }
+}
+
+void DotNode::setDistance(int distance)
+{
+ if (distance<m_distance) m_distance = distance;
+}
+
+inline int DotNode::findParent( DotNode *n )
+{
+ if ( !m_parents ) return -1;
+ return m_parents->find(n);
+}
+
+/*! helper function that deletes all nodes in a connected graph, given
+* one of the graph's nodes
+*/
+void DotNode::deleteNodes(DotNode *node,SDict<DotNode> *skipNodes)
+{
+ //printf("deleteNodes skipNodes=%p\n",skipNodes);
+ static DotNodeList deletedNodes;
+ deletedNodes.setAutoDelete(TRUE);
+ node->deleteNode(deletedNodes,skipNodes); // collect nodes to be deleted.
+ deletedNodes.clear(); // actually remove the nodes.
+}
+
+void DotNode::writeBox(FTextStream &t,
+ GraphType gt,
+ GraphOutputFormat /*format*/,
+ bool hasNonReachableChildren) const
+{
+ const char *labCol =
+ m_url.isEmpty() ? "grey75" : // non link
+ (hasNonReachableChildren ? "red" : "black");
+ t << " Node" << m_number << " [label=\"";
+
+ if (m_classDef && Config_getBool(UML_LOOK) && (gt==Inheritance || gt==Collaboration))
+ {
+ // add names shown as relations to a dictionary, so we don't show
+ // them as attributes as well
+ QDict<void> arrowNames(17);
+ if (m_edgeInfo)
+ {
+ // for each edge
+ QListIterator<EdgeInfo> li(*m_edgeInfo);
+ EdgeInfo *ei;
+ for (li.toFirst();(ei=li.current());++li)
+ {
+ if (!ei->label().isEmpty()) // labels joined by \n
+ {
+ int li=ei->label().find('\n');
+ int p=0;
+ QCString lab;
+ while ((li=ei->label().find('\n',p))!=-1)
+ {
+ lab = stripProtectionPrefix(ei->label().mid(p,li-p));
+ arrowNames.insert(lab,(void*)0x8);
+ p=li+1;
+ }
+ lab = stripProtectionPrefix(ei->label().right(ei->label().length()-p));
+ arrowNames.insert(lab,(void*)0x8);
+ }
+ }
+ }
+
+ //printf("DotNode::writeBox for %s\n",m_classDef->name().data());
+ t << "{" << convertLabel(m_label);
+ t << "\\n|";
+ writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubAttribs),m_classDef,FALSE,&arrowNames);
+ writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubStaticAttribs),m_classDef,TRUE,&arrowNames);
+ writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_properties),m_classDef,FALSE,&arrowNames);
+ writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacAttribs),m_classDef,FALSE,&arrowNames);
+ writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacStaticAttribs),m_classDef,TRUE,&arrowNames);
+ writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proAttribs),m_classDef,FALSE,&arrowNames);
+ writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proStaticAttribs),m_classDef,TRUE,&arrowNames);
+ if (Config_getBool(EXTRACT_PRIVATE))
+ {
+ writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priAttribs),m_classDef,FALSE,&arrowNames);
+ writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priStaticAttribs),m_classDef,TRUE,&arrowNames);
+ }
+ t << "|";
+ writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubMethods),m_classDef);
+ writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubStaticMethods),m_classDef,TRUE);
+ writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubSlots),m_classDef);
+ writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacMethods),m_classDef);
+ writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacStaticMethods),m_classDef,TRUE);
+ writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proMethods),m_classDef);
+ writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proStaticMethods),m_classDef,TRUE);
+ writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proSlots),m_classDef);
+ if (Config_getBool(EXTRACT_PRIVATE))
+ {
+ writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priMethods),m_classDef);
+ writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priStaticMethods),m_classDef,TRUE);
+ writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priSlots),m_classDef);
+ }
+ if (m_classDef->getLanguage()!=SrcLangExt_Fortran &&
+ m_classDef->getMemberGroupSDict())
+ {
+ MemberGroupSDict::Iterator mgdi(*m_classDef->getMemberGroupSDict());
+ MemberGroup *mg;
+ for (mgdi.toFirst();(mg=mgdi.current());++mgdi)
+ {
+ if (mg->members())
+ {
+ writeBoxMemberList(t,'*',mg->members(),m_classDef,FALSE,&arrowNames);
+ }
+ }
+ }
+ t << "}";
+ }
+ else // standard look
+ {
+ t << convertLabel(m_label);
+ }
+ t << "\",height=0.2,width=0.4";
+ if (m_isRoot)
+ {
+ t << ",color=\"black\", fillcolor=\"grey75\", style=\"filled\", fontcolor=\"black\"";
+ }
+ else
+ {
+ if (!Config_getBool(DOT_TRANSPARENT))
+ {
+ t << ",color=\"" << labCol << "\", fillcolor=\"";
+ t << "white";
+ t << "\", style=\"filled\"";
+ }
+ else
+ {
+ t << ",color=\"" << labCol << "\"";
+ }
+ if (!m_url.isEmpty())
+ {
+ int anchorPos = m_url.findRev('#');
+ if (anchorPos==-1)
+ {
+ t << ",URL=\"" << m_url << Doxygen::htmlFileExtension << "\"";
+ }
+ else
+ {
+ t << ",URL=\"" << m_url.left(anchorPos) << Doxygen::htmlFileExtension
+ << m_url.right(m_url.length()-anchorPos) << "\"";
+ }
+ }
+ }
+ if (!m_tooltip.isEmpty())
+ {
+ t << ",tooltip=\"" << escapeTooltip(m_tooltip) << "\"";
+ }
+ else
+ {
+ t << ",tooltip=\" \""; // space in tooltip is required otherwise still something like 'Node0' is used
+ }
+ t << "];" << endl;
+}
+
+void DotNode::writeArrow(FTextStream &t,
+ GraphType gt,
+ GraphOutputFormat format,
+ const DotNode *cn,
+ const EdgeInfo *ei,
+ bool topDown,
+ bool pointBack) const
+{
+ t << " Node";
+ if (topDown)
+ t << cn->number();
+ else
+ t << m_number;
+ t << " -> Node";
+ if (topDown)
+ t << m_number;
+ else
+ t << cn->number();
+ t << " [";
+
+ const EdgeProperties *eProps = Config_getBool(UML_LOOK) ? &umlEdgeProps : &normalEdgeProps;
+ QCString aStyle = eProps->arrowStyleMap[ei->color()];
+ bool umlUseArrow = aStyle=="odiamond";
+
+ if (pointBack && !umlUseArrow) t << "dir=\"back\",";
+ t << "color=\"" << eProps->edgeColorMap[ei->color()]
+ << "\",fontsize=\"" << DotGraph::DOT_FONTSIZE << "\",";
+ t << "style=\"" << eProps->edgeStyleMap[ei->style()] << "\"";
+ if (!ei->label().isEmpty())
+ {
+ t << ",label=\" " << convertLabel(ei->label()) << "\" ";
+ }
+ if (Config_getBool(UML_LOOK) &&
+ eProps->arrowStyleMap[ei->color()] &&
+ (gt==Inheritance || gt==Collaboration)
+ )
+ {
+ bool rev = pointBack;
+ if (umlUseArrow) rev=!rev; // UML use relates has arrow on the start side
+ if (rev)
+ t << ",arrowtail=\"" << eProps->arrowStyleMap[ei->color()] << "\"";
+ else
+ t << ",arrowhead=\"" << eProps->arrowStyleMap[ei->color()] << "\"";
+ }
+
+ if (format==GOF_BITMAP) t << ",fontname=\"" << DotGraph::DOT_FONTNAME << "\"";
+ t << "];" << endl;
+}
+
+void DotNode::write(FTextStream &t,
+ GraphType gt,
+ GraphOutputFormat format,
+ bool topDown,
+ bool toChildren,
+ bool backArrows) const
+{
+ //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);
+ m_written=TRUE;
+ QList<DotNode> *nl = toChildren ? m_children : m_parents;
+ if (nl)
+ {
+ if (toChildren)
+ {
+ QListIterator<DotNode> dnli1(*nl);
+ QListIterator<EdgeInfo> dnli2(*m_edgeInfo);
+ const DotNode *cn;
+ for (dnli1.toFirst();(cn=dnli1.current());++dnli1,++dnli2)
+ {
+ if (cn->isVisible())
+ {
+ //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",cn->label().data());
+ writeArrow(t,gt,format,cn,dnli2.current(),topDown,backArrows);
+ }
+ cn->write(t,gt,format,topDown,toChildren,backArrows);
+ }
+ }
+ else // render parents
+ {
+ QListIterator<DotNode> dnli(*nl);
+ DotNode *pn;
+ for (dnli.toFirst();(pn=dnli.current());++dnli)
+ {
+ if (pn->isVisible())
+ {
+ //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",pn->label().data());
+ writeArrow(t,
+ gt,
+ format,
+ pn,
+ pn->edgeInfo()->at(pn->children()->findRef(this)),
+ FALSE,
+ backArrows
+ );
+ }
+ pn->write(t,gt,format,TRUE,FALSE,backArrows);
+ }
+ }
+ }
+ //printf("end DotNode::write(%d) name=%s\n",distance,m_label.data());
+}
+
+void DotNode::writeXML(FTextStream &t,bool isClassGraph) const
+{
+ t << " <node id=\"" << m_number << "\">" << endl;
+ t << " <label>" << convertToXML(m_label) << "</label>" << endl;
+ if (!m_url.isEmpty())
+ {
+ QCString url(m_url);
+ const char *refPtr = url.data();
+ char *urlPtr = strchr(url.rawData(),'$');
+ if (urlPtr)
+ {
+ *urlPtr++='\0';
+ t << " <link refid=\"" << convertToXML(urlPtr) << "\"";
+ if (*refPtr!='\0')
+ {
+ t << " external=\"" << convertToXML(refPtr) << "\"";
+ }
+ t << "/>" << endl;
+ }
+ }
+ if (m_children)
+ {
+ QListIterator<DotNode> nli(*m_children);
+ QListIterator<EdgeInfo> eli(*m_edgeInfo);
+ DotNode *childNode;
+ EdgeInfo *edgeInfo;
+ for (;(childNode=nli.current());++nli,++eli)
+ {
+ edgeInfo=eli.current();
+ t << " <childnode refid=\"" << childNode->number() << "\" relation=\"";
+ if (isClassGraph)
+ {
+ switch(edgeInfo->color())
+ {
+ case EdgeInfo::Blue: t << "public-inheritance"; break;
+ case EdgeInfo::Green: t << "protected-inheritance"; break;
+ case EdgeInfo::Red: t << "private-inheritance"; break;
+ case EdgeInfo::Purple: t << "usage"; break;
+ case EdgeInfo::Orange: t << "template-instance"; break;
+ case EdgeInfo::Orange2: t << "type-constraint"; break;
+ case EdgeInfo::Grey: ASSERT(0); break;
+ }
+ }
+ else // include graph
+ {
+ t << "include";
+ }
+ t << "\">" << endl;
+ if (!edgeInfo->label().isEmpty())
+ {
+ int p=0;
+ int ni;
+ while ((ni=edgeInfo->label().find('\n',p))!=-1)
+ {
+ t << " <edgelabel>"
+ << convertToXML(edgeInfo->label().mid(p,ni-p))
+ << "</edgelabel>" << endl;
+ p=ni+1;
+ }
+ t << " <edgelabel>"
+ << convertToXML(edgeInfo->label().right(edgeInfo->label().length()-p))
+ << "</edgelabel>" << endl;
+ }
+ t << " </childnode>" << endl;
+ }
+ }
+ t << " </node>" << endl;
+}
+
+void DotNode::writeDocbook(FTextStream &t,bool isClassGraph) const
+{
+ t << " <node id=\"" << m_number << "\">" << endl;
+ t << " <label>" << convertToXML(m_label) << "</label>" << endl;
+ if (!m_url.isEmpty())
+ {
+ QCString url(m_url);
+ const char *refPtr = url.data();
+ char *urlPtr = strchr(url.rawData(),'$');
+ if (urlPtr)
+ {
+ *urlPtr++='\0';
+ t << " <link refid=\"" << convertToXML(urlPtr) << "\"";
+ if (*refPtr!='\0')
+ {
+ t << " external=\"" << convertToXML(refPtr) << "\"";
+ }
+ t << "/>" << endl;
+ }
+ }
+ if (m_children)
+ {
+ QListIterator<DotNode> nli(*m_children);
+ QListIterator<EdgeInfo> eli(*m_edgeInfo);
+ DotNode *childNode;
+ EdgeInfo *edgeInfo;
+ for (;(childNode=nli.current());++nli,++eli)
+ {
+ edgeInfo=eli.current();
+ t << " <childnode refid=\"" << childNode->number() << "\" relation=\"";
+ if (isClassGraph)
+ {
+ switch(edgeInfo->color())
+ {
+ case EdgeInfo::Blue: t << "public-inheritance"; break;
+ case EdgeInfo::Green: t << "protected-inheritance"; break;
+ case EdgeInfo::Red: t << "private-inheritance"; break;
+ case EdgeInfo::Purple: t << "usage"; break;
+ case EdgeInfo::Orange: t << "template-instance"; break;
+ case EdgeInfo::Orange2: t << "type-constraint"; break;
+ case EdgeInfo::Grey: ASSERT(0); break;
+ }
+ }
+ else // include graph
+ {
+ t << "include";
+ }
+ t << "\">" << endl;
+ if (!edgeInfo->label().isEmpty())
+ {
+ int p=0;
+ int ni;
+ while ((ni=edgeInfo->label().find('\n',p))!=-1)
+ {
+ t << " <edgelabel>"
+ << convertToXML(edgeInfo->label().mid(p,ni-p))
+ << "</edgelabel>" << endl;
+ p=ni+1;
+ }
+ t << " <edgelabel>"
+ << convertToXML(edgeInfo->label().right(edgeInfo->label().length()-p))
+ << "</edgelabel>" << endl;
+ }
+ t << " </childnode>" << endl;
+ }
+ }
+ t << " </node>" << endl;
+}
+
+
+void DotNode::writeDEF(FTextStream &t) const
+{
+ const char* nodePrefix = " node-";
+
+ t << " node = {" << endl;
+ t << nodePrefix << "id = " << m_number << ';' << endl;
+ t << nodePrefix << "label = '" << m_label << "';" << endl;
+
+ if (!m_url.isEmpty())
+ {
+ QCString url(m_url);
+ const char *refPtr = url.data();
+ char *urlPtr = strchr(url.rawData(),'$');
+ if (urlPtr)
+ {
+ *urlPtr++='\0';
+ t << nodePrefix << "link = {" << endl << " "
+ << nodePrefix << "link-id = '" << urlPtr << "';" << endl;
+
+ if (*refPtr!='\0')
+ {
+ t << " " << nodePrefix << "link-external = '"
+ << refPtr << "';" << endl;
+ }
+ t << " };" << endl;
+ }
+ }
+ if (m_children)
+ {
+ QListIterator<DotNode> nli(*m_children);
+ QListIterator<EdgeInfo> eli(*m_edgeInfo);
+ DotNode *childNode;
+ EdgeInfo *edgeInfo;
+ for (;(childNode=nli.current());++nli,++eli)
+ {
+ edgeInfo=eli.current();
+ t << " node-child = {" << endl;
+ t << " child-id = '" << childNode->number() << "';" << endl;
+ t << " relation = ";
+
+ switch(edgeInfo->color())
+ {
+ case EdgeInfo::Blue: t << "public-inheritance"; break;
+ case EdgeInfo::Green: t << "protected-inheritance"; break;
+ case EdgeInfo::Red: t << "private-inheritance"; break;
+ case EdgeInfo::Purple: t << "usage"; break;
+ case EdgeInfo::Orange: t << "template-instance"; break;
+ case EdgeInfo::Orange2: t << "type-constraint"; break;
+ case EdgeInfo::Grey: ASSERT(0); break;
+ }
+ t << ';' << endl;
+
+ if (!edgeInfo->label().isEmpty())
+ {
+ t << " edgelabel = <<_EnD_oF_dEf_TeXt_" << endl
+ << edgeInfo->label() << endl
+ << "_EnD_oF_dEf_TeXt_;" << endl;
+ }
+ t << " }; /* node-child */" << endl;
+ } /* for (;childNode...) */
+ }
+ t << " }; /* node */" << endl;
+}
+
+
+void DotNode::clearWriteFlag()
+{
+ m_written=FALSE;
+ if (m_parents!=0)
+ {
+ QListIterator<DotNode> dnlip(*m_parents);
+ DotNode *pn;
+ for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
+ {
+ if (pn->isWritten())
+ {
+ pn->clearWriteFlag();
+ }
+ }
+ }
+ if (m_children!=0)
+ {
+ QListIterator<DotNode> dnlic(*m_children);
+ DotNode *cn;
+ for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
+ {
+ if (cn->isWritten())
+ {
+ cn->clearWriteFlag();
+ }
+ }
+ }
+}
+
+void DotNode::colorConnectedNodes(int curColor)
+{
+ if (m_children)
+ {
+ QListIterator<DotNode> dnlic(*m_children);
+ DotNode *cn;
+ for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
+ {
+ if (cn->subgraphId()==-1) // uncolored child node
+ {
+ cn->setSubgraphId(curColor);
+ cn->markAsVisible();
+ cn->colorConnectedNodes(curColor);
+ //printf("coloring node %s (%p): %d\n",cn->label().data(),cn,cn->subgraphId());
+ }
+ }
+ }
+
+ if (m_parents)
+ {
+ QListIterator<DotNode> dnlip(*m_parents);
+ DotNode *pn;
+ for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
+ {
+ if (pn->subgraphId()==-1) // uncolored parent node
+ {
+ pn->setSubgraphId(curColor);
+ pn->markAsVisible();
+ pn->colorConnectedNodes(curColor);
+ //printf("coloring node %s (%p): %d\n",pn->label().data(),pn,pn->subgraphId());
+ }
+ }
+ }
+}
+
+void DotNode::renumberNodes(int &number)
+{
+ m_number = number++;
+ if (m_children)
+ {
+ QListIterator<DotNode> dnlic(*m_children);
+ DotNode *cn;
+ for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
+ {
+ if (!cn->isRenumbered())
+ {
+ cn->markRenumbered();
+ cn->renumberNodes(number);
+ }
+ }
+ }
+}
+
+const DotNode *DotNode::findDocNode() const
+{
+ if (!m_url.isEmpty()) return this;
+ //printf("findDocNode(): `%s'\n",m_label.data());
+ if (m_parents)
+ {
+ QListIterator<DotNode> dnli(*m_parents);
+ DotNode *pn;
+ for (dnli.toFirst();(pn=dnli.current());++dnli)
+ {
+ if (!pn->hasDocumentation())
+ {
+ pn->markHasDocumentation();
+ const DotNode *dn = pn->findDocNode();
+ if (dn) return dn;
+ }
+ }
+ }
+ if (m_children)
+ {
+ QListIterator<DotNode> dnli(*m_children);
+ DotNode *cn;
+ for (dnli.toFirst();(cn=dnli.current());++dnli)
+ {
+ if (!cn->hasDocumentation())
+ {
+ cn->markHasDocumentation();
+ const DotNode *dn = cn->findDocNode();
+ if (dn) return dn;
+ }
+ }
+ }
+ return 0;
+}
+
+//--------------------------------------------------------------
+
+int DotNodeList::compareValues(const DotNode *n1,const DotNode *n2) const
+{
+ return qstricmp(n1->label(),n2->label());
+}
+
+
+
diff --git a/src/dotnode.h b/src/dotnode.h
new file mode 100644
index 0000000..334fdef
--- /dev/null
+++ b/src/dotnode.h
@@ -0,0 +1,138 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#ifndef DOTNODE_H
+#define DOTNODE_H
+
+#include "sortdict.h"
+
+#include "dotgraph.h"
+
+class ClassDef;
+class DotNodeList;
+class FTextStream;
+
+/** Attributes of an edge of a dot graph */
+class EdgeInfo
+{
+ public:
+ enum Colors { Blue=0, Green=1, Red=2, Purple=3, Grey=4, Orange=5, Orange2=6 };
+ enum Styles { Solid=0, Dashed=1 };
+ EdgeInfo(int color,int style,const QCString &lab,const QCString &url,int labColor)
+ : m_color(color), m_style(style), m_label(lab), m_url(url), m_labColor(labColor) {}
+ ~EdgeInfo() {}
+ int color() const { return m_color; }
+ int style() const { return m_style; }
+ QCString label() const { return m_label; }
+ QCString url() const { return m_url; }
+ int labelColor() const { return m_labColor; }
+ private:
+ int m_color;
+ int m_style;
+ QCString m_label;
+ QCString m_url;
+ int m_labColor;
+};
+
+/** A node in a dot graph */
+class DotNode
+{
+ public:
+ static void deleteNodes(DotNode* node, SDict<DotNode>* skipNodes = 0);
+ static QCString convertLabel(const QCString& l);
+ DotNode(int n,const char *lab,const char *tip,const char *url,
+ bool rootNode=FALSE,const ClassDef *cd=0);
+ ~DotNode();
+
+ enum TruncState { Unknown, Truncated, Untruncated };
+
+ void addChild(DotNode *n,
+ int edgeColor=EdgeInfo::Purple,
+ int edgeStyle=EdgeInfo::Solid,
+ const char *edgeLab=0,
+ const char *edgeURL=0,
+ int edgeLabCol=-1);
+ void addParent(DotNode *n);
+ void deleteNode(DotNodeList &deletedList,SDict<DotNode> *skipNodes=0);
+ void removeChild(DotNode *n);
+ void removeParent(DotNode *n);
+ int findParent( DotNode *n );
+
+ void write(FTextStream &t,GraphType gt,GraphOutputFormat f,
+ bool topDown,bool toChildren,bool backArrows) const;
+ void writeXML(FTextStream &t,bool isClassGraph) const;
+ void writeDocbook(FTextStream &t,bool isClassGraph) const;
+ void writeDEF(FTextStream &t) const;
+ void writeBox(FTextStream &t,GraphType gt,GraphOutputFormat f,
+ bool hasNonReachableChildren) const;
+ void writeArrow(FTextStream &t,GraphType gt,GraphOutputFormat f,const DotNode *cn,
+ const EdgeInfo *ei,bool topDown, bool pointBack=TRUE) const;
+
+ QCString label() const { return m_label; }
+ int number() const { return m_number; }
+ bool isVisible() const { return m_visible; }
+ TruncState isTruncated() const { return m_truncated; }
+ int distance() const { return m_distance; }
+ int subgraphId() const { return m_subgraphId; }
+ bool isRenumbered() const { return m_renumbered; }
+ bool hasDocumentation() const { return m_hasDoc; }
+ bool isWritten() const { return m_written; }
+
+ void clearWriteFlag();
+ void renumberNodes(int &number);
+ void markRenumbered() { m_renumbered = true; }
+ void markHasDocumentation() { m_hasDoc = true; }
+ void setSubgraphId(int id) { m_subgraphId = id; }
+
+ void colorConnectedNodes(int curColor);
+ void setDistance(int distance);
+ const DotNode *findDocNode() const; // only works for acyclic graphs!
+ void markAsVisible(bool b=TRUE) { m_visible=b; }
+ void markAsTruncated(bool b=TRUE) { m_truncated=b ? Truncated : Untruncated; }
+ const QList<DotNode> *children() const { return m_children; }
+ const QList<DotNode> *parents() const { return m_parents; }
+ const QList<EdgeInfo> *edgeInfo() const { return m_edgeInfo; }
+
+ private:
+ int m_number;
+ QCString m_label; //!< label text
+ QCString m_tooltip; //!< node's tooltip
+ QCString m_url; //!< url of the node (format: remote$local)
+ QList<DotNode> *m_parents; //!< list of parent nodes (incoming arrows)
+ QList<DotNode> *m_children; //!< list of child nodes (outgoing arrows)
+ QList<EdgeInfo> *m_edgeInfo; //!< edge info for each child
+ bool m_deleted; //!< used to mark a node as deleted
+ mutable bool m_written; //!< used to mark a node as written
+ bool m_hasDoc; //!< used to mark a node as documented
+ bool m_isRoot; //!< indicates if this is a root node
+ const ClassDef * m_classDef; //!< class representing this node (can be 0)
+ bool m_visible; //!< is the node visible in the output
+ TruncState m_truncated; //!< does the node have non-visible children/parents
+ int m_distance; //!< shortest path to the root node
+ bool m_renumbered;//!< indicates if the node has been renumbered (to prevent endless loops)
+ int m_subgraphId;
+};
+
+/** Class representing a list of DotNode objects. */
+class DotNodeList : public QList<DotNode>
+{
+ public:
+ DotNodeList() : QList<DotNode>() {}
+ ~DotNodeList() {}
+ private:
+ int compareValues(const DotNode *n1,const DotNode *n2) const;
+};
+
+#endif
diff --git a/src/dotrunner.cpp b/src/dotrunner.cpp
new file mode 100644
index 0000000..22a0081
--- /dev/null
+++ b/src/dotrunner.cpp
@@ -0,0 +1,294 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#include "dotrunner.h"
+
+#include "util.h"
+#include "portable.h"
+#include "dot.h"
+#include "message.h"
+#include "ftextstream.h"
+#include "config.h"
+
+// the graphicx LaTeX has a limitation of maximum size of 16384
+// To be on the save side we take it a little bit smaller i.e. 150 inch * 72 dpi
+// It is anyway hard to view these size of images
+#define MAX_LATEX_GRAPH_INCH 150
+#define MAX_LATEX_GRAPH_SIZE (MAX_LATEX_GRAPH_INCH * 72)
+
+
+// since dot silently reproduces the input file when it does not
+// support the PNG format, we need to check the result.
+static void checkPngResult(const char *imgName)
+{
+ FILE *f = portable_fopen(imgName,"rb");
+ if (f)
+ {
+ char data[4];
+ if (fread(data,1,4,f)==4)
+ {
+ if (!(data[1]=='P' && data[2]=='N' && data[3]=='G'))
+ {
+ err("Image `%s' produced by dot is not a valid PNG!\n"
+ "You should either select a different format "
+ "(DOT_IMAGE_FORMAT in the config file) or install a more "
+ "recent version of graphviz (1.7+)\n",imgName
+ );
+ }
+ }
+ else
+ {
+ err("Could not read image `%s' generated by dot!\n",imgName);
+ }
+ fclose(f);
+ }
+ else
+ {
+ err("Could not open image `%s' generated by dot!\n",imgName);
+ }
+}
+
+static bool resetPDFSize(const int width,const int height, const char *base)
+{
+ QString tmpName = QString::fromUtf8(QCString(base)+".tmp");
+ QString patchFile = QString::fromUtf8(QCString(base)+".dot");
+ if (!QDir::current().rename(patchFile,tmpName))
+ {
+ err("Failed to rename file %s to %s!\n",patchFile.data(),tmpName.data());
+ return FALSE;
+ }
+ QFile fi(tmpName);
+ QFile fo(patchFile);
+ if (!fi.open(IO_ReadOnly))
+ {
+ err("problem opening file %s for patching!\n",tmpName.data());
+ QDir::current().rename(tmpName,patchFile);
+ return FALSE;
+ }
+ if (!fo.open(IO_WriteOnly))
+ {
+ err("problem opening file %s for patching!\n",patchFile.data());
+ QDir::current().rename(tmpName,patchFile);
+ fi.close();
+ return FALSE;
+ }
+ FTextStream t(&fo);
+ const int maxLineLen=100*1024;
+ while (!fi.atEnd()) // foreach line
+ {
+ QCString line(maxLineLen);
+ int numBytes = fi.readLine(line.rawData(),maxLineLen);
+ if (numBytes<=0)
+ {
+ break;
+ }
+ line.resize(numBytes+1);
+ if (line.find("LATEX_PDF_SIZE") != -1)
+ {
+ double scale = (width > height ? width : height)/double(MAX_LATEX_GRAPH_INCH);
+ t << " size=\""<<width/scale << "," <<height/scale <<"\";\n";
+ }
+ else
+ t << line;
+ }
+ fi.close();
+ fo.close();
+ // remove temporary file
+ QDir::current().remove(tmpName);
+ return TRUE;
+}
+
+bool DotRunner::readBoundingBox(const char *fileName,int *width,int *height,bool isEps)
+{
+ const char *bb = isEps ? "%%PageBoundingBox:" : "/MediaBox [";
+ int bblen = strlen(bb);
+ FILE *f = portable_fopen(fileName,"rb");
+ if (!f)
+ {
+ //printf("readBoundingBox: could not open %s\n",fileName);
+ return FALSE;
+ }
+ const int maxLineLen=1024;
+ char buf[maxLineLen];
+ while (fgets(buf,maxLineLen,f)!=NULL)
+ {
+ const char *p = strstr(buf,bb);
+ if (p) // found PageBoundingBox or /MediaBox string
+ {
+ int x,y;
+ fclose(f);
+ if (sscanf(p+bblen,"%d %d %d %d",&x,&y,width,height)!=4)
+ {
+ //printf("readBoundingBox sscanf fail\n");
+ return FALSE;
+ }
+ return TRUE;
+ }
+ }
+ err("Failed to extract bounding box from generated diagram file %s\n",fileName);
+ fclose(f);
+ return FALSE;
+}
+
+bool DotRunner::DOT_CLEANUP;
+bool DotRunner::DOT_MULTI_TARGETS;
+DotConstString DotRunner::DOT_EXE;
+
+DotRunner::DotRunner(const QCString& absDotName, const QCString& md5Hash)
+ : m_file(absDotName), m_md5Hash(md5Hash), m_cleanUp(DOT_CLEANUP)
+{
+ m_jobs.setAutoDelete(TRUE);
+}
+
+void DotRunner::addJob(const char *format,const char *output)
+{
+ QListIterator<DotJob> li(m_jobs);
+ DotJob *s;
+ for (li.toFirst(); (s = li.current()); ++li)
+ {
+ if (qstrcmp(s->format.data(), format) != 0) continue;
+ if (qstrcmp(s->output.data(), output) != 0) continue;
+ // we have this job already
+ return;
+ }
+ QCString args = QCString("-T")+format+" -o \""+output+"\"";
+ m_jobs.append(new DotJob(format, output, args));
+}
+
+QCString getBaseNameOfOutput(QCString const& output)
+{
+ int index = output.findRev('.');
+ if (index < 0) return output;
+ return output.left(index);
+}
+
+bool DotRunner::run()
+{
+ int exitCode=0;
+
+ QCString dotArgs;
+ QListIterator<DotJob> li(m_jobs);
+ DotJob *s;
+
+ // create output
+ if (DOT_MULTI_TARGETS)
+ {
+ dotArgs=QCString("\"")+m_file.data()+"\"";
+ for (li.toFirst();(s=li.current());++li)
+ {
+ dotArgs+=' ';
+ dotArgs+=s->args.data();
+ }
+ if ((exitCode=portable_system(DOT_EXE.data(),dotArgs,FALSE))!=0) goto error;
+ }
+ else
+ {
+ for (li.toFirst();(s=li.current());++li)
+ {
+ dotArgs=QCString("\"")+m_file.data()+"\" "+s->args.data();
+ if ((exitCode=portable_system(DOT_EXE.data(),dotArgs,FALSE))!=0) goto error;
+ }
+ }
+
+ // check output
+ // As there should be only one pdf file be generated, we don't need code for regenerating multiple pdf files in one call
+ for (li.toFirst();(s=li.current());++li)
+ {
+ if (qstrncmp(s->format.data(), "pdf", 3) == 0)
+ {
+ int width=0,height=0;
+ if (!readBoundingBox(s->output.data(),&width,&height,FALSE)) goto error;
+ if ((width > MAX_LATEX_GRAPH_SIZE) || (height > MAX_LATEX_GRAPH_SIZE))
+ {
+ if (!resetPDFSize(width,height,getBaseNameOfOutput(s->output.data()))) goto error;
+ dotArgs=QCString("\"")+m_file.data()+"\" "+s->args.data();
+ if ((exitCode=portable_system(DOT_EXE.data(),dotArgs,FALSE))!=0) goto error;
+ }
+ }
+
+ if (qstrncmp(s->format.data(), "png", 3) == 0)
+ {
+ checkPngResult(s->output.data());
+ }
+ }
+
+ // remove .dot files
+ if (m_cleanUp)
+ {
+ //printf("removing dot file %s\n",m_file.data());
+ portable_unlink(m_file.data());
+ }
+
+ // create checksum file
+ if (!m_md5Hash.isEmpty())
+ {
+ QCString md5Name = getBaseNameOfOutput(m_file.data()) + ".md5";
+ FILE *f = portable_fopen(md5Name,"w");
+ if (f)
+ {
+ fwrite(m_md5Hash.data(),1,32,f);
+ fclose(f);
+ }
+ }
+ return TRUE;
+error:
+ err("Problems running dot: exit code=%d, command='%s', arguments='%s'\n",
+ exitCode,DOT_EXE.data(),dotArgs.data());
+ return FALSE;
+}
+
+
+//--------------------------------------------------------------------
+
+void DotRunnerQueue::enqueue(DotRunner *runner)
+{
+ QMutexLocker locker(&m_mutex);
+ m_queue.enqueue(runner);
+ m_bufferNotEmpty.wakeAll();
+}
+
+DotRunner *DotRunnerQueue::dequeue()
+{
+ QMutexLocker locker(&m_mutex);
+ while (m_queue.isEmpty())
+ {
+ // wait until something is added to the queue
+ m_bufferNotEmpty.wait(&m_mutex);
+ }
+ DotRunner *result = m_queue.dequeue();
+ return result;
+}
+
+uint DotRunnerQueue::count() const
+{
+ QMutexLocker locker(&m_mutex);
+ return m_queue.count();
+}
+
+//--------------------------------------------------------------------
+
+DotWorkerThread::DotWorkerThread(DotRunnerQueue *queue)
+ : m_queue(queue)
+{
+}
+
+void DotWorkerThread::run()
+{
+ DotRunner *runner;
+ while ((runner=m_queue->dequeue()))
+ {
+ runner->run();
+ }
+}
diff --git a/src/dotrunner.h b/src/dotrunner.h
new file mode 100644
index 0000000..4128fe8
--- /dev/null
+++ b/src/dotrunner.h
@@ -0,0 +1,138 @@
+/******************************************************************************
+*
+* Copyright (C) 1997-2019 by Dimitri van Heesch.
+*
+* Permission to use, copy, modify, and distribute this software and its
+* documentation under the terms of the GNU General Public License is hereby
+* granted. No representations are made about the suitability of this software
+* for any purpose. It is provided "as is" without express or implied warranty.
+* See the GNU General Public License for more details.
+*
+* Documents produced by Doxygen are derivative works derived from the
+* input used in their production; they are not affected by this license.
+*
+*/
+
+#ifndef DOTRUNNER_H
+#define DOTRUNNER_H
+
+#include "qcstring.h"
+#include "qlist.h"
+#include "qwaitcondition.h"
+#include "qthread.h"
+#include "qqueue.h"
+#include "qmutex.h"
+
+/** Minimal constant string class that is thread safe, once initialized. */
+class DotConstString
+{
+ public:
+ DotConstString() { m_str=0;}
+ ~DotConstString() { delete[] m_str;}
+ DotConstString(char const* s) : m_str(0) { set(s); }
+ DotConstString(const QCString &s) : m_str(0) { set(s); }
+ DotConstString(const DotConstString &s) : m_str(0) { set(s.data()); }
+ const char *data() const { return m_str; }
+ bool isEmpty() const { return m_str==0 || m_str[0]=='\0'; }
+ void init(const char *s) { set(s); }
+
+ private:
+ void set(char const* s)
+ {
+ delete[] m_str;
+ m_str=0;
+ if (s)
+ {
+ m_str=new char[strlen(s) + 1];
+ qstrcpy(m_str,s);
+ }
+ }
+
+ void set(const QCString &s)
+ {
+ delete[] m_str;
+ m_str=0;
+ if (!s.isEmpty())
+ {
+ m_str=new char[s.length()+1];
+ qstrcpy(m_str,s.data());
+ }
+ }
+
+ DotConstString &operator=(const DotConstString &);
+
+ char *m_str;
+};
+
+/** Helper class to run dot from doxygen from multiple threads. */
+class DotRunner
+{
+ public:
+ struct DotJob
+ {
+ DotJob(const DotConstString & format,
+ const DotConstString & output,
+ const DotConstString & args)
+ : format(format), output(output), args(args) {}
+ DotConstString format;
+ DotConstString output;
+ DotConstString args;
+ };
+
+ /** Creates a runner for a dot \a file. */
+ DotRunner(const QCString& absDotName, const QCString& md5Hash);
+
+ /** Adds an additional job to the run.
+ * Performing multiple jobs one file can be faster.
+ */
+ void addJob(const char *format,const char *output);
+
+ /** Prevent cleanup of the dot file (for user provided dot files) */
+ void preventCleanUp() { m_cleanUp = FALSE; }
+
+ /** Runs dot for all jobs added. */
+ bool run();
+
+ // DotConstString const& getFileName() { return m_file; }
+ DotConstString const& getMd5Hash() { return m_md5Hash; }
+
+ static bool readBoundingBox(const char* fileName, int* width, int* height, bool isEps);
+
+ private:
+ DotConstString m_file;
+ DotConstString m_md5Hash;
+ bool m_cleanUp;
+ QList<DotJob> m_jobs;
+
+ static bool DOT_CLEANUP;
+ static bool DOT_MULTI_TARGETS;
+ static DotConstString DOT_EXE;
+ friend void initDot();
+
+};
+
+/** Queue of dot jobs to run. */
+// all methods are thread save
+class DotRunnerQueue
+{
+ public:
+ void enqueue(DotRunner *runner);
+ DotRunner *dequeue();
+ uint count() const;
+ private:
+ QWaitCondition m_bufferNotEmpty;
+ QQueue<DotRunner> m_queue;
+ mutable QMutex m_mutex;
+};
+
+/** Worker thread to execute a dot run */
+class DotWorkerThread : public QThread
+{
+ public:
+ DotWorkerThread(DotRunnerQueue *queue);
+ void run();
+ private:
+ DotRunnerQueue *m_queue;
+};
+
+#endif
diff --git a/src/doxygen.cpp b/src/doxygen.cpp
index 30c2001..55b601c 100644
--- a/src/doxygen.cpp
+++ b/src/doxygen.cpp
@@ -174,6 +174,7 @@ QCString Doxygen::spaces;
bool Doxygen::generatingXmlOutput = FALSE;
bool Doxygen::markdownSupport = TRUE;
GenericsSDict *Doxygen::genericsDict;
+DocGroup Doxygen::docGroup;
// locally accessible globals
static QDict<Entry> g_classEntries(1009);
@@ -5551,7 +5552,7 @@ static bool findGlobalMember(Entry *root,
NamespaceDef *rnd = 0;
if (!namespaceName.isEmpty()) rnd = Doxygen::namespaceSDict->find(namespaceName);
- ArgumentList *mdAl = md->argumentList();
+ const ArgumentList *mdAl = const_cast<const MemberDef *>(md)->argumentList();
bool matching=
(mdAl==0 && root->argList->count()==0) ||
md->isVariable() || md->isTypedef() || /* in case of function pointers */
@@ -6721,10 +6722,10 @@ static void findMember(Entry *root,
if (rmn)
{
MemberNameIterator rmni(*rmn);
- MemberDef *rmd;
+ const MemberDef *rmd;
while ((rmd=rmni.current()) && !found) // see if we got another member with matching arguments
{
- ArgumentList *rmdAl = rmd->argumentList();
+ const ArgumentList *rmdAl = rmd->argumentList();
// check for matching argument lists
if (
matchArguments2(rmd->getOuterScope(),rmd->getFileDef(),rmdAl,
@@ -10146,7 +10147,7 @@ static void usage(const char *name)
msg(" RTF: %s -e rtf extensionsFile\n\n",name);
msg("7) Use doxygen to compare the used configuration file with the template configuration file\n");
msg(" %s -x [configFile]\n\n",name);
- msg("8) Use doxygen to show a list of build in emoji.\n");
+ msg("8) Use doxygen to show a list of built-in emojis.\n");
msg(" %s -f emoji outputFileName\n\n",name);
msg(" If - is used for outputFileName doxygen will write to standard output.\n\n");
msg("If -s is specified the comments of the configuration items in the config file will be omitted.\n");
@@ -11270,9 +11271,7 @@ void parseInput()
if (layoutFile.open(IO_ReadOnly))
{
msg("Parsing layout file %s...\n",layoutFileName.data());
- QTextStream t(&layoutFile);
- t.setEncoding(QTextStream::Latin1);
- LayoutDocManager::instance().parse(t,layoutFileName);
+ LayoutDocManager::instance().parse(layoutFileName);
}
else if (!defaultLayoutUsed)
{
@@ -11652,6 +11651,7 @@ void generateOutput()
}
initSearchIndexer();
+ initDot();
bool generateHtml = Config_getBool(GENERATE_HTML);
bool generateLatex = Config_getBool(GENERATE_LATEX);
diff --git a/src/doxygen.h b/src/doxygen.h
index 4ff8a56..f5ad0bb 100644
--- a/src/doxygen.h
+++ b/src/doxygen.h
@@ -27,6 +27,7 @@
#include "membergroup.h"
#include "dirdef.h"
#include "memberlist.h"
+#include "docgroup.h"
class RefList;
class PageSList;
@@ -150,6 +151,7 @@ class Doxygen
static bool generatingXmlOutput;
static bool markdownSupport;
static GenericsSDict *genericsDict;
+ static DocGroup docGroup;
};
void initDoxygen();
diff --git a/src/filedef.cpp b/src/filedef.cpp
index f721c9f..7a3323d 100644
--- a/src/filedef.cpp
+++ b/src/filedef.cpp
@@ -26,6 +26,7 @@
#include "language.h"
#include "outputlist.h"
#include "dot.h"
+#include "dotincldepgraph.h"
#include "message.h"
#include "docparser.h"
#include "searchindex.h"
diff --git a/src/fortranscanner.l b/src/fortranscanner.l
index 0ad03e3..1076e68 100644
--- a/src/fortranscanner.l
+++ b/src/fortranscanner.l
@@ -2304,7 +2304,7 @@ static void initEntry()
current->virt = virt;
current->stat = gstat;
current->lang = SrcLangExt_Fortran;
- initGroupInfo(current);
+ Doxygen::docGroup.initGroupInfo(current);
}
/**
@@ -2712,7 +2712,7 @@ static void parseMain(const char *fileName,const char *fileBuf,Entry *rt, Fortra
global_scope = rt;
startScope(rt); // implies current_root = rt
initParser();
- groupEnterFile(yyFileName,yyLineNr);
+ Doxygen::docGroup.enterFile(yyFileName,yyLineNr);
current = new Entry;
current->lang = SrcLangExt_Fortran;
@@ -2729,7 +2729,7 @@ static void parseMain(const char *fileName,const char *fileBuf,Entry *rt, Fortra
}
fortranscannerYYlex();
- groupLeaveFile(yyFileName,yyLineNr);
+ Doxygen::docGroup.leaveFile(yyFileName,yyLineNr);
if (global_scope && global_scope != (Entry *) -1) endScope(current_root, TRUE); // TRUE - global root
diff --git a/src/groupdef.cpp b/src/groupdef.cpp
index 01c4cc4..9525053 100644
--- a/src/groupdef.cpp
+++ b/src/groupdef.cpp
@@ -33,6 +33,7 @@
#include "docparser.h"
#include "searchindex.h"
#include "dot.h"
+#include "dotgroupcollaboration.h"
#include "vhdldocgen.h"
#include "layout.h"
#include "arguments.h"
diff --git a/src/htags.cpp b/src/htags.cpp
index 7f5870d..51cd6d9 100644
--- a/src/htags.cpp
+++ b/src/htags.cpp
@@ -91,6 +91,10 @@ bool Htags::execute(const QCString &htmldir)
//printf("CommandLine=[%s]\n",commandLine.data());
portable_sysTimerStart();
bool result=portable_system("htags",commandLine,FALSE)==0;
+ if (!result)
+ {
+ err("Problems running %s. Check your installation\n", "htags");
+ }
portable_sysTimerStop();
QDir::setCurrent(oldDir);
return result;
diff --git a/src/htmldocvisitor.cpp b/src/htmldocvisitor.cpp
index e7d9d57..05cdc42 100644
--- a/src/htmldocvisitor.cpp
+++ b/src/htmldocvisitor.cpp
@@ -784,7 +784,9 @@ void HtmlDocVisitor::visit(DocIncOperator *op)
pushEnabled();
m_hide=TRUE;
}
- SrcLangExt langExt = getLanguageFromFileName(m_langExt);
+ QCString locLangExt = getFileNameExtension(op->includeFileName());
+ if (locLangExt.isEmpty()) locLangExt = m_langExt;
+ SrcLangExt langExt = getLanguageFromFileName(locLangExt);
if (op->type()!=DocIncOperator::Skip)
{
popEnabled();
@@ -796,7 +798,7 @@ void HtmlDocVisitor::visit(DocIncOperator *op)
QFileInfo cfi( op->includeFileName() );
fd = createFileDef( cfi.dirPath().utf8(), cfi.fileName().utf8() );
}
- Doxygen::parserManager->getParser(m_langExt)
+ Doxygen::parserManager->getParser(locLangExt)
->parseCode(
m_ci,
op->context(),
@@ -2116,14 +2118,6 @@ void HtmlDocVisitor::visitPost(DocInternalRef *)
m_t << " ";
}
-void HtmlDocVisitor::visitPre(DocCopy *)
-{
-}
-
-void HtmlDocVisitor::visitPost(DocCopy *)
-{
-}
-
void HtmlDocVisitor::visitPre(DocText *)
{
}
diff --git a/src/htmldocvisitor.h b/src/htmldocvisitor.h
index 1c08c03..c994bac 100644
--- a/src/htmldocvisitor.h
+++ b/src/htmldocvisitor.h
@@ -127,8 +127,6 @@ class HtmlDocVisitor : public DocVisitor
void visitPost(DocXRefItem *);
void visitPre(DocInternalRef *);
void visitPost(DocInternalRef *);
- void visitPre(DocCopy *);
- void visitPost(DocCopy *);
void visitPre(DocText *);
void visitPost(DocText *);
void visitPre(DocHtmlBlockQuote *);
diff --git a/src/htmlgen.cpp b/src/htmlgen.cpp
index 0067fa1..b3abd09 100644
--- a/src/htmlgen.cpp
+++ b/src/htmlgen.cpp
@@ -28,6 +28,12 @@
#include "diagram.h"
#include "version.h"
#include "dot.h"
+#include "dotcallgraph.h"
+#include "dotclassgraph.h"
+#include "dotdirdeps.h"
+#include "dotgfxhierarchytable.h"
+#include "dotgroupcollaboration.h"
+#include "dotincldepgraph.h"
#include "language.h"
#include "htmlhelp.h"
#include "docparser.h"
@@ -51,6 +57,7 @@ static QCString g_header;
static QCString g_footer;
static QCString g_mathjax_code;
+static bool DoxyCodeLineOpen = FALSE;
// note: this is only active if DISABLE_INDEX=YES, if DISABLE_INDEX is disabled, this
// part will be rendered inside menu.js
@@ -524,7 +531,12 @@ void HtmlCodeGenerator::writeLineNumber(const char *ref,const char *filename,
qsnprintf(lineNumber,maxLineNrStr,"%5d",l);
qsnprintf(lineAnchor,maxLineNrStr,"l%05d",l);
- m_t << "<div class=\"line\">";
+ if (!DoxyCodeLineOpen)
+ {
+ m_t << "<div class=\"line\">";
+ DoxyCodeLineOpen = TRUE;
+ }
+
m_t << "<a name=\"" << lineAnchor << "\"></a><span class=\"lineno\">";
if (filename)
{
@@ -655,12 +667,16 @@ void HtmlCodeGenerator::writeTooltip(const char *id, const DocLinkInfo &docInfo,
}
-void HtmlCodeGenerator::startCodeLine(bool hasLineNumbers)
+void HtmlCodeGenerator::startCodeLine(bool)
{
if (m_streamSet)
{
- if (!hasLineNumbers) m_t << "<div class=\"line\">";
m_col=0;
+ if (!DoxyCodeLineOpen)
+ {
+ m_t << "<div class=\"line\">";
+ DoxyCodeLineOpen = TRUE;
+ }
}
}
@@ -673,7 +689,11 @@ void HtmlCodeGenerator::endCodeLine()
m_t << " ";
m_col++;
}
- m_t << "</div>";
+ if (DoxyCodeLineOpen)
+ {
+ m_t << "</div>\n";
+ DoxyCodeLineOpen = FALSE;
+ }
}
}
@@ -1775,7 +1795,7 @@ void HtmlGenerator::startDotGraph()
startSectionHeader(t,relPath,m_sectionCount);
}
-void HtmlGenerator::endDotGraph(const DotClassGraph &g)
+void HtmlGenerator::endDotGraph(DotClassGraph &g)
{
bool generateLegend = Config_getBool(GENERATE_LEGEND);
bool umlLook = Config_getBool(UML_LOOK);
@@ -1803,7 +1823,7 @@ void HtmlGenerator::startInclDepGraph()
startSectionHeader(t,relPath,m_sectionCount);
}
-void HtmlGenerator::endInclDepGraph(const DotInclDepGraph &g)
+void HtmlGenerator::endInclDepGraph(DotInclDepGraph &g)
{
endSectionHeader(t);
startSectionSummary(t,m_sectionCount);
@@ -1821,7 +1841,7 @@ void HtmlGenerator::startGroupCollaboration()
startSectionHeader(t,relPath,m_sectionCount);
}
-void HtmlGenerator::endGroupCollaboration(const DotGroupCollaboration &g)
+void HtmlGenerator::endGroupCollaboration(DotGroupCollaboration &g)
{
endSectionHeader(t);
startSectionSummary(t,m_sectionCount);
@@ -1839,7 +1859,7 @@ void HtmlGenerator::startCallGraph()
startSectionHeader(t,relPath,m_sectionCount);
}
-void HtmlGenerator::endCallGraph(const DotCallGraph &g)
+void HtmlGenerator::endCallGraph(DotCallGraph &g)
{
endSectionHeader(t);
startSectionSummary(t,m_sectionCount);
@@ -1857,7 +1877,7 @@ void HtmlGenerator::startDirDepGraph()
startSectionHeader(t,relPath,m_sectionCount);
}
-void HtmlGenerator::endDirDepGraph(const DotDirDeps &g)
+void HtmlGenerator::endDirDepGraph(DotDirDeps &g)
{
endSectionHeader(t);
startSectionSummary(t,m_sectionCount);
@@ -1870,7 +1890,7 @@ void HtmlGenerator::endDirDepGraph(const DotDirDeps &g)
m_sectionCount++;
}
-void HtmlGenerator::writeGraphicalHierarchy(const DotGfxHierarchyTable &g)
+void HtmlGenerator::writeGraphicalHierarchy(DotGfxHierarchyTable &g)
{
g.writeGraph(t,dir,fileName);
}
@@ -2634,6 +2654,19 @@ void HtmlGenerator::endConstraintList()
t << "</div>" << endl;
}
+void HtmlGenerator::startCodeFragment()
+{
+ t << PREFRAG_START;
+}
+
+void HtmlGenerator::endCodeFragment()
+{
+ //endCodeLine checks is there is still an open code line, if so closes it.
+ endCodeLine();
+
+ t << PREFRAG_END;
+}
+
void HtmlGenerator::lineBreak(const char *style)
{
if (style)
diff --git a/src/htmlgen.h b/src/htmlgen.h
index 4bf975d..2db5b74 100644
--- a/src/htmlgen.h
+++ b/src/htmlgen.h
@@ -212,8 +212,8 @@ class HtmlGenerator : public OutputGenerator
void writeRuler() { t << "<hr/>"; }
void writeAnchor(const char *,const char *name)
{ t << "<a name=\"" << name <<"\" id=\"" << name << "\"></a>"; }
- void startCodeFragment() { t << PREFRAG_START; }
- void endCodeFragment() { t << PREFRAG_END; }
+ void startCodeFragment();
+ void endCodeFragment();
void startEmphasis() { t << "<em>"; }
void endEmphasis() { t << "</em>"; }
void startBold() { t << "<b>"; }
@@ -283,16 +283,16 @@ class HtmlGenerator : public OutputGenerator
void endDescTableData();
void startDotGraph();
- void endDotGraph(const DotClassGraph &g);
+ void endDotGraph(DotClassGraph &g);
void startInclDepGraph();
- void endInclDepGraph(const DotInclDepGraph &g);
+ void endInclDepGraph(DotInclDepGraph &g);
void startGroupCollaboration();
- void endGroupCollaboration(const DotGroupCollaboration &g);
+ void endGroupCollaboration(DotGroupCollaboration &g);
void startCallGraph();
- void endCallGraph(const DotCallGraph &g);
+ void endCallGraph(DotCallGraph &g);
void startDirDepGraph();
- void endDirDepGraph(const DotDirDeps &g);
- void writeGraphicalHierarchy(const DotGfxHierarchyTable &g);
+ void endDirDepGraph(DotDirDeps &g);
+ void writeGraphicalHierarchy(DotGfxHierarchyTable &g);
void startTextBlock(bool)
{ t << "<div class=\"textblock\">"; }
diff --git a/src/index.cpp b/src/index.cpp
index a577d9a..5d6c0a5 100644
--- a/src/index.cpp
+++ b/src/index.cpp
@@ -40,6 +40,7 @@
#include "htmlhelp.h"
#include "ftvhelp.h"
#include "dot.h"
+#include "dotgfxhierarchytable.h"
#include "pagedef.h"
#include "dirdef.h"
#include "vhdldocgen.h"
@@ -4730,7 +4731,7 @@ static void writeIndex(OutputList &ol)
ol.parseText(/*projPrefix+*/theTranslator->trExceptionIndex());
ol.endIndexSection(isCompoundIndex);
}
- if (documentedFiles>0)
+ if (Config_getBool(SHOW_FILES) && (documentedFiles>0))
{
ol.startIndexSection(isFileIndex);
ol.parseText(/*projPrefix+*/theTranslator->trFileIndex());
diff --git a/src/latexdocvisitor.cpp b/src/latexdocvisitor.cpp
index 2e979bd..9652580 100644
--- a/src/latexdocvisitor.cpp
+++ b/src/latexdocvisitor.cpp
@@ -566,20 +566,22 @@ void LatexDocVisitor::visit(DocIncOperator *op)
pushEnabled();
m_hide = TRUE;
}
- SrcLangExt langExt = getLanguageFromFileName(m_langExt);
+ QCString locLangExt = getFileNameExtension(op->includeFileName());
+ if (locLangExt.isEmpty()) locLangExt = m_langExt;
+ SrcLangExt langExt = getLanguageFromFileName(locLangExt);
if (op->type()!=DocIncOperator::Skip)
{
popEnabled();
if (!m_hide)
{
- FileDef *fd;
+ FileDef *fd = 0;
if (!op->includeFileName().isEmpty())
{
QFileInfo cfi( op->includeFileName() );
fd = createFileDef( cfi.dirPath().utf8(), cfi.fileName().utf8() );
}
- Doxygen::parserManager->getParser(m_langExt)
+ Doxygen::parserManager->getParser(locLangExt)
->parseCode(m_ci,op->context(),op->text(),langExt,
op->isExample(),op->exampleFile(),
fd, // fileDef
@@ -1696,14 +1698,6 @@ void LatexDocVisitor::visitPost(DocInternalRef *ref)
endLink(0,ref->file(),ref->anchor());
}
-void LatexDocVisitor::visitPre(DocCopy *)
-{
-}
-
-void LatexDocVisitor::visitPost(DocCopy *)
-{
-}
-
void LatexDocVisitor::visitPre(DocText *)
{
}
diff --git a/src/latexdocvisitor.h b/src/latexdocvisitor.h
index 7ea8ae1..71fb5be 100644
--- a/src/latexdocvisitor.h
+++ b/src/latexdocvisitor.h
@@ -128,8 +128,6 @@ class LatexDocVisitor : public DocVisitor
void visitPost(DocXRefItem *);
void visitPre(DocInternalRef *);
void visitPost(DocInternalRef *);
- void visitPre(DocCopy *);
- void visitPost(DocCopy *);
void visitPre(DocText *);
void visitPost(DocText *);
void visitPre(DocHtmlBlockQuote *);
diff --git a/src/latexgen.cpp b/src/latexgen.cpp
index ad983a2..f88f79b 100644
--- a/src/latexgen.cpp
+++ b/src/latexgen.cpp
@@ -27,6 +27,11 @@
#include "language.h"
#include "version.h"
#include "dot.h"
+#include "dotcallgraph.h"
+#include "dotclassgraph.h"
+#include "dotdirdeps.h"
+#include "dotgroupcollaboration.h"
+#include "dotincldepgraph.h"
#include "pagedef.h"
#include "docparser.h"
#include "latexdocvisitor.h"
@@ -480,7 +485,7 @@ static void writeDefaultHeaderPart1(FTextStream &t)
if (Config_getBool(LATEX_BATCHMODE))
t << "\\batchmode\n";
- // to overcome problems wit too many open files
+ // to overcome problems with too many open files
t << "\\let\\mypdfximage\\pdfximage"
"\\def\\pdfximage{\\immediate\\mypdfximage}";
@@ -492,6 +497,14 @@ static void writeDefaultHeaderPart1(FTextStream &t)
documentClass = "book";
t << "\\documentclass[twoside]{" << documentClass << "}\n"
"\n";
+ t << "%% moved from doxygen.sty due to workaround for LaTex 2019 version and unmaintained tabu package\n"
+ "\\usepackage{ifthen}\n"
+ "\\ifx\\requestedLaTeXdate\\undefined\n"
+ "\\usepackage{array}\n"
+ "\\else\n"
+ "\\usepackage{array}[=2016-10-06]\n"
+ "\\fi\n"
+ "%%\n";
// Load required packages
t << "% Packages required by doxygen\n"
@@ -1324,6 +1337,15 @@ void LatexGenerator::writeStyleInfo(int part)
startPlainFile("doxygen.sty");
writeDefaultStyleSheet(t);
endPlainFile();
+
+ // workaround for the problem caused by change in LaTeX in version 2019
+ // in the unmaintained tabu package
+ startPlainFile("tabu_doxygen.sty");
+ t << ResourceMgr::instance().getAsString("tabu_doxygen.sty");
+ endPlainFile();
+ startPlainFile("longtable_doxygen.sty");
+ t << ResourceMgr::instance().getAsString("longtable_doxygen.sty");
+ endPlainFile();
}
void LatexGenerator::newParagraph()
@@ -1376,7 +1398,7 @@ void LatexGenerator::startHtmlLink(const char *url)
if (Config_getBool(PDF_HYPERLINKS))
{
t << "\\href{";
- t << url;
+ t << latexFilterURL(url);
t << "}";
}
t << "{\\texttt{ ";
@@ -2034,7 +2056,7 @@ void LatexGenerator::startDotGraph()
newParagraph();
}
-void LatexGenerator::endDotGraph(const DotClassGraph &g)
+void LatexGenerator::endDotGraph(DotClassGraph &g)
{
g.writeGraph(t,GOF_EPS,EOF_LaTeX,Config_getString(LATEX_OUTPUT),fileName,relPath);
}
@@ -2043,7 +2065,7 @@ void LatexGenerator::startInclDepGraph()
{
}
-void LatexGenerator::endInclDepGraph(const DotInclDepGraph &g)
+void LatexGenerator::endInclDepGraph(DotInclDepGraph &g)
{
g.writeGraph(t,GOF_EPS,EOF_LaTeX,Config_getString(LATEX_OUTPUT),fileName,relPath);
}
@@ -2052,7 +2074,7 @@ void LatexGenerator::startGroupCollaboration()
{
}
-void LatexGenerator::endGroupCollaboration(const DotGroupCollaboration &g)
+void LatexGenerator::endGroupCollaboration(DotGroupCollaboration &g)
{
g.writeGraph(t,GOF_EPS,EOF_LaTeX,Config_getString(LATEX_OUTPUT),fileName,relPath);
}
@@ -2061,7 +2083,7 @@ void LatexGenerator::startCallGraph()
{
}
-void LatexGenerator::endCallGraph(const DotCallGraph &g)
+void LatexGenerator::endCallGraph(DotCallGraph &g)
{
g.writeGraph(t,GOF_EPS,EOF_LaTeX,Config_getString(LATEX_OUTPUT),fileName,relPath);
}
@@ -2070,7 +2092,7 @@ void LatexGenerator::startDirDepGraph()
{
}
-void LatexGenerator::endDirDepGraph(const DotDirDeps &g)
+void LatexGenerator::endDirDepGraph(DotDirDeps &g)
{
g.writeGraph(t,GOF_EPS,EOF_LaTeX,Config_getString(LATEX_OUTPUT),fileName,relPath);
}
diff --git a/src/latexgen.h b/src/latexgen.h
index b06a382..6430dbc 100644
--- a/src/latexgen.h
+++ b/src/latexgen.h
@@ -273,16 +273,16 @@ class LatexGenerator : public OutputGenerator
void lastIndexPage();
void startDotGraph();
- void endDotGraph(const DotClassGraph &);
+ void endDotGraph(DotClassGraph &);
void startInclDepGraph();
- void endInclDepGraph(const DotInclDepGraph &);
+ void endInclDepGraph(DotInclDepGraph &);
void startCallGraph();
void startGroupCollaboration();
- void endGroupCollaboration(const DotGroupCollaboration &g);
- void endCallGraph(const DotCallGraph &);
+ void endGroupCollaboration(DotGroupCollaboration &g);
+ void endCallGraph(DotCallGraph &);
void startDirDepGraph();
- void endDirDepGraph(const DotDirDeps &g);
- void writeGraphicalHierarchy(const DotGfxHierarchyTable &) {}
+ void endDirDepGraph(DotDirDeps &g);
+ void writeGraphicalHierarchy(DotGfxHierarchyTable &) {}
void startTextBlock(bool) {}
void endTextBlock(bool) {}
diff --git a/src/layout.cpp b/src/layout.cpp
index a5df6f4..4ffdfb8 100644
--- a/src/layout.cpp
+++ b/src/layout.cpp
@@ -1538,10 +1538,11 @@ void LayoutDocManager::clear(LayoutDocManager::LayoutPart p)
d->docEntries[(int)p].clear();
}
-void LayoutDocManager::parse(QTextStream &t,const char *fileName)
+void LayoutDocManager::parse(const char *fileName)
{
LayoutErrorHandler errorHandler(fileName);
- QXmlInputSource source( t );
+ QXmlInputSource source;
+ source.setData(fileToString(fileName));
QXmlSimpleReader reader;
reader.setContentHandler( &LayoutParser::instance() );
reader.setErrorHandler( &errorHandler );
diff --git a/src/layout.h b/src/layout.h
index b25aa4e..b1facf5 100644
--- a/src/layout.h
+++ b/src/layout.h
@@ -201,7 +201,7 @@ class LayoutDocManager
LayoutNavEntry *rootNavEntry() const;
/** Parses a user provided layout */
- void parse(QTextStream &t,const char *fileName);
+ void parse(const char *fileName);
void init();
private:
void addEntry(LayoutPart p,LayoutDocEntry*e);
diff --git a/src/lodepng.cpp b/src/lodepng.cpp
deleted file mode 100644
index 1906f09..0000000
--- a/src/lodepng.cpp
+++ /dev/null
@@ -1,2445 +0,0 @@
-/*
-LodePNG version 20080927
-
-Copyright (c) 2005-2008 Lode Vandevenne
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
-*/
-
-/*
-The manual and changelog can be found in the header file "lodepng.h"
-You are free to name this file lodepng.cpp or lodepng.c depending on your usage.
-*/
-
-#include "lodepng.h"
-#include "portable.h"
-
-#define USE_BRUTE_FORCE_ENCODING 1
-
-#define VERSION_STRING "20080927"
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Tools For C / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*
-About these tools (vector, uivector, ucvector and string):
--LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version.
--The string tools are made to avoid problems with compilers that declare things like strncat as deprecated.
--They're not used in the interface, only internally in this file, so all their functions are made static.
-*/
-
-//--------------------------------------------------------------------------------------------
-
-
-/*LodePNG_chunk functions: These functions need as input a large enough amount of allocated memory.*/
-
-static unsigned LodePNG_chunk_length(const unsigned char* chunk); /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/
-
-static void LodePNG_chunk_generate_crc(unsigned char* chunk); /*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/
-
-/*add chunks to out buffer. It reallocs the buffer to append the data. returns error code*/
-static unsigned LodePNG_create_chunk(unsigned char** out, size_t* outlength, unsigned length, const char* type, const unsigned char* data); /*appends new chunk to out. Returns pointer to start of appended chunk, or NULL if error happened; may change memory address of out buffer*/
-
-static void LodePNG_InfoColor_init(LodePNG_InfoColor* info);
-static void LodePNG_InfoColor_cleanup(LodePNG_InfoColor* info);
-static unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor* dest, const LodePNG_InfoColor* source);
-
-/*Use these functions instead of allocating palette manually*/
-static void LodePNG_InfoColor_clearPalette(LodePNG_InfoColor* info);
-
-/*additional color info*/
-static unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor* info); /*bits per pixel*/
-static unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor* info); /*is it a greyscale type? (colorType 0 or 4)*/
-static unsigned LodePNG_InfoColor_isAlphaType(const LodePNG_InfoColor* info); /*has it an alpha channel? (colorType 2 or 6)*/
-
-static void LodePNG_InfoPng_init(LodePNG_InfoPng* info);
-static void LodePNG_InfoPng_cleanup(LodePNG_InfoPng* info);
-static unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng* dest, const LodePNG_InfoPng* source);
-
-static void LodePNG_InfoRaw_init(LodePNG_InfoRaw* info);
-static void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw* info);
-static unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw* dest, const LodePNG_InfoRaw* source);
-
-/*
-LodePNG_convert: Converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code
-The out buffer must have (w * h * bpp + 7) / 8, where bpp is the bits per pixel of the output color type (LodePNG_InfoColor_getBpp)
-*/
-static unsigned LodePNG_convert(unsigned char* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h);
-
-static void LodeZlib_DeflateSettings_init(LodeZlib_DeflateSettings* settings);
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* LodeFlate & LodeZlib */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*This function reallocates the out buffer and appends the data.
-Either, *out must be NULL and *outsize must be 0, or, *out must be a valid buffer and *outsize its size in bytes.*/
-//unsigned LodeZlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodeZlib_DeflateSettings* settings);
-
-//--------------------------------------------------------------------------------------------
-
-typedef struct vector /*this one is used only by the deflate compressor*/
-{
- void* data;
- size_t size; /*in groups of bytes depending on type*/
- size_t allocsize; /*in bytes*/
- unsigned typesize; /*sizeof the type you store in data*/
-} vector;
-
-static unsigned vector_resize(vector* p, size_t size) /*returns 1 if success, 0 if failure ==> nothing done*/
-{
- if(size * p->typesize > p->allocsize)
- {
- size_t newsize = size * p->typesize * 2;
- void* data = realloc(p->data, newsize);
- if(data)
- {
- p->allocsize = newsize;
- p->data = data;
- p->size = size;
- }
- else return 0;
- }
- else p->size = size;
- return 1;
-}
-
-static unsigned vector_resized(vector* p, size_t size, void dtor(void*)) /*resize and use destructor on elements if it gets smaller*/
-{
- size_t i;
- if(size < p->size) for(i = size; i < p->size; i++) dtor(&((char*)(p->data))[i * p->typesize]);
- return vector_resize(p, size);
-}
-
-static void vector_cleanup(void* p)
-{
- ((vector*)p)->size = ((vector*)p)->allocsize = 0;
- free(((vector*)p)->data);
- ((vector*)p)->data = NULL;
-}
-
-static void vector_cleanupd(vector* p, void dtor(void*)) /*clear and use destructor on elements*/
-{
- vector_resized(p, 0, dtor);
- vector_cleanup(p);
-}
-
-static void vector_init(vector* p, unsigned typesize)
-{
- p->data = NULL;
- p->size = p->allocsize = 0;
- p->typesize = typesize;
-}
-
-static void vector_swap(vector* p, vector* q) /*they're supposed to have the same typesize*/
-{
- size_t tmp;
- void* tmpp;
- tmp = p->size; p->size = q->size; q->size = tmp;
- tmp = p->allocsize; p->allocsize = q->allocsize; q->allocsize = tmp;
- tmpp = p->data; p->data = q->data; q->data = tmpp;
-}
-
-static void* vector_get(vector* p, size_t index)
-{
- return &((char*)p->data)[index * p->typesize];
-}
-
-/* /////////////////////////////////////////////////////////////////////////// */
-
-typedef struct uivector
-{
- unsigned* data;
- size_t size; /*size in number of unsigned longs*/
- size_t allocsize; /*allocated size in bytes*/
-} uivector;
-
-static void uivector_cleanup(void* p)
-{
- ((uivector*)p)->size = ((uivector*)p)->allocsize = 0;
- free(((uivector*)p)->data);
- ((uivector*)p)->data = NULL;
-}
-
-static unsigned uivector_resize(uivector* p, size_t size) /*returns 1 if success, 0 if failure ==> nothing done*/
-{
- if(size * sizeof(unsigned) > p->allocsize)
- {
- size_t newsize = size * sizeof(unsigned) * 2;
- void* data = realloc(p->data, newsize);
- if(data)
- {
- p->allocsize = newsize;
- p->data = (unsigned*)data;
- p->size = size;
- }
- else return 0;
- }
- else p->size = size;
- return 1;
-}
-
-static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) /*resize and give all new elements the value*/
-{
- size_t oldsize = p->size, i;
- if(!uivector_resize(p, size)) return 0;
- for(i = oldsize; i < size; i++) p->data[i] = value;
- return 1;
-}
-
-static void uivector_init(uivector* p)
-{
- p->data = NULL;
- p->size = p->allocsize = 0;
-}
-
-static unsigned uivector_push_back(uivector* p, unsigned c) /*returns 1 if success, 0 if failure ==> nothing done*/
-{
- if(!uivector_resize(p, p->size + 1)) return 0;
- p->data[p->size - 1] = c;
- return 1;
-}
-
-static unsigned uivector_copy(uivector* p, const uivector* q) /*copy q to p, returns 1 if success, 0 if failure ==> nothing done*/
-{
- size_t i;
- if(!uivector_resize(p, q->size)) return 0;
- for(i = 0; i < q->size; i++) p->data[i] = q->data[i];
- return 1;
-}
-
-static void uivector_swap(uivector* p, uivector* q)
-{
- size_t tmp;
- unsigned* tmpp;
- tmp = p->size; p->size = q->size; q->size = tmp;
- tmp = p->allocsize; p->allocsize = q->allocsize; q->allocsize = tmp;
- tmpp = p->data; p->data = q->data; q->data = tmpp;
-}
-
-/* /////////////////////////////////////////////////////////////////////////// */
-
-typedef struct ucvector
-{
- unsigned char* data;
- size_t size; /*used size*/
- size_t allocsize; /*allocated size*/
-} ucvector;
-
-static void ucvector_cleanup(void* p)
-{
- ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0;
- free(((ucvector*)p)->data);
- ((ucvector*)p)->data = NULL;
-}
-
-static unsigned ucvector_resize(ucvector* p, size_t size) /*returns 1 if success, 0 if failure ==> nothing done*/
-{
- if(size * sizeof(unsigned) > p->allocsize)
- {
- size_t newsize = size * sizeof(unsigned) * 2;
- void* data = realloc(p->data, newsize);
- if(data)
- {
- p->allocsize = newsize;
- p->data = (unsigned char*)data;
- p->size = size;
- }
- else return 0; /*error: not enough memory*/
- }
- else p->size = size;
- return 1;
-}
-
-
-static void ucvector_init(ucvector* p)
-{
- p->data = NULL;
- p->size = p->allocsize = 0;
-}
-
-/*you can both convert from vector to buffer&size and vica versa*/
-static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size)
-{
- p->data = buffer;
- p->allocsize = p->size = size;
-}
-
-static unsigned ucvector_push_back(ucvector* p, unsigned char c) /*returns 1 if success, 0 if failure ==> nothing done*/
-{
- if(!ucvector_resize(p, p->size + 1)) return 0;
- p->data[p->size - 1] = c;
- return 1;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Reading and writing single bits and bytes from/to stream for Deflate / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static void addBitToStream(size_t* bitpointer, ucvector* bitstream, unsigned char bit)
-{
- if((*bitpointer) % 8 == 0) ucvector_push_back(bitstream, 0); /*add a new byte at the end*/
- (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7)); /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/
- (*bitpointer)++;
-}
-
-static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits)
-{
- size_t i;
- for(i = 0; i < nbits; i++) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1));
-}
-
-static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits)
-{
- size_t i;
- for(i = 0; i < nbits; i++) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1));
-}
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Deflate - Huffman / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#define FIRST_LENGTH_CODE_INDEX 257
-#define LAST_LENGTH_CODE_INDEX 285
-#define NUM_DEFLATE_CODE_SYMBOLS 288 /*256 literals, the end code, some length codes, and 2 unused codes*/
-#define NUM_DISTANCE_SYMBOLS 32 /*the distance codes have their own symbols, 30 used, 2 unused*/
-#define NUM_CODE_LENGTH_CODES 19 /*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/
-
-static const unsigned LENGTHBASE[29] /*the base lengths represented by codes 257-285*/
- = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258};
-static const unsigned LENGTHEXTRA[29] /*the extra bits used by codes 257-285 (added to base length)*/
- = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
-static const unsigned DISTANCEBASE[30] /*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/
- = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
-static const unsigned DISTANCEEXTRA[30] /*the extra bits of backwards distances (added to base)*/
- = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
-static const unsigned CLCL[NUM_CODE_LENGTH_CODES] /*the order in which "code length alphabet code lengths" are stored, out of this the huffman tree of the dynamic huffman tree lengths is generated*/
- = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
-
-/* /////////////////////////////////////////////////////////////////////////// */
-
-/*terminology used for the package-merge algorithm and the coin collector's problem*/
-typedef struct Coin /*a coin can be multiple coins (when they're merged)*/
-{
- uivector symbols;
- float weight; /*the sum of all weights in this coin*/
-} Coin;
-
-static void Coin_init(Coin* c)
-{
- uivector_init(&c->symbols);
-}
-
-static void Coin_cleanup(void* c) /*void* so that this dtor can be given as function pointer to the vector resize function*/
-{
- uivector_cleanup(&((Coin*)c)->symbols);
-}
-
-static void Coin_copy(Coin* c1, const Coin* c2)
-{
- c1->weight = c2->weight;
- uivector_copy(&c1->symbols, &c2->symbols);
-}
-
-static void addCoins(Coin* c1, const Coin* c2)
-{
- unsigned i;
- for(i = 0; i < c2->symbols.size; i++) uivector_push_back(&c1->symbols, c2->symbols.data[i]);
- c1->weight += c2->weight;
-}
-
-static void Coin_sort(Coin* data, size_t amount) /*combsort*/
-{
- size_t gap = amount;
- unsigned char swapped = 0;
- while(gap > 1 || swapped)
- {
- size_t i;
- gap = (gap * 10) / 13; /*shrink factor 1.3*/
- if(gap == 9 || gap == 10) gap = 11; /*combsort11*/
- if(gap < 1) gap = 1;
- swapped = 0;
- for(i = 0; i < amount - gap; i++)
- {
- size_t j = i + gap;
- if(data[j].weight < data[i].weight)
- {
- float temp = data[j].weight; data[j].weight = data[i].weight; data[i].weight = temp;
- uivector_swap(&data[i].symbols, &data[j].symbols);
- swapped = 1;
- }
- }
- }
-}
-
-typedef struct HuffmanTree
-{
- uivector tree2d;
- uivector tree1d;
- uivector lengths; /*the lengths of the codes of the 1d-tree*/
- unsigned maxbitlen; /*maximum number of bits a single code can get*/
- unsigned numcodes; /*number of symbols in the alphabet = number of codes*/
-} HuffmanTree;
-
-/*function used for debug purposes*/
-/*#include <iostream>
-static void HuffmanTree_draw(HuffmanTree* tree)
-{
- std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl;
- for(size_t i = 0; i < tree->tree1d.size; i++)
- {
- if(tree->lengths.data[i])
- std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl;
- }
- std::cout << std::endl;
-}*/
-
-static void HuffmanTree_init(HuffmanTree* tree)
-{
- uivector_init(&tree->tree2d);
- uivector_init(&tree->tree1d);
- uivector_init(&tree->lengths);
-}
-
-static void HuffmanTree_cleanup(HuffmanTree* tree)
-{
- uivector_cleanup(&tree->tree2d);
- uivector_cleanup(&tree->tree1d);
- uivector_cleanup(&tree->lengths);
-}
-
-/*the tree representation used by the decoder. return value is error*/
-static unsigned HuffmanTree_make2DTree(HuffmanTree* tree)
-{
- unsigned nodefilled = 0; /*up to which node it is filled*/
- unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/
- unsigned n, i;
-
- if(!uivector_resize(&tree->tree2d, tree->numcodes * 2)) return 9901; /*if failed return not enough memory error*/
- /*convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means uninited, a value >= numcodes is an address to another bit, a value < numcodes is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as many columns as codes - 1
- a good huffmann tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. Here, the internal nodes are stored (what their 0 and 1 option point to). There is only memory for such good tree currently, if there are more nodes (due to too long length codes), error 55 will happen*/
- for(n = 0; n < tree->numcodes * 2; n++) tree->tree2d.data[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/
-
- for(n = 0; n < tree->numcodes; n++) /*the codes*/
- for(i = 0; i < tree->lengths.data[n]; i++) /*the bits for this code*/
- {
- unsigned char bit = (unsigned char)((tree->tree1d.data[n] >> (tree->lengths.data[n] - i - 1)) & 1);
- if(treepos > tree->numcodes - 2) return 55; /*error 55: oversubscribed; see description in header*/
- if(tree->tree2d.data[2 * treepos + bit] == 32767) /*not yet filled in*/
- {
- if(i + 1 == tree->lengths.data[n]) /*last bit*/
- {
- tree->tree2d.data[2 * treepos + bit] = n; /*put the current code in it*/
- treepos = 0;
- }
- else /*put address of the next step in here, first that address has to be found of course (it's just nodefilled + 1)...*/
- {
- nodefilled++;
- tree->tree2d.data[2 * treepos + bit] = nodefilled + tree->numcodes; /*addresses encoded with numcodes added to it*/
- treepos = nodefilled;
- }
- }
- else treepos = tree->tree2d.data[2 * treepos + bit] - tree->numcodes;
- }
- for(n = 0; n < tree->numcodes * 2; n++) if(tree->tree2d.data[n] == 32767) tree->tree2d.data[n] = 0; /*remove possible remaining 32767's*/
-
- return 0;
-}
-
-static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) /*given that numcodes, lengths and maxbitlen are already filled in correctly. return value is error.*/
-{
- uivector blcount;
- uivector nextcode;
- unsigned bits, n, error = 0;
-
- uivector_init(&blcount);
- uivector_init(&nextcode);
- if(!uivector_resize(&tree->tree1d, tree->numcodes)
- || !uivector_resizev(&blcount, tree->maxbitlen + 1, 0)
- || !uivector_resizev(&nextcode, tree->maxbitlen + 1, 0))
- error = 9902;
-
- if(!error)
- {
- /*step 1: count number of instances of each code length*/
- for(bits = 0; bits < tree->numcodes; bits++) blcount.data[tree->lengths.data[bits]]++;
- /*step 2: generate the nextcode values*/
- for(bits = 1; bits <= tree->maxbitlen; bits++) nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1;
- /*step 3: generate all the codes*/
- for(n = 0; n < tree->numcodes; n++) if(tree->lengths.data[n] != 0) tree->tree1d.data[n] = nextcode.data[tree->lengths.data[n]]++;
- }
-
- uivector_cleanup(&blcount);
- uivector_cleanup(&nextcode);
-
- if(!error) return HuffmanTree_make2DTree(tree);
- else return error;
-}
-
-/*given the code lengths (as stored in the PNG file), generate the tree as defined by Deflate. maxbitlen is the maximum bits that a code in the tree can have. return value is error.*/
-static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen)
-{
- unsigned i;
- if(!uivector_resize(&tree->lengths, numcodes)) return 9903;
- for(i = 0; i < numcodes; i++) tree->lengths.data[i] = bitlen[i];
- tree->numcodes = (unsigned)numcodes; /*number of symbols*/
- tree->maxbitlen = maxbitlen;
- return HuffmanTree_makeFromLengths2(tree);
-}
-
-static unsigned HuffmanTree_fillInCoins(vector* coins, const unsigned* frequencies, unsigned numcodes, size_t sum)
-{
- unsigned i;
- for(i = 0; i < numcodes; i++)
- {
- Coin* coin;
- if(frequencies[i] == 0) continue; /*it's important to exclude symbols that aren't present*/
- if(!vector_resize(coins, coins->size + 1)) { vector_cleanup(coins); return 9904; }
- coin = (Coin*)(vector_get(coins, coins->size - 1));
- Coin_init(coin);
- coin->weight = frequencies[i] / (float)sum;
- uivector_push_back(&coin->symbols, i);
- }
- if(coins->size) Coin_sort((Coin*)coins->data, coins->size);
- return 0;
-}
-
-static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, size_t numcodes, unsigned maxbitlen)
-{
- unsigned i, j;
- size_t sum = 0, numpresent = 0;
- unsigned error = 0;
-
- vector prev_row; /*type Coin, the previous row of coins*/
- vector coins; /*type Coin, the coins of the currently calculated row*/
-
- tree->maxbitlen = maxbitlen;
-
- for(i = 0; i < numcodes; i++)
- {
- if(frequencies[i] > 0)
- {
- numpresent++;
- sum += frequencies[i];
- }
- }
-
- if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/
- tree->numcodes = (unsigned)numcodes; /*number of symbols*/
- uivector_resize(&tree->lengths, 0);
- if(!uivector_resizev(&tree->lengths, tree->numcodes, 0)) return 9905;
-
- if(numpresent == 0) /*there are no symbols at all, in that case add one symbol of value 0 to the tree (see RFC 1951 section 3.2.7) */
- {
- tree->lengths.data[0] = 1;
- return HuffmanTree_makeFromLengths2(tree);
- }
- else if(numpresent == 1) /*the package merge algorithm gives wrong results if there's only one symbol (theoretically 0 bits would then suffice, but we need a proper symbol for zlib)*/
- {
- for(i = 0; i < numcodes; i++) if(frequencies[i]) tree->lengths.data[i] = 1;
- return HuffmanTree_makeFromLengths2(tree);
- }
-
- vector_init(&coins, sizeof(Coin));
- vector_init(&prev_row, sizeof(Coin));
-
- /*Package-Merge algorithm represented by coin collector's problem
- For every symbol, maxbitlen coins will be created*/
-
- /*first row, lowest denominator*/
- error = HuffmanTree_fillInCoins(&coins, frequencies, tree->numcodes, sum);
- if(!error)
- {
- for(j = 1; j <= maxbitlen && !error; j++) /*each of the remaining rows*/
- {
- vector_swap(&coins, &prev_row); /*swap instead of copying*/
- if(!vector_resized(&coins, 0, Coin_cleanup)) { error = 9906; break; }
-
- for(i = 0; i + 1 < prev_row.size; i += 2)
- {
- if(!vector_resize(&coins, coins.size + 1)) { error = 9907; break; }
- Coin_init((Coin*)vector_get(&coins, coins.size - 1));
- Coin_copy((Coin*)vector_get(&coins, coins.size - 1), (Coin*)vector_get(&prev_row, i));
- addCoins((Coin*)vector_get(&coins, coins.size - 1), (Coin*)vector_get(&prev_row, i + 1)); /*merge the coins into packages*/
- }
- if(j < maxbitlen)
- {
- error = HuffmanTree_fillInCoins(&coins, frequencies, tree->numcodes, sum);
- }
- }
- }
-
- if(!error)
- {
- /*keep the coins with lowest weight, so that they add up to the amount of symbols - 1*/
- vector_resized(&coins, numpresent - 1, Coin_cleanup);
-
- /*calculate the lengths of each symbol, as the amount of times a coin of each symbol is used*/
- for(i = 0; i < coins.size; i++)
- {
- Coin* coin = (Coin*)vector_get(&coins, i);
- for(j = 0; j < coin->symbols.size; j++) tree->lengths.data[coin->symbols.data[j]]++;
- }
-
- error = HuffmanTree_makeFromLengths2(tree);
- }
-
- vector_cleanupd(&coins, Coin_cleanup);
- vector_cleanupd(&prev_row, Coin_cleanup);
-
- return error;
-}
-
-static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) { return tree->tree1d.data[index]; }
-static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) { return tree->lengths.data[index]; }
-
-/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/
-static unsigned generateFixedTree(HuffmanTree* tree)
-{
- unsigned i, error = 0;
- uivector bitlen;
- uivector_init(&bitlen);
- if(!uivector_resize(&bitlen, NUM_DEFLATE_CODE_SYMBOLS)) error = 9909;
-
- if(!error)
- {
- /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/
- for(i = 0; i <= 143; i++) bitlen.data[i] = 8;
- for(i = 144; i <= 255; i++) bitlen.data[i] = 9;
- for(i = 256; i <= 279; i++) bitlen.data[i] = 7;
- for(i = 280; i <= 287; i++) bitlen.data[i] = 8;
-
- error = HuffmanTree_makeFromLengths(tree, bitlen.data, NUM_DEFLATE_CODE_SYMBOLS, 15);
- }
-
- uivector_cleanup(&bitlen);
- return error;
-}
-
-static unsigned generateDistanceTree(HuffmanTree* tree)
-{
- unsigned i, error = 0;
- uivector bitlen;
- uivector_init(&bitlen);
- if(!uivector_resize(&bitlen, NUM_DISTANCE_SYMBOLS)) error = 9910;
-
- /*there are 32 distance codes, but 30-31 are unused*/
- if(!error)
- {
- for(i = 0; i < NUM_DISTANCE_SYMBOLS; i++) bitlen.data[i] = 5;
- error = HuffmanTree_makeFromLengths(tree, bitlen.data, NUM_DISTANCE_SYMBOLS, 15);
- }
- uivector_cleanup(&bitlen);
- return error;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Deflator / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258;
-
-/*bitlen is the size in bits of the code*/
-static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, unsigned bitlen)
-{
- addBitsToStreamReversed(bp, compressed, code, bitlen);
-}
-
-/*search the index in the array, that has the largest value smaller than or equal to the given value, given array must be sorted (if no value is smaller, it returns the size of the given array)*/
-static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value)
-{
- /*linear search implementation*/
- /*for(size_t i = 1; i < array_size; i++) if(array[i] > value) return i - 1;
- return array_size - 1;*/
-
- /*binary search implementation (not that much faster) (precondition: array_size > 0)*/
- size_t left = 1;
- size_t right = array_size - 1;
- while(left <= right)
- {
- size_t mid = (left + right) / 2;
- if(array[mid] <= value) left = mid + 1; /*the value to find is more to the right*/
- else if(array[mid - 1] > value) right = mid - 1; /*the value to find is more to the left*/
- else return mid - 1;
- }
- return array_size - 1;
-}
-
-static void addLengthDistance(uivector* values, size_t length, size_t distance)
-{
- /*values in encoded vector are those used by deflate:
- 0-255: literal bytes
- 256: end
- 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits)
- 286-287: invalid*/
-
- unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length);
- unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]);
- unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance);
- unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]);
-
- uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX);
- uivector_push_back(values, extra_length);
- uivector_push_back(values, dist_code);
- uivector_push_back(values, extra_distance);
-}
-
-#if USE_BRUTE_FORCE_ENCODING
-#define encodeLZ77 encodeLZ77_brute
-/*the "brute force" version of the encodeLZ7 algorithm, not used anymore, kept here for reference*/
-static unsigned encodeLZ77_brute(uivector* out, const unsigned char* in, size_t size, unsigned windowSize)
-{
- size_t pos;
- /*using pointer instead of vector for input makes it faster when NOT using optimization when compiling; no influence if optimization is used*/
- for(pos = 0; pos < size; pos++)
- {
- /*Phase 1: doxygen images often have long runs of the same color, try to find them*/
- const int minLength = 4; // Minimum length for a run to make sense
-
- if(pos < size - minLength * 4)
- {
- size_t p, fp;
- size_t current_length;
-
- /*RGBA pixel run?*/
- p = pos;
- fp = pos + 4;
- current_length = 0;
-
- while(fp < size && in[p] == in[fp] && current_length < MAX_SUPPORTED_DEFLATE_LENGTH)
- {
- ++p;
- ++fp;
- ++current_length;
- }
-
- if (current_length > (minLength - 1 ) * 4) /*worth using?*/
- {
- uivector_push_back(out, in[pos ]);
- uivector_push_back(out, in[pos + 1]);
- uivector_push_back(out, in[pos + 2]);
- uivector_push_back(out, in[pos + 3]);
- addLengthDistance(out, current_length, 4);
-
- pos += current_length + 4 - 1; /*-1 for loop's pos++*/
- continue;
- }
-
- /*RGB pixel run?*/
- p = pos;
- fp = pos + 3;
- current_length = 0;
-
- while(fp < size && in[p] == in[fp] && current_length < MAX_SUPPORTED_DEFLATE_LENGTH)
- {
- ++p;
- ++fp;
- ++current_length;
- }
-
- if (current_length > (minLength - 1 ) * 3) /*worth using?*/
- {
- uivector_push_back(out, in[pos ]);
- uivector_push_back(out, in[pos + 1]);
- uivector_push_back(out, in[pos + 2]);
- addLengthDistance(out, current_length, 3);
-
- pos += current_length + 3 - 1; /*-1 for loop's pos++*/
- continue;
- }
- }
-
- size_t length = 0, offset = 0; /*the length and offset found for the current position*/
- size_t max_offset = pos < windowSize ? pos : windowSize; /*how far back to test*/
- size_t current_offset;
-
- /**search for the longest string**/
- for(current_offset = 1; current_offset < max_offset; current_offset++) /*search backwards through all possible distances (=offsets)*/
- {
- size_t backpos = pos - current_offset;
- if(in[backpos] == in[pos])
- {
- /*test the next characters*/
- size_t current_length = 1;
- size_t backtest = backpos + 1;
- size_t foretest = pos + 1;
- while(foretest < size && in[backtest] == in[foretest] && current_length < MAX_SUPPORTED_DEFLATE_LENGTH) /*maximum support length by deflate is max length*/
- {
- if(backpos >= pos) backpos -= current_offset; /*continue as if we work on the decoded bytes after pos by jumping back before pos*/
- current_length++;
- backtest++;
- foretest++;
- }
- if(current_length > length)
- {
- length = current_length; /*the longest length*/
- offset = current_offset; /*the offset that is related to this longest length*/
- if(current_length == MAX_SUPPORTED_DEFLATE_LENGTH) break; /*you can jump out of this for loop once a length of max length is found (gives significant speed gain)*/
- }
- }
- }
-
- /**encode it as length/distance pair or literal value**/
- if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/
- {
- uivector_push_back(out, in[pos]);
- }
- else
- {
- addLengthDistance(out, length, offset);
- pos += (length - 1);
- }
- } /*end of the loop through each character of input*/
-
- return 0;
-}
-#endif
-
-/*
-static const unsigned HASH_NUM_VALUES = 65536;
-static const unsigned HASH_NUM_CHARACTERS = 6;
-static const unsigned HASH_SHIFT = 2;
-Good and fast values: HASH_NUM_VALUES=65536, HASH_NUM_CHARACTERS=6, HASH_SHIFT=2
-making HASH_NUM_CHARACTERS larger (like 8), makes the file size larger but is a bit faster
-making HASH_NUM_CHARACTERS smaller (like 3), makes the file size smaller but is slower
-*/
-
-#if !defined(USE_BRUTE_FORCE_ENCODING)
-static unsigned getHash(const unsigned char* data, size_t size, size_t pos)
-{
- unsigned result = 0;
- size_t amount, i;
- if(pos >= size) return 0;
- amount = HASH_NUM_CHARACTERS; if(pos + amount >= size) amount = size - pos;
- for(i = 0; i < amount; i++) result ^= (data[pos + i] << (i * HASH_SHIFT));
- return result % HASH_NUM_VALUES;
-}
-
-/*LZ77-encode the data using a hash table technique to let it encode faster. Return value is error code*/
-static unsigned encodeLZ77(uivector* out, const unsigned char* in, size_t size, unsigned windowSize)
-{
- /**generate hash table**/
- vector table; /*HASH_NUM_VALUES uivectors; this represents what would be an std::vector<std::vector<unsigned> > in C++*/
- uivector tablepos1, tablepos2;
- unsigned pos, i, error = 0;
-
- vector_init(&table, sizeof(uivector));
- if(!vector_resize(&table, HASH_NUM_VALUES)) return 9917;
- for(i = 0; i < HASH_NUM_VALUES; i++)
- {
- uivector* v = (uivector*)vector_get(&table, i);
- uivector_init(v);
- }
-
- /*remember start and end positions in the tables to searching in*/
- uivector_init(&tablepos1);
- uivector_init(&tablepos2);
- if(!uivector_resizev(&tablepos1, HASH_NUM_VALUES, 0)) error = 9918;
- if(!uivector_resizev(&tablepos2, HASH_NUM_VALUES, 0)) error = 9919;
-
- if(!error)
- {
- for(pos = 0; pos < size; pos++)
- {
- unsigned length = 0, offset = 0; /*the length and offset found for the current position*/
- unsigned max_offset = pos < windowSize ? pos : windowSize; /*how far back to test*/
- unsigned tablepos;
-
- /*/search for the longest string*/
- /*first find out where in the table to start (the first value that is in the range from "pos - max_offset" to "pos")*/
- unsigned hash = getHash(in, size, pos);
- if(!uivector_push_back((uivector*)vector_get(&table, hash), pos)) { error = 9920; break; }
-
- while(((uivector*)vector_get(&table, hash))->data[tablepos1.data[hash]] < pos - max_offset) tablepos1.data[hash]++; /*it now points to the first value in the table for which the index is larger than or equal to pos - max_offset*/
- while(((uivector*)vector_get(&table, hash))->data[tablepos2.data[hash]] < pos) tablepos2.data[hash]++; /*it now points to the first value in the table for which the index is larger than or equal to pos*/
-
- for(tablepos = tablepos2.data[hash] - 1; tablepos >= tablepos1.data[hash] && tablepos < tablepos2.data[hash]; tablepos--)
- {
- unsigned backpos = ((uivector*)vector_get(&table, hash))->data[tablepos];
- unsigned current_offset = pos - backpos;
-
- /*test the next characters*/
- unsigned current_length = 0;
- unsigned backtest = backpos;
- unsigned foretest = pos;
- while(foretest < size && in[backtest] == in[foretest] && current_length < MAX_SUPPORTED_DEFLATE_LENGTH) /*maximum support length by deflate is max length*/
- {
- if(backpos >= pos) backpos -= current_offset; /*continue as if we work on the decoded bytes after pos by jumping back before pos*/
- current_length++;
- backtest++;
- foretest++;
- }
- if(current_length > length)
- {
- length = current_length; /*the longest length*/
- offset = current_offset; /*the offset that is related to this longest length*/
- if(current_length == MAX_SUPPORTED_DEFLATE_LENGTH) break; /*you can jump out of this for loop once a length of max length is found (gives significant speed gain)*/
- }
- }
-
- /**encode it as length/distance pair or literal value**/
- if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/
- {
- if(!uivector_push_back(out, in[pos])) { error = 9921; break; }
- }
- else
- {
- unsigned j;
- addLengthDistance(out, length, offset);
- for(j = 0; j < length - 1; j++)
- {
- pos++;
- if(!uivector_push_back((uivector*)vector_get(&table, getHash(in, size, pos)), pos)) { error = 9922; break; }
- }
- }
- } /*end of the loop through each character of input*/
- } /*end of "if(!error)"*/
-
- /*cleanup*/
- for(i = 0; i < table.size; i++)
- {
- uivector* v = (uivector*)vector_get(&table, i);
- uivector_cleanup(v);
- }
- vector_cleanup(&table);
- uivector_cleanup(&tablepos1);
- uivector_cleanup(&tablepos2);
- return error;
-}
-#endif
-
-/* /////////////////////////////////////////////////////////////////////////// */
-
-static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize)
-{
- /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/
-
- size_t i, j, numdeflateblocks = datasize / 65536 + 1;
- unsigned datapos = 0;
- for(i = 0; i < numdeflateblocks; i++)
- {
- unsigned BFINAL, BTYPE, LEN, NLEN;
- unsigned char firstbyte;
-
- BFINAL = (i == numdeflateblocks - 1);
- BTYPE = 0;
-
- firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1) << 1) + ((BTYPE & 2) << 1));
- ucvector_push_back(out, firstbyte);
-
- LEN = 65535;
- if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos;
- NLEN = 65535 - LEN;
-
- ucvector_push_back(out, (unsigned char)(LEN % 256));
- ucvector_push_back(out, (unsigned char)(LEN / 256));
- ucvector_push_back(out, (unsigned char)(NLEN % 256));
- ucvector_push_back(out, (unsigned char)(NLEN / 256));
-
- /*Decompressed data*/
- for(j = 0; j < 65535 && datapos < datasize; j++)
- {
- ucvector_push_back(out, data[datapos++]);
- }
- }
-
- return 0;
-}
-
-/*write the encoded data, using lit/len as well as distance codes*/
-static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encoded, const HuffmanTree* codes, const HuffmanTree* codesD)
-{
- size_t i = 0;
- for(i = 0; i < lz77_encoded->size; i++)
- {
- unsigned val = lz77_encoded->data[i];
- addHuffmanSymbol(bp, out, HuffmanTree_getCode(codes, val), HuffmanTree_getLength(codes, val));
- if(val > 256) /*for a length code, 3 more things have to be added*/
- {
- unsigned length_index = val - FIRST_LENGTH_CODE_INDEX;
- unsigned n_length_extra_bits = LENGTHEXTRA[length_index];
- unsigned length_extra_bits = lz77_encoded->data[++i];
-
- unsigned distance_code = lz77_encoded->data[++i];
-
- unsigned distance_index = distance_code;
- unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index];
- unsigned distance_extra_bits = lz77_encoded->data[++i];
-
- addBitsToStream(bp, out, length_extra_bits, n_length_extra_bits);
- addHuffmanSymbol(bp, out, HuffmanTree_getCode(codesD, distance_code), HuffmanTree_getLength(codesD, distance_code));
- addBitsToStream(bp, out, distance_extra_bits, n_distance_extra_bits);
- }
- }
-}
-
-static unsigned deflateDynamic(ucvector* out, const unsigned char* data, size_t datasize, const LodeZlib_DeflateSettings* settings)
-{
- /*
- after the BFINAL and BTYPE, the dynamic block consists out of the following:
- - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN
- - (HCLEN+4)*3 bits code lengths of code length alphabet
- - HLIT + 257 code lengths of lit/length alphabet (encoded using the code length alphabet, + possible repetition codes 16, 17, 18)
- - HDIST + 1 code lengths of distance alphabet (encoded using the code length alphabet, + possible repetition codes 16, 17, 18)
- - compressed data
- - 256 (end code)
- */
-
- unsigned error = 0;
-
- uivector lz77_encoded;
- HuffmanTree codes; /*tree for literal values and length codes*/
- HuffmanTree codesD; /*tree for distance codes*/
- HuffmanTree codelengthcodes;
- uivector frequencies;
- uivector frequenciesD;
- uivector amounts; /*the amounts in the "normal" order*/
- uivector lldl;
- uivector lldll; /*lit/len & dist code lengths*/
- uivector clcls;
-
- unsigned BFINAL = 1; /*make only one block... the first and final one*/
- size_t numcodes, numcodesD, i, bp = 0; /*the bit pointer*/
- unsigned HLIT, HDIST, HCLEN;
-
- uivector_init(&lz77_encoded);
- HuffmanTree_init(&codes);
- HuffmanTree_init(&codesD);
- HuffmanTree_init(&codelengthcodes);
- uivector_init(&frequencies);
- uivector_init(&frequenciesD);
- uivector_init(&amounts);
- uivector_init(&lldl);
- uivector_init(&lldll);
- uivector_init(&clcls);
-
- while(!error) /*the goto-avoiding while construct: break out to go to the cleanup phase, a break at the end makes sure the while is never repeated*/
- {
- if(settings->useLZ77)
- {
- error = encodeLZ77(&lz77_encoded, data, datasize, settings->windowSize); /*LZ77 encoded*/
- if(error) break;
- }
- else
- {
- if(!uivector_resize(&lz77_encoded, datasize)) { error = 9923; break; }
- for(i = 0; i < datasize; i++) lz77_encoded.data[i] = data[i]; /*no LZ77, but still will be Huffman compressed*/
- }
-
- if(!uivector_resizev(&frequencies, 286, 0)) { error = 9924; break; }
- if(!uivector_resizev(&frequenciesD, 30, 0)) { error = 9925; break; }
- for(i = 0; i < lz77_encoded.size; i++)
- {
- unsigned symbol = lz77_encoded.data[i];
- frequencies.data[symbol]++;
- if(symbol > 256)
- {
- unsigned dist = lz77_encoded.data[i + 2];
- frequenciesD.data[dist]++;
- i += 3;
- }
- }
- frequencies.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/
-
- error = HuffmanTree_makeFromFrequencies(&codes, frequencies.data, frequencies.size, 15);
- if(error) break;
- error = HuffmanTree_makeFromFrequencies(&codesD, frequenciesD.data, frequenciesD.size, 15);
- if(error) break;
-
- addBitToStream(&bp, out, BFINAL);
- addBitToStream(&bp, out, 0); /*first bit of BTYPE "dynamic"*/
- addBitToStream(&bp, out, 1); /*second bit of BTYPE "dynamic"*/
-
- numcodes = codes.numcodes; if(numcodes > 286) numcodes = 286;
- numcodesD = codesD.numcodes; if(numcodesD > 30) numcodesD = 30;
- for(i = 0; i < numcodes; i++) uivector_push_back(&lldll, HuffmanTree_getLength(&codes, (unsigned)i));
- for(i = 0; i < numcodesD; i++) uivector_push_back(&lldll, HuffmanTree_getLength(&codesD, (unsigned)i));
-
- /*make lldl smaller by using repeat codes 16 (copy length 3-6 times), 17 (3-10 zeros), 18 (11-138 zeros)*/
- for(i = 0; i < (unsigned)lldll.size; i++)
- {
- unsigned j = 0;
- while(i + j + 1 < (unsigned)lldll.size && lldll.data[i + j + 1] == lldll.data[i]) j++;
-
- if(lldll.data[i] == 0 && j >= 2)
- {
- j++; /*include the first zero*/
- if(j <= 10) { uivector_push_back(&lldl, 17); uivector_push_back(&lldl, j - 3); }
- else
- {
- if(j > 138) j = 138;
- uivector_push_back(&lldl, 18); uivector_push_back(&lldl, j - 11);
- }
- i += (j - 1);
- }
- else if(j >= 3)
- {
- size_t k;
- unsigned num = j / 6, rest = j % 6;
- uivector_push_back(&lldl, lldll.data[i]);
- for(k = 0; k < num; k++) { uivector_push_back(&lldl, 16); uivector_push_back(&lldl, 6 - 3); }
- if(rest >= 3) { uivector_push_back(&lldl, 16); uivector_push_back(&lldl, rest - 3); }
- else j -= rest;
- i += j;
- }
- else uivector_push_back(&lldl, lldll.data[i]);
- }
-
- /*generate huffmantree for the length codes of lit/len and dist codes*/
- if(!uivector_resizev(&amounts, 19, 0)) { error = 9926; break; } /*16 possible lengths (0-15) and 3 repeat codes (16, 17 and 18)*/
- for(i = 0; i < lldl.size; i++)
- {
- amounts.data[lldl.data[i]]++;
- if(lldl.data[i] >= 16) i++; /*after a repeat code come the bits that specify the amount, those don't need to be in the amounts calculation*/
- }
-
- error = HuffmanTree_makeFromFrequencies(&codelengthcodes, amounts.data, amounts.size, 7);
- if(error) break;
-
- if(!uivector_resize(&clcls, 19)) { error = 9927; break; }
- for(i = 0; i < 19; i++) clcls.data[i] = HuffmanTree_getLength(&codelengthcodes, CLCL[i]); /*lengths of code length tree is in the order as specified by deflate*/
- while(clcls.data[clcls.size - 1] == 0 && clcls.size > 4)
- {
- if(!uivector_resize(&clcls, clcls.size - 1)) { error = 9928; break; } /*remove zeros at the end, but minimum size must be 4*/
- }
- if(error) break;
-
- /*write the HLIT, HDIST and HCLEN values*/
- HLIT = (unsigned)(numcodes - 257);
- HDIST = (unsigned)(numcodesD - 1);
- HCLEN = (unsigned)clcls.size - 4;
- addBitsToStream(&bp, out, HLIT, 5);
- addBitsToStream(&bp, out, HDIST, 5);
- addBitsToStream(&bp, out, HCLEN, 4);
-
- /*write the code lengths of the code length alphabet*/
- for(i = 0; i < HCLEN + 4; i++) addBitsToStream(&bp, out, clcls.data[i], 3);
-
- /*write the lengths of the lit/len AND the dist alphabet*/
- for(i = 0; i < lldl.size; i++)
- {
- addHuffmanSymbol(&bp, out, HuffmanTree_getCode(&codelengthcodes, lldl.data[i]), HuffmanTree_getLength(&codelengthcodes, lldl.data[i]));
- /*extra bits of repeat codes*/
- if(lldl.data[i] == 16) addBitsToStream(&bp, out, lldl.data[++i], 2);
- else if(lldl.data[i] == 17) addBitsToStream(&bp, out, lldl.data[++i], 3);
- else if(lldl.data[i] == 18) addBitsToStream(&bp, out, lldl.data[++i], 7);
- }
-
- /*write the compressed data symbols*/
- writeLZ77data(&bp, out, &lz77_encoded, &codes, &codesD);
- if(HuffmanTree_getLength(&codes, 256) == 0) { error = 64; break; } /*the length of the end code 256 must be larger than 0*/
- addHuffmanSymbol(&bp, out, HuffmanTree_getCode(&codes, 256), HuffmanTree_getLength(&codes, 256)); /*end code*/
-
- break; /*end of error-while*/
- }
-
- /*cleanup*/
- uivector_cleanup(&lz77_encoded);
- HuffmanTree_cleanup(&codes);
- HuffmanTree_cleanup(&codesD);
- HuffmanTree_cleanup(&codelengthcodes);
- uivector_cleanup(&frequencies);
- uivector_cleanup(&frequenciesD);
- uivector_cleanup(&amounts);
- uivector_cleanup(&lldl);
- uivector_cleanup(&lldll);
- uivector_cleanup(&clcls);
-
- return error;
-}
-
-static unsigned deflateFixed(ucvector* out, const unsigned char* data, size_t datasize, const LodeZlib_DeflateSettings* settings)
-{
- HuffmanTree codes; /*tree for literal values and length codes*/
- HuffmanTree codesD; /*tree for distance codes*/
-
- unsigned BFINAL = 1; /*make only one block... the first and final one*/
- unsigned error = 0;
- size_t i, bp = 0; /*the bit pointer*/
-
- HuffmanTree_init(&codes);
- HuffmanTree_init(&codesD);
-
- generateFixedTree(&codes);
- generateDistanceTree(&codesD);
-
- addBitToStream(&bp, out, BFINAL);
- addBitToStream(&bp, out, 1); /*first bit of BTYPE*/
- addBitToStream(&bp, out, 0); /*second bit of BTYPE*/
-
- if(settings->useLZ77) /*LZ77 encoded*/
- {
- uivector lz77_encoded;
- uivector_init(&lz77_encoded);
- error = encodeLZ77(&lz77_encoded, data, datasize, settings->windowSize);
- if(!error) writeLZ77data(&bp, out, &lz77_encoded, &codes, &codesD);
- uivector_cleanup(&lz77_encoded);
- }
- else /*no LZ77, but still will be Huffman compressed*/
- {
- for(i = 0; i < datasize; i++) addHuffmanSymbol(&bp, out, HuffmanTree_getCode(&codes, data[i]), HuffmanTree_getLength(&codes, data[i]));
- }
- if(!error) addHuffmanSymbol(&bp, out, HuffmanTree_getCode(&codes, 256), HuffmanTree_getLength(&codes, 256)); /*"end" code*/
-
- /*cleanup*/
- HuffmanTree_cleanup(&codes);
- HuffmanTree_cleanup(&codesD);
-
- return error;
-}
-
-unsigned LodeFlate_deflate(ucvector* out, const unsigned char* data, size_t datasize, const LodeZlib_DeflateSettings* settings)
-{
- unsigned error = 0;
- if(settings->btype == 0) error = deflateNoCompression(out, data, datasize);
- else if(settings->btype == 1) error = deflateFixed(out, data, datasize, settings);
- else if(settings->btype == 2) error = deflateDynamic(out, data, datasize, settings);
- else error = 61;
- return error;
-}
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Adler32 */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len)
-{
- unsigned s1 = adler & 0xffff;
- unsigned s2 = (adler >> 16) & 0xffff;
-
- while(len > 0)
- {
- /*at least 5550 sums can be done before the sums overflow, saving us from a lot of module divisions*/
- unsigned amount = len > 5550 ? 5550 : len;
- len -= amount;
- while(amount > 0)
- {
- s1 = (s1 + *data++);
- s2 = (s2 + s1);
- amount--;
- }
- s1 %= 65521;
- s2 %= 65521;
- }
-
- return (s2 << 16) | s1;
-}
-
-/*Return the adler32 of the bytes data[0..len-1]*/
-static unsigned adler32(const unsigned char* data, unsigned len)
-{
- return update_adler32(1L, data, len);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Reading and writing single bits and bytes from/to stream for Zlib / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static void LodeZlib_add32bitInt(ucvector* buffer, unsigned value)
-{
- ucvector_push_back(buffer, (unsigned char)((value >> 24) & 0xff));
- ucvector_push_back(buffer, (unsigned char)((value >> 16) & 0xff));
- ucvector_push_back(buffer, (unsigned char)((value >> 8) & 0xff));
- ucvector_push_back(buffer, (unsigned char)((value ) & 0xff));
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Zlib / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned LodeZlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodeZlib_DeflateSettings* settings)
-{
- /*initially, *out must be NULL and outsize 0, if you just give some random *out that's pointing to a non allocated buffer, this'll crash*/
- ucvector deflatedata, outv;
- size_t i;
- unsigned error;
-
- unsigned ADLER32;
- /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/
- unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/
- unsigned FLEVEL = 0;
- unsigned FDICT = 0;
- unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64;
- unsigned FCHECK = 31 - CMFFLG % 31;
- CMFFLG += FCHECK;
-
- ucvector_init_buffer(&outv, *out, *outsize); /*ucvector-controlled version of the output buffer, for dynamic array*/
-
- ucvector_push_back(&outv, (unsigned char)(CMFFLG / 256));
- ucvector_push_back(&outv, (unsigned char)(CMFFLG % 256));
-
- ucvector_init(&deflatedata);
- error = LodeFlate_deflate(&deflatedata, in, insize, settings);
-
- if(!error)
- {
- ADLER32 = adler32(in, (unsigned)insize);
- for(i = 0; i < deflatedata.size; i++) ucvector_push_back(&outv, deflatedata.data[i]);
- ucvector_cleanup(&deflatedata);
- LodeZlib_add32bitInt(&outv, ADLER32);
- }
-
- *out = outv.data;
- *outsize = outv.size;
-
- return error;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-void LodeZlib_DeflateSettings_init(LodeZlib_DeflateSettings* settings)
-{
- settings->btype = 2; /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/
- settings->useLZ77 = 1;
- settings->windowSize = 2048; /*this is a good tradeoff between speed and compression ratio*/
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // End of Zlib related code, now comes the PNG related code that uses it// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*
-The two functions below (LodePNG_decompress and LodePNG_compress) directly call the
-LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions
-below, is to provide the ability to let LodePNG use a different Zlib encoder by only
-changing the two functions below, instead of changing it inside the various places
-in the other LodePNG functions.
-
-*out must be NULL and *outsize must be 0 initially, and after the function is done,
-*out must point to the decompressed data, *outsize must be the size of it, and must
-be the size of the useful data in bytes, not the alloc size.
-*/
-
-static unsigned LodePNG_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodeZlib_DeflateSettings* settings)
-{
- return LodeZlib_compress(out, outsize, in, insize, settings);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / CRC32 / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned Crc32_crc_table_computed = 0;
-static unsigned Crc32_crc_table[256];
-
-/*Make the table for a fast CRC.*/
-static void Crc32_make_crc_table(void)
-{
- unsigned int c, k, n;
- for(n = 0; n < 256; n++)
- {
- c = n;
- for(k = 0; k < 8; k++)
- {
- if(c & 1) c = (unsigned int)(0xedb88320L ^ (c >> 1));
- else c = c >> 1;
- }
- Crc32_crc_table[n] = c;
- }
- Crc32_crc_table_computed = 1;
-}
-
-/*Update a running CRC with the bytes buf[0..len-1]--the CRC should be
-initialized to all 1's, and the transmitted value is the 1's complement of the
-final running CRC (see the crc() routine below).*/
-static unsigned Crc32_update_crc(const unsigned char* buf, unsigned int crc, size_t len)
-{
- unsigned int c = crc;
- size_t n;
-
- if(!Crc32_crc_table_computed) Crc32_make_crc_table();
- for(n = 0; n < len; n++)
- {
- c = Crc32_crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
- }
- return c;
-}
-
-/*Return the CRC of the bytes buf[0..len-1].*/
-static unsigned Crc32_crc(const unsigned char* buf, size_t len)
-{
- return Crc32_update_crc(buf, 0xffffffffu, len) ^ 0xffffffffu;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Reading and writing single bits and bytes from/to stream for LodePNG / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream)
-{
- unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
- (*bitpointer)++;
- return result;
-}
-
-static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits)
-{
- unsigned result = 0;
- size_t i;
- for(i = nbits - 1; i < nbits; i--) result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i;
- return result;
-}
-
-static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
-{
- /*the current bit in bitstream may be 0 or 1 for this to work*/
- if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7))));
- else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7)));
- (*bitpointer)++;
-}
-
-static unsigned LodePNG_read32bitInt(const unsigned char* buffer)
-{
- return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
-}
-
-static void LodePNG_set32bitInt(unsigned char* buffer, unsigned value) /*buffer must have at least 4 allocated bytes available*/
-{
- buffer[0] = (unsigned char)((value >> 24) & 0xff);
- buffer[1] = (unsigned char)((value >> 16) & 0xff);
- buffer[2] = (unsigned char)((value >> 8) & 0xff);
- buffer[3] = (unsigned char)((value ) & 0xff);
-}
-
-static void LodePNG_add32bitInt(ucvector* buffer, unsigned value)
-{
- ucvector_resize(buffer, buffer->size + 4);
- LodePNG_set32bitInt(&buffer->data[buffer->size - 4], value);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG chunks / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned LodePNG_chunk_length(const unsigned char* chunk) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/
-{
- return LodePNG_read32bitInt(&chunk[0]);
-}
-
-static void LodePNG_chunk_generate_crc(unsigned char* chunk) /*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/
-{
- unsigned length = LodePNG_chunk_length(chunk);
- unsigned CRC = Crc32_crc(&chunk[4], length + 4);
- LodePNG_set32bitInt(chunk + 8 + length, CRC);
-}
-
-static unsigned LodePNG_create_chunk(unsigned char** out, size_t* outlength, unsigned length, const char* type, const unsigned char* data) /*appends new chunk to out. Returns error code; may change memory address of out buffer*/
-{
- unsigned i;
- unsigned char *chunk, *new_buffer;
- size_t new_length = (*outlength) + length + 12;
- if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/
- new_buffer = (unsigned char*)realloc(*out, new_length);
- if(!new_buffer) return 9930;
- (*out) = new_buffer;
- (*outlength) = new_length;
- chunk = &(*out)[(*outlength) - length - 12];
-
- /*1: length*/
- LodePNG_set32bitInt(chunk, (unsigned)length);
-
- /*2: chunk name (4 letters)*/
- chunk[4] = type[0];
- chunk[5] = type[1];
- chunk[6] = type[2];
- chunk[7] = type[3];
-
- /*3: the data*/
- for(i = 0; i < length; i++) chunk[8 + i] = data[i];
-
- /*4: CRC (of the chunkname characters and the data)*/
- LodePNG_chunk_generate_crc(chunk);
-
- return 0;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Color types and such / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*return type is a LodePNG error code*/
-static unsigned checkColorValidity(unsigned colorType, unsigned bd) /*bd = bitDepth*/
-{
- switch(colorType)
- {
- case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/
- case 2: if(!( bd == 8 || bd == 16)) return 37; break; /*RGB*/
- case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/
- case 4: if(!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/
- case 6: if(!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/
- default: return 31;
- }
- return 0; /*allowed color type / bits combination*/
-}
-
-static unsigned getNumColorChannels(unsigned colorType)
-{
- switch(colorType)
- {
- case 0: return 1; /*grey*/
- case 2: return 3; /*RGB*/
- case 3: return 1; /*palette*/
- case 4: return 2; /*grey + alpha*/
- case 6: return 4; /*RGBA*/
- }
- return 0; /*unexisting color type*/
-}
-
-static unsigned getBpp(unsigned colorType, unsigned bitDepth)
-{
- return getNumColorChannels(colorType) * bitDepth; /*bits per pixel is amount of channels * bits per channel*/
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static void LodePNG_InfoColor_init(LodePNG_InfoColor* info)
-{
- info->key_defined = 0;
- info->key_r = info->key_g = info->key_b = 0;
- info->colorType = 6;
- info->bitDepth = 8;
- info->palette = 0;
- info->palettesize = 0;
-}
-
-static void LodePNG_InfoColor_cleanup(LodePNG_InfoColor* info)
-{
- LodePNG_InfoColor_clearPalette(info);
-}
-
-static void LodePNG_InfoColor_clearPalette(LodePNG_InfoColor* info)
-{
- if(info->palette) free(info->palette);
- info->palettesize = 0;
-}
-
-unsigned LodePNG_InfoColor_addPalette(LodePNG_InfoColor* info, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
-{
- unsigned char* data;
- /*the same resize technique as C++ std::vectors is used, and here it's made so that for a palette with the max of 256 colors, it'll have the exact alloc size*/
- if(!(info->palettesize & (info->palettesize - 1))) /*if palettesize is 0 or a power of two*/
- {
- /*allocated data must be at least 4* palettesize (for 4 color bytes)*/
- size_t alloc_size = info->palettesize == 0 ? 4 : info->palettesize * 4 * 2;
- data = (unsigned char*)realloc(info->palette, alloc_size);
- if(!data) return 9931;
- else info->palette = data;
- }
- info->palette[4 * info->palettesize + 0] = r;
- info->palette[4 * info->palettesize + 1] = g;
- info->palette[4 * info->palettesize + 2] = b;
- info->palette[4 * info->palettesize + 3] = a;
- info->palettesize++;
- return 0;
-}
-
-static unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor* info) { return getBpp(info->colorType, info->bitDepth); } /*calculate bits per pixel out of colorType and bitDepth*/
-static unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor* info) { return info->colorType == 0 || info->colorType == 4; }
-static unsigned LodePNG_InfoColor_isAlphaType(const LodePNG_InfoColor* info) { return (info->colorType & 4) != 0; }
-
-static unsigned LodePNG_InfoColor_equal(const LodePNG_InfoColor* info1, const LodePNG_InfoColor* info2)
-{
- return info1->colorType == info2->colorType
- && info1->bitDepth == info2->bitDepth; /*palette and color key not compared*/
-}
-
-
-static void LodePNG_InfoPng_init(LodePNG_InfoPng* info)
-{
- info->width = info->height = 0;
- LodePNG_InfoColor_init(&info->color);
- info->interlaceMethod = 0;
- info->compressionMethod = 0;
- info->filterMethod = 0;
-}
-
-static void LodePNG_InfoPng_cleanup(LodePNG_InfoPng* info)
-{
- LodePNG_InfoColor_cleanup(&info->color);
-}
-
-static unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng* dest, const LodePNG_InfoPng* source)
-{
- unsigned error = 0;
- LodePNG_InfoPng_cleanup(dest);
- *dest = *source;
- LodePNG_InfoColor_init(&dest->color);
- error = LodePNG_InfoColor_copy(&dest->color, &source->color); if(error) return error;
- return error;
-}
-
-static unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor* dest, const LodePNG_InfoColor* source)
-{
- size_t i;
- LodePNG_InfoColor_cleanup(dest);
- *dest = *source;
- dest->palette = (unsigned char*)malloc(source->palettesize * 4);
- if(!dest->palette && source->palettesize) return 9935;
- for(i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i];
- return 0;
-}
-
-static void LodePNG_InfoRaw_init(LodePNG_InfoRaw* info)
-{
- LodePNG_InfoColor_init(&info->color);
-}
-
-static void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw* info)
-{
- LodePNG_InfoColor_cleanup(&info->color);
-}
-
-static unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw* dest, const LodePNG_InfoRaw* source)
-{
- unsigned error = 0;
- LodePNG_InfoRaw_cleanup(dest);
- *dest = *source;
- LodePNG_InfoColor_init(&dest->color);
- error = LodePNG_InfoColor_copy(&dest->color, &source->color);
- return error;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*
-converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code
-the out buffer must have (w * h * bpp + 7) / 8 bytes, where bpp is the bits per pixel of the output color type (LodePNG_InfoColor_getBpp)
-for < 8 bpp images, there may _not_ be padding bits at the end of scanlines.
-*/
-static unsigned LodePNG_convert(unsigned char* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h)
-{
- const size_t numpixels = w * h; /*amount of pixels*/
- const unsigned OUT_BYTES = LodePNG_InfoColor_getBpp(infoOut) / 8; /*bytes per pixel in the output image*/
- const unsigned OUT_ALPHA = LodePNG_InfoColor_isAlphaType(infoOut); /*use 8-bit alpha channel*/
- size_t i, c, bp = 0; /*bitpointer, used by less-than-8-bit color types*/
-
- /*cases where in and out already have the same format*/
- if(LodePNG_InfoColor_equal(infoIn, infoOut))
- {
- size_t i, size = (w * h * LodePNG_InfoColor_getBpp(infoIn) + 7) / 8;
- for(i = 0; i < size; i++) out[i] = in[i];
- return 0;
- }
-
- if((infoOut->colorType == 2 || infoOut->colorType == 6) && infoOut->bitDepth == 8)
- {
- if(infoIn->bitDepth == 8)
- {
- switch(infoIn->colorType)
- {
- case 0: /*greyscale color*/
- for(i = 0; i < numpixels; i++)
- {
- if(OUT_ALPHA) out[OUT_BYTES * i + 3] = 255;
- out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] = out[OUT_BYTES * i + 2] = in[i];
- if(OUT_ALPHA && infoIn->key_defined && in[i] == infoIn->key_r) out[OUT_BYTES * i + 3] = 0;
- }
- break;
- case 2: /*RGB color*/
- for(i = 0; i < numpixels; i++)
- {
- if(OUT_ALPHA) out[OUT_BYTES * i + 3] = 255;
- for(c = 0; c < 3; c++) out[OUT_BYTES * i + c] = in[3 * i + c];
- if(OUT_ALPHA && infoIn->key_defined == 1 && in[3 * i + 0] == infoIn->key_r && in[3 * i + 1] == infoIn->key_g && in[3 * i + 2] == infoIn->key_b) out[OUT_BYTES * i + 3] = 0;
- }
- break;
- case 3: /*indexed color (palette)*/
- for(i = 0; i < numpixels; i++)
- {
- if(OUT_ALPHA) out[OUT_BYTES * i + 3] = 255;
- if(in[i] >= infoIn->palettesize) return 46;
- for(c = 0; c < OUT_BYTES; c++) out[OUT_BYTES * i + c] = infoIn->palette[4 * in[i] + c]; /*get rgb colors from the palette*/
- }
- break;
- case 4: /*greyscale with alpha*/
- for(i = 0; i < numpixels; i++)
- {
- out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] = out[OUT_BYTES * i + 2] = in[2 * i + 0];
- if(OUT_ALPHA) out[OUT_BYTES * i + 3] = in[2 * i + 1];
- }
- break;
- case 6: /*RGB with alpha*/
- for(i = 0; i < numpixels; i++)
- {
- for(c = 0; c < OUT_BYTES; c++) out[OUT_BYTES * i + c] = in[4 * i + c];
- }
- break;
- default: break;
- }
- }
- else if(infoIn->bitDepth == 16)
- {
- switch(infoIn->colorType)
- {
- case 0: /*greyscale color*/
- for(i = 0; i < numpixels; i++)
- {
- if(OUT_ALPHA) out[OUT_BYTES * i + 3] = 255;
- out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] = out[OUT_BYTES * i + 2] = in[2 * i];
- if(OUT_ALPHA && infoIn->key_defined && 256U * in[i] + in[i + 1] == infoIn->key_r) out[OUT_BYTES * i + 3] = 0;
- }
- break;
- case 2: /*RGB color*/
- for(i = 0; i < numpixels; i++)
- {
- if(OUT_ALPHA) out[OUT_BYTES * i + 3] = 255;
- for(c = 0; c < 3; c++) out[OUT_BYTES * i + c] = in[6 * i + 2 * c];
- if(OUT_ALPHA && infoIn->key_defined && 256U * in[6 * i + 0] + in[6 * i + 1] == infoIn->key_r && 256U * in[6 * i + 2] + in[6 * i + 3] == infoIn->key_g && 256U * in[6 * i + 4] + in[6 * i + 5] == infoIn->key_b) out[OUT_BYTES * i + 3] = 0;
- }
- break;
- case 4: /*greyscale with alpha*/
- for(i = 0; i < numpixels; i++)
- {
- out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] = out[OUT_BYTES * i + 2] = in[4 * i]; /*most significant byte*/
- if(OUT_ALPHA) out[OUT_BYTES * i + 3] = in[4 * i + 2];
- }
- break;
- case 6: /*RGB with alpha*/
- for(i = 0; i < numpixels; i++)
- {
- for(c = 0; c < OUT_BYTES; c++) out[OUT_BYTES * i + c] = in[8 * i + 2 * c];
- }
- break;
- default: break;
- }
- }
- else /*infoIn->bitDepth is less than 8 bit per channel*/
- {
- switch(infoIn->colorType)
- {
- case 0: /*greyscale color*/
- for(i = 0; i < numpixels; i++)
- {
- unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
- if(OUT_ALPHA) out[OUT_BYTES * i + 3] = 255;
- if(OUT_ALPHA && infoIn->key_defined && value && ((1U << infoIn->bitDepth) - 1U) == infoIn->key_r && ((1U << infoIn->bitDepth) - 1U)) out[OUT_BYTES * i + 3] = 0;
- value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
- out[OUT_BYTES * i + 0] = out[OUT_BYTES * i + 1] = out[OUT_BYTES * i + 2] = (unsigned char)(value);
- }
- break;
- case 3: /*indexed color (palette)*/
- for(i = 0; i < numpixels; i++)
- {
- unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
- if(OUT_ALPHA) out[OUT_BYTES * i + 3] = 255;
- if(value >= infoIn->palettesize) return 47;
- for(c = 0; c < OUT_BYTES; c++) out[OUT_BYTES * i + c] = infoIn->palette[4 * value + c]; /*get rgb colors from the palette*/
- }
- break;
- default: break;
- }
- }
- }
- else if(LodePNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/
- {
- if(!LodePNG_InfoColor_isGreyscaleType(infoIn)) return 62;
- if(infoIn->bitDepth == 8)
- {
- switch(infoIn->colorType)
- {
- case 0: /*greyscale color*/
- for(i = 0; i < numpixels; i++)
- {
- if(OUT_ALPHA) out[OUT_BYTES * i + 1] = 255;
- out[OUT_BYTES * i] = in[i];
- if(OUT_ALPHA && infoIn->key_defined && in[i] == infoIn->key_r) out[OUT_BYTES * i + 1] = 0;
- }
- break;
- case 4: /*greyscale with alpha*/
- for(i = 0; i < numpixels; i++)
- {
- out[OUT_BYTES * i + 0] = in[2 * i + 0];
- if(OUT_ALPHA) out[OUT_BYTES * i + 1] = in[2 * i + 1];
- }
- break;
- default: return 31;
- }
- }
- else if(infoIn->bitDepth == 16)
- {
- switch(infoIn->colorType)
- {
- case 0: /*greyscale color*/
- for(i = 0; i < numpixels; i++)
- {
- if(OUT_ALPHA) out[OUT_BYTES * i + 1] = 255;
- out[OUT_BYTES * i] = in[2 * i];
- if(OUT_ALPHA && infoIn->key_defined && 256U * in[i] + in[i + 1] == infoIn->key_r) out[OUT_BYTES * i + 1] = 0;
- }
- break;
- case 4: /*greyscale with alpha*/
- for(i = 0; i < numpixels; i++)
- {
- out[OUT_BYTES * i] = in[4 * i]; /*most significant byte*/
- if(OUT_ALPHA) out[OUT_BYTES * i + 1] = in[4 * i + 2]; /*most significant byte*/
- }
- break;
- default: return 31;
- }
- }
- else /*infoIn->bitDepth is less than 8 bit per channel*/
- {
- if(infoIn->colorType != 0) return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/
- for(i = 0; i < numpixels; i++)
- {
- unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
- if(OUT_ALPHA) out[OUT_BYTES * i + 1] = 255;
- if(OUT_ALPHA && infoIn->key_defined && value && ((1U << infoIn->bitDepth) - 1U) == infoIn->key_r && ((1U << infoIn->bitDepth) - 1U)) out[OUT_BYTES * i + 1] = 0;
- value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
- out[OUT_BYTES * i] = (unsigned char)(value);
- }
- }
- }
- else return 59;
-
- return 0;
-}
-
-/*Path predictor, used by PNG filter type 4*/
-static int paethPredictor(int a, int b, int c)
-{
- int p = a + b - c;
- int pa = p > a ? p - a : a - p;
- int pb = p > b ? p - b : b - p;
- int pc = p > c ? p - c : c - p;
-
- if(pa <= pb && pa <= pc) return a;
- else if(pb <= pc) return b;
- else return c;
-}
-
-/*shared values used by multiple Adam7 related functions*/
-
-static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
-static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
-static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
-static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
-
-static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp)
-{
- /*the passstart values have 8 values: the 8th one actually indicates the byte after the end of the 7th (= last) pass*/
- unsigned i;
-
- /*calculate width and height in pixels of each pass*/
- for(i = 0; i < 7; i++)
- {
- passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
- passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
- if(passw[i] == 0) passh[i] = 0;
- if(passh[i] == 0) passw[i] = 0;
- }
-
- filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
- for(i = 0; i < 7; i++)
- {
- filter_passstart[i + 1] = filter_passstart[i] + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0); /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/
- padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8); /*bits padded if needed to fill full byte at end of each scanline*/
- passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; /*only padded at end of reduced image*/
- }
-}
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG Encoder / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*chunkName must be string of 4 characters*/
-static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length)
-{
- unsigned error = LodePNG_create_chunk(&out->data, &out->size, (unsigned)length, chunkName, data);
- if(error) return error;
- out->allocsize = out->size; /*fix the allocsize again*/
- return 0;
-}
-
-static void writeSignature(ucvector* out)
-{
- /*8 bytes PNG signature*/
- ucvector_push_back(out, 137);
- ucvector_push_back(out, 80);
- ucvector_push_back(out, 78);
- ucvector_push_back(out, 71);
- ucvector_push_back(out, 13);
- ucvector_push_back(out, 10);
- ucvector_push_back(out, 26);
- ucvector_push_back(out, 10);
-}
-
-static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, unsigned bitDepth, unsigned colorType, unsigned interlaceMethod)
-{
- unsigned error = 0;
- ucvector header;
- ucvector_init(&header);
-
- LodePNG_add32bitInt(&header, w); /*width*/
- LodePNG_add32bitInt(&header, h); /*height*/
- ucvector_push_back(&header, (unsigned char)bitDepth); /*bit depth*/
- ucvector_push_back(&header, (unsigned char)colorType); /*color type*/
- ucvector_push_back(&header, 0); /*compression method*/
- ucvector_push_back(&header, 0); /*filter method*/
- ucvector_push_back(&header, interlaceMethod); /*interlace method*/
-
- error = addChunk(out, "IHDR", header.data, header.size);
- ucvector_cleanup(&header);
-
- return error;
-}
-
-static unsigned addChunk_PLTE(ucvector* out, const LodePNG_InfoColor* info)
-{
- unsigned error = 0;
- size_t i;
- ucvector PLTE;
- ucvector_init(&PLTE);
- for(i = 0; i < info->palettesize * 4; i++) if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); /*add all channels except alpha channel*/
- error = addChunk(out, "PLTE", PLTE.data, PLTE.size);
- ucvector_cleanup(&PLTE);
-
- return error;
-}
-
-static unsigned addChunk_tRNS(ucvector* out, const LodePNG_InfoColor* info)
-{
- unsigned error = 0;
- size_t i;
- ucvector tRNS;
- ucvector_init(&tRNS);
- if(info->colorType == 3)
- {
- for(i = 0; i < info->palettesize; i++) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); /*add only alpha channel*/
- }
- else if(info->colorType == 0)
- {
- if(info->key_defined)
- {
- ucvector_push_back(&tRNS, (unsigned char)(info->key_r / 256));
- ucvector_push_back(&tRNS, (unsigned char)(info->key_r % 256));
- }
- }
- else if(info->colorType == 2)
- {
- if(info->key_defined)
- {
- ucvector_push_back(&tRNS, (unsigned char)(info->key_r / 256));
- ucvector_push_back(&tRNS, (unsigned char)(info->key_r % 256));
- ucvector_push_back(&tRNS, (unsigned char)(info->key_g / 256));
- ucvector_push_back(&tRNS, (unsigned char)(info->key_g % 256));
- ucvector_push_back(&tRNS, (unsigned char)(info->key_b / 256));
- ucvector_push_back(&tRNS, (unsigned char)(info->key_b % 256));
- }
- }
-
- error = addChunk(out, "tRNS", tRNS.data, tRNS.size);
- ucvector_cleanup(&tRNS);
-
- return error;
-}
-
-static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, LodeZlib_DeflateSettings* zlibsettings)
-{
- ucvector zlibdata;
- unsigned error = 0;
-
- /*compress with the Zlib compressor*/
- ucvector_init(&zlibdata);
- error = LodePNG_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings);
- if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size);
- ucvector_cleanup(&zlibdata);
-
- return error;
-}
-
-static unsigned addChunk_IEND(ucvector* out)
-{
- unsigned error = 0;
- error = addChunk(out, "IEND", 0, 0);
- return error;
-}
-
-static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, size_t length, size_t bytewidth, unsigned char filterType)
-{
- size_t i;
- switch(filterType)
- {
- case 0:
- for(i = 0; i < length; i++) out[i] = scanline[i];
- break;
- case 1:
- for(i = 0; i < bytewidth; i++) out[i] = scanline[i];
- for(i = bytewidth; i < length; i++) out[i] = scanline[i] - scanline[i - bytewidth];
- break;
- case 2:
- if(prevline) for(i = 0; i < length; i++) out[i] = scanline[i] - prevline[i];
- else for(i = 0; i < length; i++) out[i] = scanline[i];
- break;
- case 3:
- if(prevline)
- {
- for(i = 0; i < bytewidth; i++) out[i] = scanline[i] - prevline[i] / 2;
- for(i = bytewidth; i < length; i++) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) / 2);
- }
- else
- {
- for(i = 0; i < length; i++) out[i] = scanline[i];
- for(i = bytewidth; i < length; i++) out[i] = scanline[i] - scanline[i - bytewidth] / 2;
- }
- break;
- case 4:
- if(prevline)
- {
- for(i = 0; i < bytewidth; i++) out[i] = (unsigned char)(scanline[i] - paethPredictor(0, prevline[i], 0));
- for(i = bytewidth; i < length; i++) out[i] = (unsigned char)(scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth]));
- }
- else
- {
- for(i = 0; i < bytewidth; i++) out[i] = scanline[i];
- for(i = bytewidth; i < length; i++) out[i] = (unsigned char)(scanline[i] - paethPredictor(scanline[i - bytewidth], 0, 0));
- }
- break;
- default: return; /*unexisting filter type given*/
- }
-}
-
-static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, const LodePNG_InfoColor* info)
-{
- /*
- For PNG filter method 0
- out must be a buffer with as size: h + (w * h * bpp + 7) / 8, because there are the scanlines with 1 extra byte per scanline
-
- There is a nice heuristic described here: http://www.cs.toronto.edu/~cosmin/pngtech/optipng.html. It says:
- * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. use fixed filtering, with the filter None).
- * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply all five filters and select the filter that produces the smallest sum of absolute values per row.
-
- Here the above method is used mostly. Note though that it appears to be better to use the adaptive filtering on the plasma 8-bit palette example, but that image isn't the best reference for palette images in general.
- */
-
- unsigned bpp = LodePNG_InfoColor_getBpp(info);
- size_t linebytes = (w * bpp + 7) / 8; /*the width of a scanline in bytes, not including the filter type*/
- size_t bytewidth = (bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
- const unsigned char* prevline = 0;
- unsigned x, y;
- unsigned heuristic;
- unsigned error = 0;
-
- if(bpp == 0) return 31; /*invalid color type*/
-
- /*choose heuristic as described above*/
- if(info->colorType == 3 || info->bitDepth < 8) heuristic = 0;
- else heuristic = 1;
-
- if(heuristic == 0) /*None filtertype for everything*/
- {
- for(y = 0; y < h; y++)
- {
- size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
- size_t inindex = linebytes * y;
- const unsigned TYPE = 0;
- out[outindex] = TYPE; /*filter type byte*/
- filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, TYPE);
- prevline = &in[inindex];
- }
- }
- else if(heuristic == 1) /*adaptive filtering*/
- {
- size_t sum[5];
- ucvector attempt[5]; /*five filtering attempts, one for each filter type*/
- size_t smallest = 0;
- unsigned type, bestType = 0;
-
- for(type = 0; type < 5; type++) ucvector_init(&attempt[type]);
- for(type = 0; type < 5; type++)
- {
- if(!ucvector_resize(&attempt[type], linebytes)) { error = 9949; break; }
- }
-
- if(!error)
- {
- for(y = 0; y < h; y++)
- {
- /*try the 5 filter types*/
- for(type = 0; type < 5; type++)
- {
- filterScanline(attempt[type].data, &in[y * linebytes], prevline, linebytes, bytewidth, type);
-
- /*calculate the sum of the result*/
- sum[type] = 0;
- for(x = 0; x < attempt[type].size; x+=3) sum[type] += attempt[type].data[x]; /*note that not all pixels are checked to speed this up while still having probably the best choice*/
-
- /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/
- if(type == 0 || sum[type] < smallest)
- {
- bestType = type;
- smallest = sum[type];
- }
- }
-
- prevline = &in[y * linebytes];
-
- /*now fill the out values*/
- out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
- for(x = 0; x < linebytes; x++) out[y * (linebytes + 1) + 1 + x] = attempt[bestType].data[x];
- }
- }
-
- for(type = 0; type < 5; type++) ucvector_cleanup(&attempt[type]);
- }
-
- return error;
-}
-
-static void addPaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h)
-{
- /*The opposite of the removePaddingBits function
- olinebits must be >= ilinebits*/
- unsigned y;
- size_t diff = olinebits - ilinebits;
- size_t obp = 0, ibp = 0; /*bit pointers*/
- for(y = 0; y < h; y++)
- {
- size_t x;
- for(x = 0; x < ilinebits; x++)
- {
- unsigned char bit = readBitFromReversedStream(&ibp, in);
- setBitOfReversedStream(&obp, out, bit);
- }
- /*obp += diff; --> no, fill in some value in the padding bits too, to avoid "Use of uninitialised value of size ###" warning from valgrind*/
- for(x = 0; x < diff; x++) setBitOfReversedStream(&obp, out, 0);
- }
-}
-
-static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
-{
- /*Note: this function works on image buffers WITHOUT padding bits at end of scanlines with non-multiple-of-8 bit amounts, only between reduced images is padding*/
- unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
- unsigned i;
-
- Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
- if(bpp >= 8)
- {
- for(i = 0; i < 7; i++)
- {
- unsigned x, y, b;
- size_t bytewidth = bpp / 8;
- for(y = 0; y < passh[i]; y++)
- for(x = 0; x < passw[i]; x++)
- {
- size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth;
- size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth;
- for(b = 0; b < bytewidth; b++)
- {
- out[pixeloutstart + b] = in[pixelinstart + b];
- }
- }
- }
- }
- else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/
- {
- for(i = 0; i < 7; i++)
- {
- unsigned x, y, b;
- unsigned ilinebits = bpp * passw[i];
- unsigned olinebits = bpp * w;
- size_t obp, ibp; /*bit pointers (for out and in buffer)*/
- for(y = 0; y < passh[i]; y++)
- for(x = 0; x < passw[i]; x++)
- {
- ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp;
- obp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
- for(b = 0; b < bpp; b++)
- {
- unsigned char bit = readBitFromReversedStream(&ibp, in);
- setBitOfReversedStream(&obp, out, bit);
- }
- }
- }
- }
-}
-
-/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image*/
-static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, const LodePNG_InfoPng* infoPng) /*return value is error*/
-{
- /*
- This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps:
- *) if no Adam7: 1) add padding bits (= possible extra bits per scanline if bpp < 8) 2) filter
- *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter
- */
- unsigned bpp = LodePNG_InfoColor_getBpp(&infoPng->color);
- unsigned w = infoPng->width;
- unsigned h = infoPng->height;
- unsigned error = 0;
-
- if(infoPng->interlaceMethod == 0)
- {
- *outsize = h + (h * ((w * bpp + 7) / 8)); /*image size plus an extra byte per scanline + possible padding bits*/
- *out = (unsigned char*)malloc(*outsize);
- if(!(*out) && (*outsize)) error = 9950;
-
- if(!error)
- {
- if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) /*non multiple of 8 bits per scanline, padding bits needed per scanline*/
- {
- ucvector padded;
- ucvector_init(&padded);
- if(!ucvector_resize(&padded, h * ((w * bpp + 7) / 8))) error = 9951;
- if(!error)
- {
- addPaddingBits(padded.data, in, ((w * bpp + 7) / 8) * 8, w * bpp, h);
- error = filter(*out, padded.data, w, h, &infoPng->color);
- }
- ucvector_cleanup(&padded);
- }
- else error = filter(*out, in, w, h, &infoPng->color); /*we can immediately filter into the out buffer, no other steps needed*/
- }
- }
- else /*interlaceMethod is 1 (Adam7)*/
- {
- unsigned char* adam7 = (unsigned char*)malloc((h * w * bpp + 7) / 8);
- if(!adam7 && ((h * w * bpp + 7) / 8)) error = 9952; /*malloc failed*/
-
- while(!error) /*not a real while loop, used to break out to cleanup to avoid a goto*/
- {
- unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
- unsigned i;
-
- Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
- *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/
- *out = (unsigned char*)malloc(*outsize);
- if(!(*out) && (*outsize)) { error = 9953; break; }
-
- Adam7_interlace(adam7, in, w, h, bpp);
-
- for(i = 0; i < 7; i++)
- {
- if(bpp < 8)
- {
- ucvector padded;
- ucvector_init(&padded);
- if(!ucvector_resize(&padded, h * ((w * bpp + 7) / 8))) error = 9954;
- if(!error)
- {
- addPaddingBits(&padded.data[padded_passstart[i]], &adam7[passstart[i]], ((passw[i] * bpp + 7) / 8) * 8, passw[i] * bpp, passh[i]);
- error = filter(&(*out)[filter_passstart[i]], &padded.data[padded_passstart[i]], passw[i], passh[i], &infoPng->color);
- }
-
- ucvector_cleanup(&padded);
- }
- else
- {
- error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], passw[i], passh[i], &infoPng->color);
- }
- }
-
- break;
- }
-
- free(adam7);
- }
-
- return error;
-}
-
-/*palette must have 4 * palettesize bytes allocated*/
-static unsigned isPaletteFullyOpaque(const unsigned char* palette, size_t palettesize) /*palette given in format RGBARGBARGBARGBA...*/
-{
- size_t i;
- for(i = 0; i < palettesize; i++)
- {
- if(palette[4 * i + 3] != 255) return 0;
- }
- return 1;
-}
-
-/*this function checks if the input image given by the user has no transparent pixels*/
-static unsigned isFullyOpaque(const unsigned char* image, unsigned w, unsigned h, const LodePNG_InfoColor* info)
-{
- /*TODO: When the user specified a color key for the input image, then this function must also check for pixels that are the same as the color key and treat those as transparent.*/
-
- unsigned i, numpixels = w * h;
- if(info->colorType == 6)
- {
- if(info->bitDepth == 8)
- {
- for(i = 0; i < numpixels; i++) if(image[i * 4 + 3] != 255) return 0;
- }
- else
- {
- for(i = 0; i < numpixels; i++) if(image[i * 8 + 6] != 255 || image[i * 8 + 7] != 255) return 0;
- }
- return 1; /*no single pixel with alpha channel other than 255 found*/
- }
- else if(info->colorType == 4)
- {
- if(info->bitDepth == 8)
- {
- for(i = 0; i < numpixels; i++) if(image[i * 2 + 1] != 255) return 0;
- }
- else
- {
- for(i = 0; i < numpixels; i++) if(image[i * 4 + 2] != 255 || image[i * 4 + 3] != 255) return 0;
- }
- return 1; /*no single pixel with alpha channel other than 255 found*/
- }
- else if(info->colorType == 3)
- {
- /*when there's a palette, we could check every pixel for translucency, but much quicker is to just check the palette*/
- return(isPaletteFullyOpaque(info->palette, info->palettesize));
- }
-
- return 0; /*color type that isn't supported by this function yet, so assume there is transparency to be safe*/
-}
-
-void LodePNG_encode(LodePNG_Encoder* encoder, unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h)
-{
- LodePNG_InfoPng info;
- ucvector outv;
- unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/
- size_t datasize = 0;
-
- /*provide some proper output values if error will happen*/
- *out = 0;
- *outsize = 0;
- encoder->error = 0;
-
- info = encoder->infoPng; /*UNSAFE copy to avoid having to cleanup! but we will only change primitive parameters, and not invoke the cleanup function nor touch the palette's buffer so we use it safely*/
- info.width = w;
- info.height = h;
-
- if(encoder->settings.autoLeaveOutAlphaChannel && isFullyOpaque(image, w, h, &encoder->infoRaw.color))
- {
- /*go to a color type without alpha channel*/
- if(info.color.colorType == 6) info.color.colorType = 2;
- else if(info.color.colorType == 4) info.color.colorType = 0;
- }
-
- if(encoder->settings.zlibsettings.windowSize > 32768) { encoder->error = 60; return; } /*error: windowsize larger than allowed*/
- if(encoder->settings.zlibsettings.btype > 2) { encoder->error = 61; return; } /*error: unexisting btype*/
- if(encoder->infoPng.interlaceMethod > 1) { encoder->error = 71; return; } /*error: unexisting interlace mode*/
- if((encoder->error = checkColorValidity(info.color.colorType, info.color.bitDepth))) return; /*error: unexisting color type given*/
- if((encoder->error = checkColorValidity(encoder->infoRaw.color.colorType, encoder->infoRaw.color.bitDepth))) return; /*error: unexisting color type given*/
-
- if(!LodePNG_InfoColor_equal(&encoder->infoRaw.color, &info.color))
- {
- unsigned char* converted;
- size_t size = (w * h * LodePNG_InfoColor_getBpp(&info.color) + 7) / 8;
-
- if((info.color.colorType != 6 && info.color.colorType != 2) || (info.color.bitDepth != 8)) { encoder->error = 59; return; } /*for the output image, only these types are supported*/
- converted = (unsigned char*)malloc(size);
- if(!converted && size) encoder->error = 9955; /*error: malloc failed*/
- if(!encoder->error) encoder->error = LodePNG_convert(converted, image, &info.color, &encoder->infoRaw.color, w, h);
- if(!encoder->error) preProcessScanlines(&data, &datasize, converted, &info);/*filter(data.data, converted.data, w, h, LodePNG_InfoColor_getBpp(&info.color));*/
- free(converted);
- }
- else preProcessScanlines(&data, &datasize, image, &info);/*filter(data.data, image, w, h, LodePNG_InfoColor_getBpp(&info.color));*/
-
- ucvector_init(&outv);
- while(!encoder->error) /*not really a while loop, this is only used to break out if an error happens to avoid goto's to do the ucvector cleanup*/
- {
- /*write signature and chunks*/
- writeSignature(&outv);
- /*IHDR*/
- addChunk_IHDR(&outv, w, h, info.color.bitDepth, info.color.colorType, info.interlaceMethod);
- /*PLTE*/
- if(info.color.colorType == 3)
- {
- if(info.color.palettesize == 0 || info.color.palettesize > 256) { encoder->error = 68; break; }
- addChunk_PLTE(&outv, &info.color);
- }
- if(encoder->settings.force_palette && (info.color.colorType == 2 || info.color.colorType == 6))
- {
- if(info.color.palettesize == 0 || info.color.palettesize > 256) { encoder->error = 68; break; }
- addChunk_PLTE(&outv, &info.color);
- }
- /*tRNS*/
- if(info.color.colorType == 3 && !isPaletteFullyOpaque(info.color.palette, info.color.palettesize)) addChunk_tRNS(&outv, &info.color);
- if((info.color.colorType == 0 || info.color.colorType == 2) && info.color.key_defined) addChunk_tRNS(&outv, &info.color);
- /*IDAT (multiple IDAT chunks must be consecutive)*/
- encoder->error = addChunk_IDAT(&outv, data, datasize, &encoder->settings.zlibsettings);
- if(encoder->error) break;
- /*IEND*/
- addChunk_IEND(&outv);
-
- break; /*this isn't really a while loop; no error happened so break out now!*/
- }
-
- free(data);
- /*instead of cleaning the vector up, give it to the output*/
- *out = outv.data;
- *outsize = outv.size;
-}
-
-void LodePNG_EncodeSettings_init(LodePNG_EncodeSettings* settings)
-{
- LodeZlib_DeflateSettings_init(&settings->zlibsettings);
- settings->autoLeaveOutAlphaChannel = 1;
- settings->force_palette = 0;
-}
-
-void LodePNG_Encoder_init(LodePNG_Encoder* encoder)
-{
- LodePNG_EncodeSettings_init(&encoder->settings);
- LodePNG_InfoPng_init(&encoder->infoPng);
- LodePNG_InfoRaw_init(&encoder->infoRaw);
- encoder->error = 1;
-}
-
-void LodePNG_Encoder_cleanup(LodePNG_Encoder* encoder)
-{
- LodePNG_InfoPng_cleanup(&encoder->infoPng);
- LodePNG_InfoRaw_cleanup(&encoder->infoRaw);
-}
-
-void LodePNG_Encoder_copy(LodePNG_Encoder* dest, const LodePNG_Encoder* source)
-{
- LodePNG_Encoder_cleanup(dest);
- *dest = *source;
- LodePNG_InfoPng_init(&dest->infoPng);
- LodePNG_InfoRaw_init(&dest->infoRaw);
- dest->error = LodePNG_InfoPng_copy(&dest->infoPng, &source->infoPng); if(dest->error) return;
- dest->error = LodePNG_InfoRaw_copy(&dest->infoRaw, &source->infoRaw); if(dest->error) return;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / File IO / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*write given buffer to the file, overwriting the file, it doesn't append to it.*/
-unsigned LodePNG_saveFile(const unsigned char* buffer, size_t buffersize, const char* filename)
-{
- FILE* file;
- file = portable_fopen(filename, "wb" );
- if(!file) return 79;
- fwrite((char*)buffer , 1 , buffersize, file);
- fclose(file);
- return 0;
-}
-
diff --git a/src/lodepng.h b/src/lodepng.h
deleted file mode 100644
index fb079cc..0000000
--- a/src/lodepng.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
-LodePNG version 20080927
-
-Copyright (c) 2005-2008 Lode Vandevenne
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
-*/
-
-/** Minified version of LodePNG, with only the encoder code */
-
-#ifndef LODEPNG_H
-#define LODEPNG_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* LodeFlate & LodeZlib Setting structs */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-
-typedef struct LodeZlib_DeflateSettings /*deflate = compress*/
-{
- /*LZ77 related settings*/
- unsigned btype; /*the block type for LZ*/
- unsigned useLZ77; /*whether or not to use LZ77*/
- unsigned windowSize; /*the maximum is 32768*/
-} LodeZlib_DeflateSettings;
-
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* LodePNG */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-typedef struct LodePNG_InfoColor /*info about the color type of an image*/
-{
- /*header (IHDR)*/
- unsigned colorType; /*color type*/
- unsigned bitDepth; /*bits per sample*/
-
- /*palette (PLTE)*/
- unsigned char* palette; /*palette in RGBARGBA... order*/
- size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/
-
- /*transparent color key (tRNS)*/
- unsigned key_defined; /*is a transparent color key given?*/
- unsigned key_r; /*red component of color key*/
- unsigned key_g; /*green component of color key*/
- unsigned key_b; /*blue component of color key*/
-} LodePNG_InfoColor;
-
-typedef struct LodePNG_InfoPng /*information about the PNG image, except pixels and sometimes except width and height*/
-{
- /*header (IHDR), palette (PLTE) and transparency (tRNS)*/
- unsigned width; /*width of the image in pixels (ignored by encoder, but filled in by decoder)*/
- unsigned height; /*height of the image in pixels (ignored by encoder, but filled in by decoder)*/
- unsigned compressionMethod; /*compression method of the original file*/
- unsigned filterMethod; /*filter method of the original file*/
- unsigned interlaceMethod; /*interlace method of the original file*/
- LodePNG_InfoColor color; /*color type and bits, palette, transparency*/
-} LodePNG_InfoPng;
-
-typedef struct LodePNG_InfoRaw /*contains user-chosen information about the raw image data, which is independent of the PNG image*/
-{
- LodePNG_InfoColor color;
-} LodePNG_InfoRaw;
-
-unsigned LodePNG_InfoColor_addPalette(LodePNG_InfoColor* info, unsigned char r, unsigned char g, unsigned char b, unsigned char a); /*add 1 color to the palette*/
-
-typedef struct LodePNG_EncodeSettings
-{
- LodeZlib_DeflateSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/
-
- unsigned autoLeaveOutAlphaChannel; /*automatically use color type without alpha instead of given one, if given image is opaque*/
- unsigned force_palette; /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). If colortype is 3, PLTE is _always_ created.*/
-} LodePNG_EncodeSettings;
-
-void LodePNG_EncodeSettings_init(LodePNG_EncodeSettings* settings);
-
-typedef struct LodePNG_Encoder
-{
- LodePNG_EncodeSettings settings;
- LodePNG_InfoPng infoPng; /*the info specified by the user may not be changed by the encoder. The encoder will try to generate a PNG close to the given info.*/
- LodePNG_InfoRaw infoRaw; /*put the properties of the input raw image in here*/
- unsigned error;
-} LodePNG_Encoder;
-
-void LodePNG_Encoder_init(LodePNG_Encoder* encoder);
-void LodePNG_Encoder_cleanup(LodePNG_Encoder* encoder);
-void LodePNG_Encoder_copy(LodePNG_Encoder* dest, const LodePNG_Encoder* source);
-
-/*This function allocates the out buffer and stores the size in *outsize.*/
-void LodePNG_encode(LodePNG_Encoder* encoder, unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h);
-
-/*free functions allowing to load and save a file from/to harddisk*/
-/*This function allocates the out buffer and stores the size in *outsize.*/
-//unsigned LodePNG_loadFile(unsigned char** out, size_t* outsize, const char* filename);
-unsigned LodePNG_saveFile(const unsigned char* buffer, size_t buffersize, const char* filename);
-
-#endif
-
diff --git a/src/mandocvisitor.cpp b/src/mandocvisitor.cpp
index 5c98c6f..9f5a45b 100644
--- a/src/mandocvisitor.cpp
+++ b/src/mandocvisitor.cpp
@@ -367,7 +367,9 @@ void ManDocVisitor::visit(DocInclude *inc)
void ManDocVisitor::visit(DocIncOperator *op)
{
- SrcLangExt langExt = getLanguageFromFileName(m_langExt);
+ QCString locLangExt = getFileNameExtension(op->includeFileName());
+ if (locLangExt.isEmpty()) locLangExt = m_langExt;
+ SrcLangExt langExt = getLanguageFromFileName(locLangExt);
//printf("DocIncOperator: type=%d first=%d, last=%d text=`%s'\n",
// op->type(),op->isFirst(),op->isLast(),op->text().data());
if (op->isFirst())
@@ -386,14 +388,14 @@ void ManDocVisitor::visit(DocIncOperator *op)
popEnabled();
if (!m_hide)
{
- FileDef *fd;
+ FileDef *fd = 0;
if (!op->includeFileName().isEmpty())
{
QFileInfo cfi( op->includeFileName() );
fd = createFileDef( cfi.dirPath().utf8(), cfi.fileName().utf8() );
}
- Doxygen::parserManager->getParser(m_langExt)
+ Doxygen::parserManager->getParser(locLangExt)
->parseCode(m_ci,op->context(),op->text(),langExt,
op->isExample(),op->exampleFile(),
fd, // fileDef
@@ -569,7 +571,7 @@ void ManDocVisitor::visitPre(DocSimpleSect *s)
// special case 1: user defined title
if (s->type()!=DocSimpleSect::User && s->type()!=DocSimpleSect::Rcs)
{
- m_t << ":\\fP" << endl;
+ m_t << "\\fP" << endl;
m_t << ".RS 4" << endl;
}
}
@@ -942,7 +944,7 @@ void ManDocVisitor::visitPre(DocParamSect *s)
default:
ASSERT(0);
}
- m_t << ":\\fP" << endl;
+ m_t << "\\fP" << endl;
m_t << ".RS 4" << endl;
}
@@ -1026,14 +1028,6 @@ void ManDocVisitor::visitPost(DocInternalRef *)
m_t << "\\fP";
}
-void ManDocVisitor::visitPre(DocCopy *)
-{
-}
-
-void ManDocVisitor::visitPost(DocCopy *)
-{
-}
-
void ManDocVisitor::visitPre(DocText *)
{
}
diff --git a/src/mandocvisitor.h b/src/mandocvisitor.h
index 8efc223..fa65424 100644
--- a/src/mandocvisitor.h
+++ b/src/mandocvisitor.h
@@ -128,8 +128,6 @@ class ManDocVisitor : public DocVisitor
void visitPost(DocXRefItem *);
void visitPre(DocInternalRef *);
void visitPost(DocInternalRef *);
- void visitPre(DocCopy *);
- void visitPost(DocCopy *);
void visitPre(DocText *);
void visitPost(DocText *);
void visitPre(DocHtmlBlockQuote *);
diff --git a/src/mangen.h b/src/mangen.h
index 959a34c..d912923 100644
--- a/src/mangen.h
+++ b/src/mangen.h
@@ -207,16 +207,16 @@ class ManGenerator : public OutputGenerator
void endDescTableData() {}
void startDotGraph() {}
- void endDotGraph(const DotClassGraph &) {}
+ void endDotGraph(DotClassGraph &) {}
void startInclDepGraph() {}
- void endInclDepGraph(const DotInclDepGraph &) {}
+ void endInclDepGraph(DotInclDepGraph &) {}
void startGroupCollaboration() {}
- void endGroupCollaboration(const DotGroupCollaboration &) {}
+ void endGroupCollaboration(DotGroupCollaboration &) {}
void startCallGraph() {}
- void endCallGraph(const DotCallGraph &) {}
+ void endCallGraph(DotCallGraph &) {}
void startDirDepGraph() {}
- void endDirDepGraph(const DotDirDeps &) {}
- void writeGraphicalHierarchy(const DotGfxHierarchyTable &) {}
+ void endDirDepGraph(DotDirDeps &) {}
+ void writeGraphicalHierarchy(DotGfxHierarchyTable &) {}
void startTextBlock(bool) {}
void endTextBlock(bool) {}
diff --git a/src/markdown.cpp b/src/markdown.cpp
index 15f119b..8670642 100644
--- a/src/markdown.cpp
+++ b/src/markdown.cpp
@@ -2439,7 +2439,10 @@ static QCString extractPageTitle(QCString &docs,QCString &id)
{
docs=docs.mid(end1);
}
- id = extractTitleId(title, 0);
+ else
+ {
+ id = extractTitleId(title, 0);
+ }
//printf("extractPageTitle(title='%s' docs='%s' id='%s')\n",title.data(),docs.data(),id.data());
return title;
}
diff --git a/src/memberdef.cpp b/src/memberdef.cpp
index 3442229..15da899 100644
--- a/src/memberdef.cpp
+++ b/src/memberdef.cpp
@@ -33,6 +33,7 @@
#include "defargs.h"
#include "docparser.h"
#include "dot.h"
+#include "dotcallgraph.h"
#include "searchindex.h"
#include "parserintf.h"
#include "objcache.h"
@@ -2945,7 +2946,7 @@ void MemberDefImpl::_writeCallerGraph(OutputList &ol) const
{
warn_uncond("Caller graph for '%s' not generated, too many nodes. Consider increasing DOT_GRAPH_MAX_NODES.\n",qPrint(qualifiedName()));
}
- else if (!callerGraph.isTrivial() && !callerGraph.isTooBig())
+ else if (!callerGraph.isTrivial())
{
msg("Generating caller graph for function %s\n",qPrint(qualifiedName()));
ol.disable(OutputGenerator::Man);
diff --git a/src/msc.cpp b/src/msc.cpp
index 29f96ac..0137e1b 100644
--- a/src/msc.cpp
+++ b/src/msc.cpp
@@ -24,6 +24,7 @@
#include "index.h"
#include "util.h"
#include "ftextstream.h"
+#include "mscgen_api.h"
#include <qdir.h>
@@ -97,50 +98,38 @@ void writeMscGraphFromFile(const char *inFile,const char *outDir,
absOutFile+=portable_pathSeparator();
absOutFile+=outFile;
- // chdir to the output dir, so dot can find the font file.
- QCString oldDir = QDir::currentDirPath().utf8();
- // go to the html output directory (i.e. path)
- QDir::setCurrent(outDir);
- //printf("Going to dir %s\n",QDir::currentDirPath().data());
- QCString mscExe = Config_getString(MSCGEN_PATH)+"mscgen"+portable_commandExtension();
- QCString mscArgs;
- QCString imgName = outFile;
+ mscgen_format_t msc_format;
+ QCString imgName = absOutFile;
switch (format)
{
case MSC_BITMAP:
- mscArgs+="-T png";
+ msc_format = mscgen_format_png;
imgName+=".png";
break;
case MSC_EPS:
- mscArgs+="-T eps";
+ msc_format = mscgen_format_eps;
imgName+=".eps";
break;
case MSC_SVG:
- mscArgs+="-T svg";
+ msc_format = mscgen_format_svg;
imgName+=".svg";
break;
default:
- goto error; // I am not very fond of goto statements, but when in Rome...
+ return;
}
- mscArgs+=" -i \"";
- mscArgs+=inFile;
-
- mscArgs+="\" -o \"";
- mscArgs+=imgName+"\"";
- int exitCode;
-// printf("*** running: %s %s outDir:%s %s\n",mscExe.data(),mscArgs.data(),outDir,outFile);
- portable_sysTimerStart();
- if ((exitCode=portable_system(mscExe,mscArgs,FALSE))!=0)
+ int code;
+ if ((code=mscgen_generate(inFile,imgName,msc_format))!=0)
{
- portable_sysTimerStop();
- goto error;
+ err("Problems generating msc output (error=%s). Look for typos in you msc file %s\n",
+ mscgen_error2str(code),inFile);
+ return;
}
- portable_sysTimerStop();
+
if ( (format==MSC_EPS) && (Config_getBool(USE_PDFLATEX)) )
{
QCString epstopdfArgs(maxCmdLine);
epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
- outFile,outFile);
+ absOutFile.data(),absOutFile.data());
portable_sysTimerStart();
if (portable_system("epstopdf",epstopdfArgs)!=0)
{
@@ -151,45 +140,28 @@ void writeMscGraphFromFile(const char *inFile,const char *outDir,
Doxygen::indexList->addImageFile(imgName);
-error:
- QDir::setCurrent(oldDir);
}
-QCString getMscImageMapFromFile(const QCString& inFile, const QCString& outDir,
- const QCString& relPath,const QCString& context)
+static QCString getMscImageMapFromFile(const QCString& inFile, const QCString& outDir,
+ const QCString& relPath,const QCString& context,
+ bool writeSVGMap)
{
QCString outFile = inFile + ".map";
-
- //printf("*** running:getMscImageMapFromFile \n");
- // chdir to the output dir, so dot can find the font file.
- QCString oldDir = QDir::currentDirPath().utf8();
- // go to the html output directory (i.e. path)
- QDir::setCurrent(outDir);
- //printf("Going to dir %s\n",QDir::currentDirPath().data());
-
- QCString mscExe = Config_getString(MSCGEN_PATH)+"mscgen"+portable_commandExtension();
- QCString mscArgs = "-T ismap -i \"";
- mscArgs+=inFile;
- mscArgs+="\" -o \"";
- mscArgs+=outFile + "\"";
-
- int exitCode;
- portable_sysTimerStart();
- if ((exitCode=portable_system(mscExe,mscArgs,FALSE))!=0)
+ int code;
+ if ((code=mscgen_generate(inFile,outFile,
+ writeSVGMap ? mscgen_format_svgmap : mscgen_format_pngmap))!=0)
{
- portable_sysTimerStop();
- QDir::setCurrent(oldDir);
+ err("Problems generating msc output (error=%s). Look for typos in you msc file %s\n",
+ mscgen_error2str(code),inFile.data());
return "";
}
- portable_sysTimerStop();
-
+
QGString result;
FTextStream tmpout(&result);
convertMapFile(tmpout, outFile, relPath, context);
QDir().remove(outFile);
- QDir::setCurrent(oldDir);
return result.data();
}
@@ -217,7 +189,7 @@ void writeMscImageMapFromFile(FTextStream &t,const QCString &inFile,
default:
t << "unknown";
}
- QCString imap = getMscImageMapFromFile(inFile,outDir,relPath,context);
+ QCString imap = getMscImageMapFromFile(inFile,outDir,relPath,context,format==MSC_SVG);
if (!imap.isEmpty())
{
t << "\" alt=\""
diff --git a/src/outputgen.h b/src/outputgen.h
index 8d9db65..e302f42 100644
--- a/src/outputgen.h
+++ b/src/outputgen.h
@@ -428,16 +428,16 @@ class OutputGenerator : public BaseOutputDocInterface
virtual void startClassDiagram() = 0;
virtual void endClassDiagram(const ClassDiagram &,const char *,const char *) = 0;
virtual void startDotGraph() = 0;
- virtual void endDotGraph(const DotClassGraph &g) = 0;
+ virtual void endDotGraph(DotClassGraph &g) = 0;
virtual void startInclDepGraph() = 0;
- virtual void endInclDepGraph(const DotInclDepGraph &g) = 0;
+ virtual void endInclDepGraph(DotInclDepGraph &g) = 0;
virtual void startGroupCollaboration() = 0;
- virtual void endGroupCollaboration(const DotGroupCollaboration &g) = 0;
+ virtual void endGroupCollaboration(DotGroupCollaboration &g) = 0;
virtual void startCallGraph() = 0;
- virtual void endCallGraph(const DotCallGraph &g) = 0;
+ virtual void endCallGraph(DotCallGraph &g) = 0;
virtual void startDirDepGraph() = 0;
- virtual void endDirDepGraph(const DotDirDeps &g) = 0;
- virtual void writeGraphicalHierarchy(const DotGfxHierarchyTable &g) = 0;
+ virtual void endDirDepGraph(DotDirDeps &g) = 0;
+ virtual void writeGraphicalHierarchy(DotGfxHierarchyTable &g) = 0;
virtual void startQuickIndices() = 0;
virtual void endQuickIndices() = 0;
virtual void writeSplitBar(const char *) = 0;
diff --git a/src/outputlist.cpp b/src/outputlist.cpp
index 1d6db55..0306f94 100644
--- a/src/outputlist.cpp
+++ b/src/outputlist.cpp
@@ -316,12 +316,12 @@ void OutputList::forall(void (OutputGenerator::*func)(a1,a2,a3,a4,a5,a6,a7,a8),a
FORALL1(const char *a1,a1)
FORALL1(char a1,a1)
FORALL1(int a1,a1)
-FORALL1(const DotClassGraph &a1,a1)
-FORALL1(const DotInclDepGraph &a1,a1)
-FORALL1(const DotCallGraph &a1,a1)
-FORALL1(const DotDirDeps &a1,a1)
-FORALL1(const DotGfxHierarchyTable &a1,a1)
-FORALL1(const DotGroupCollaboration &a1,a1)
+FORALL1(DotClassGraph &a1,a1)
+FORALL1(DotInclDepGraph &a1,a1)
+FORALL1(DotCallGraph &a1,a1)
+FORALL1(DotDirDeps &a1,a1)
+FORALL1(DotGfxHierarchyTable &a1,a1)
+FORALL1(DotGroupCollaboration &a1,a1)
FORALL1(SectionTypes a1,a1)
#if defined(HAS_BOOL_TYPE) || defined(Q_HAS_BOOL_TYPE)
FORALL1(bool a1,a1)
diff --git a/src/outputlist.h b/src/outputlist.h
index 1371b3a..35d68a8 100644
--- a/src/outputlist.h
+++ b/src/outputlist.h
@@ -391,25 +391,25 @@ class OutputList : public OutputDocInterface
{ forall(&OutputGenerator::endDescTableData); }
void startDotGraph()
{ forall(&OutputGenerator::startDotGraph); }
- void endDotGraph(const DotClassGraph &g)
+ void endDotGraph(DotClassGraph &g)
{ forall(&OutputGenerator::endDotGraph,g); }
void startInclDepGraph()
{ forall(&OutputGenerator::startInclDepGraph); }
- void endInclDepGraph(const DotInclDepGraph &g)
+ void endInclDepGraph(DotInclDepGraph &g)
{ forall(&OutputGenerator::endInclDepGraph,g); }
void startCallGraph()
{ forall(&OutputGenerator::startCallGraph); }
- void endCallGraph(const DotCallGraph &g)
+ void endCallGraph(DotCallGraph &g)
{ forall(&OutputGenerator::endCallGraph,g); }
void startDirDepGraph()
{ forall(&OutputGenerator::startDirDepGraph); }
- void endDirDepGraph(const DotDirDeps &g)
+ void endDirDepGraph(DotDirDeps &g)
{ forall(&OutputGenerator::endDirDepGraph,g); }
void startGroupCollaboration()
{ forall(&OutputGenerator::startGroupCollaboration); }
- void endGroupCollaboration(const DotGroupCollaboration &g)
+ void endGroupCollaboration(DotGroupCollaboration &g)
{ forall(&OutputGenerator::endGroupCollaboration,g); }
- void writeGraphicalHierarchy(const DotGfxHierarchyTable &g)
+ void writeGraphicalHierarchy(DotGfxHierarchyTable &g)
{ forall(&OutputGenerator::writeGraphicalHierarchy,g); }
void startTextBlock(bool dense=FALSE)
{ forall(&OutputGenerator::startTextBlock,dense); }
@@ -520,12 +520,12 @@ class OutputList : public OutputDocInterface
FORALLPROTO1(char);
FORALLPROTO1(IndexSections);
FORALLPROTO1(int);
- FORALLPROTO1(const DotClassGraph &);
- FORALLPROTO1(const DotInclDepGraph &);
- FORALLPROTO1(const DotCallGraph &);
- FORALLPROTO1(const DotGroupCollaboration &);
- FORALLPROTO1(const DotDirDeps &);
- FORALLPROTO1(const DotGfxHierarchyTable &);
+ FORALLPROTO1(DotClassGraph &);
+ FORALLPROTO1(DotInclDepGraph &);
+ FORALLPROTO1(DotCallGraph &);
+ FORALLPROTO1(DotGroupCollaboration &);
+ FORALLPROTO1(DotDirDeps &);
+ FORALLPROTO1(DotGfxHierarchyTable &);
FORALLPROTO1(SectionTypes);
#if defined(HAS_BOOL_TYPE) || defined(Q_HAS_BOOL_TYPE)
FORALLPROTO1(bool);
diff --git a/src/perlmodgen.cpp b/src/perlmodgen.cpp
index 3813c56..7a804b1 100644
--- a/src/perlmodgen.cpp
+++ b/src/perlmodgen.cpp
@@ -388,8 +388,6 @@ public:
void visitPost(DocXRefItem *);
void visitPre(DocInternalRef *);
void visitPost(DocInternalRef *);
- void visitPre(DocCopy *);
- void visitPost(DocCopy *);
void visitPre(DocText *);
void visitPost(DocText *);
void visitPre(DocHtmlBlockQuote *);
@@ -1385,14 +1383,6 @@ void PerlModDocVisitor::visitPost(DocInternalRef *)
closeItem();
}
-void PerlModDocVisitor::visitPre(DocCopy *)
-{
-}
-
-void PerlModDocVisitor::visitPost(DocCopy *)
-{
-}
-
void PerlModDocVisitor::visitPre(DocText *)
{
}
diff --git a/src/portable.cpp b/src/portable.cpp
index 3dccaed..3d64638 100644
--- a/src/portable.cpp
+++ b/src/portable.cpp
@@ -396,7 +396,7 @@ const char *portable_commandExtension()
bool portable_fileSystemIsCaseSensitive()
{
-#if defined(_WIN32) || defined(macintosh) || defined(__MACOSX__) || defined(__APPLE__)
+#if defined(_WIN32) || defined(macintosh) || defined(__MACOSX__) || defined(__APPLE__) || defined(__CYGWIN__)
return FALSE;
#else
return TRUE;
@@ -463,7 +463,18 @@ void portable_correct_path(void)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
const char *p = portable_getenv("PATH");
+ if (!p) return; // no path nothing to correct
QCString result = substitute(p,'/','\\');
if (result!=p) portable_setenv("PATH",result.data());
#endif
}
+
+void portable_unlink(const char *fileName)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ _unlink(fileName);
+#else
+ unlink(fileName);
+#endif
+}
+
diff --git a/src/portable.h b/src/portable.h
index c5578a3..83f90ef 100644
--- a/src/portable.h
+++ b/src/portable.h
@@ -23,6 +23,7 @@ void portable_unsetenv(const char *variable);
portable_off_t portable_fseek(FILE *f,portable_off_t offset, int whence);
portable_off_t portable_ftell(FILE *f);
FILE * portable_fopen(const char *fileName,const char *mode);
+void portable_unlink(const char *fileName);
char portable_pathSeparator();
char portable_pathListSeparator();
const char * portable_ghostScriptCommand();
diff --git a/src/pre.l b/src/pre.l
index ca2fe2b..6829a92 100644
--- a/src/pre.l
+++ b/src/pre.l
@@ -1089,7 +1089,12 @@ static void expandExpression(QCString &expr,QCString *rest,int pos)
if (g_expandedDict->find(macroName)==0) // expand macro
{
Define *def=DefineManager::instance().isDefined(macroName);
- if (definedTest) // macro name was found after defined
+ if (macroName=="defined")
+ {
+ //printf("found defined inside macro definition '%s'\n",expr.right(expr.length()-p).data());
+ definedTest=TRUE;
+ }
+ else if (definedTest) // macro name was found after defined
{
if (def) expMacro = " 1 "; else expMacro = " 0 ";
replaced=TRUE;
@@ -1118,11 +1123,6 @@ static void expandExpression(QCString &expr,QCString *rest,int pos)
replaced=replaceFunctionMacro(expr,rest,p+l,len,def,expMacro);
len+=l;
}
- else if (macroName=="defined")
- {
- //printf("found defined inside macro definition '%s'\n",expr.right(expr.length()-p).data());
- definedTest=TRUE;
- }
if (replaced) // expand the macro and rescan the expression
{
diff --git a/src/printdocvisitor.h b/src/printdocvisitor.h
index 26bb3e7..b4997fd 100644
--- a/src/printdocvisitor.h
+++ b/src/printdocvisitor.h
@@ -676,16 +676,6 @@ class PrintDocVisitor : public DocVisitor
indent_post();
printf("</internalref>\n");
}
- void visitPre(DocCopy *c)
- {
- indent_pre();
- printf("<copy link=\"%s\">\n",c->link().data());
- }
- void visitPost(DocCopy *)
- {
- indent_post();
- printf("</copy>\n");
- }
void visitPre(DocText *)
{
indent_pre();
diff --git a/src/pyscanner.l b/src/pyscanner.l
index fe94e64..3f98f3a 100644
--- a/src/pyscanner.l
+++ b/src/pyscanner.l
@@ -143,7 +143,7 @@ static void initEntry()
current->stat = gstat;
current->lang = SrcLangExt_Python;
current->setParent(current_root);
- initGroupInfo(current);
+ Doxygen::docGroup.initGroupInfo(current);
gstat = FALSE;
}
@@ -1772,14 +1772,14 @@ static void parseCompounds(Entry *rt)
current = new Entry;
initEntry();
- groupEnterCompound(yyFileName,yyLineNr,ce->name);
+ Doxygen::docGroup.enterCompound(yyFileName,yyLineNr,ce->name);
pyscannerYYlex() ;
g_lexInit=TRUE;
delete current; current=0;
ce->program.resize(0);
- groupLeaveCompound(yyFileName,yyLineNr,ce->name);
+ Doxygen::docGroup.leaveCompound(yyFileName,yyLineNr,ce->name);
}
parseCompounds(ce);
@@ -1839,7 +1839,7 @@ static void parseMain(const char *fileName,const char *fileBuf,Entry *rt)
initParser();
current = new Entry;
- groupEnterFile(yyFileName,yyLineNr);
+ Doxygen::docGroup.enterFile(yyFileName,yyLineNr);
current->reset();
initEntry();
@@ -1848,7 +1848,7 @@ static void parseMain(const char *fileName,const char *fileBuf,Entry *rt)
pyscannerYYlex();
g_lexInit=TRUE;
- groupLeaveFile(yyFileName,yyLineNr);
+ Doxygen::docGroup.leaveFile(yyFileName,yyLineNr);
current_root->program.resize(0);
delete current; current=0;
diff --git a/src/rtfdocvisitor.cpp b/src/rtfdocvisitor.cpp
index 55c03a5..4e89193 100644
--- a/src/rtfdocvisitor.cpp
+++ b/src/rtfdocvisitor.cpp
@@ -532,7 +532,9 @@ void RTFDocVisitor::visit(DocIncOperator *op)
//printf("DocIncOperator: type=%d first=%d, last=%d text=`%s'\n",
// op->type(),op->isFirst(),op->isLast(),op->text().data());
DBG_RTF("{\\comment RTFDocVisitor::visit(DocIncOperator)}\n");
- SrcLangExt langExt = getLanguageFromFileName(m_langExt);
+ QCString locLangExt = getFileNameExtension(op->includeFileName());
+ if (locLangExt.isEmpty()) locLangExt = m_langExt;
+ SrcLangExt langExt = getLanguageFromFileName(locLangExt);
if (op->isFirst())
{
if (!m_hide)
@@ -549,14 +551,14 @@ void RTFDocVisitor::visit(DocIncOperator *op)
popEnabled();
if (!m_hide)
{
- FileDef *fd;
+ FileDef *fd = 0;
if (!op->includeFileName().isEmpty())
{
QFileInfo cfi( op->includeFileName() );
fd = createFileDef( cfi.dirPath().utf8(), cfi.fileName().utf8() );
}
- Doxygen::parserManager->getParser(m_langExt)
+ Doxygen::parserManager->getParser(locLangExt)
->parseCode(m_ci,op->context(),op->text(),langExt,
op->isExample(),op->exampleFile(),
fd, // fileDef
@@ -782,7 +784,6 @@ void RTFDocVisitor::visitPre(DocSimpleSect *s)
// special case 1: user defined title
if (s->type()!=DocSimpleSect::User && s->type()!=DocSimpleSect::Rcs)
{
- m_t << ":";
m_t << "\\par";
m_t << "}"; // end bold
incIndentLevel();
@@ -1377,7 +1378,6 @@ void RTFDocVisitor::visitPre(DocParamSect *s)
default:
ASSERT(0);
}
- m_t << ":";
m_t << "\\par";
m_t << "}" << endl;
bool useTable = s->type()==DocParamSect::Param ||
@@ -1655,18 +1655,6 @@ void RTFDocVisitor::visitPost(DocInternalRef *)
m_t << " ";
}
-void RTFDocVisitor::visitPre(DocCopy *)
-{
- if (m_hide) return;
- DBG_RTF("{\\comment RTFDocVisitor::visitPre(DocCopy)}\n");
-}
-
-void RTFDocVisitor::visitPost(DocCopy *)
-{
- if (m_hide) return;
- DBG_RTF("{\\comment RTFDocVisitor::visitPost(DocCopy)}\n");
-}
-
void RTFDocVisitor::visitPre(DocText *)
{
if (m_hide) return;
diff --git a/src/rtfdocvisitor.h b/src/rtfdocvisitor.h
index b7cc3ea..82e4453 100644
--- a/src/rtfdocvisitor.h
+++ b/src/rtfdocvisitor.h
@@ -126,8 +126,6 @@ class RTFDocVisitor : public DocVisitor
void visitPost(DocXRefItem *);
void visitPre(DocInternalRef *);
void visitPost(DocInternalRef *);
- void visitPre(DocCopy *);
- void visitPost(DocCopy *);
void visitPre(DocText *);
void visitPost(DocText *);
void visitPre(DocHtmlBlockQuote *);
diff --git a/src/rtfgen.cpp b/src/rtfgen.cpp
index bb2075b..2f24ca7 100644
--- a/src/rtfgen.cpp
+++ b/src/rtfgen.cpp
@@ -31,6 +31,10 @@
#include "diagram.h"
#include "language.h"
#include "dot.h"
+#include "dotcallgraph.h"
+#include "dotclassgraph.h"
+#include "dotdirdeps.h"
+#include "dotincldepgraph.h"
#include "version.h"
#include "pagedef.h"
#include "rtfstyle.h"
@@ -44,6 +48,8 @@
#include "filename.h"
#include "namespacedef.h"
+static bool DoxyCodeLineOpen = FALSE;
+
//#define DBG_RTF(x) x;
#define DBG_RTF(x)
@@ -1948,6 +1954,9 @@ void RTFGenerator::endCodeFragment()
//styleStack.pop();
//printf("RTFGenerator::endCodeFrament() top=%s\n",styleStack.top());
//t << rtf_Style_Reset << styleStack.top() << endl;
+ //endCodeLine checks is there is still an open code line, if so closes it.
+ endCodeLine();
+
DBG_RTF(t << "{\\comment (endCodeFragment) }" << endl)
t << "}" << endl;
m_omitParagraph = TRUE;
@@ -2497,7 +2506,7 @@ void RTFGenerator::startDotGraph()
DBG_RTF(t << "{\\comment (startDotGraph)}" << endl)
}
-void RTFGenerator::endDotGraph(const DotClassGraph &g)
+void RTFGenerator::endDotGraph(DotClassGraph &g)
{
newParagraph();
@@ -2521,7 +2530,7 @@ void RTFGenerator::startInclDepGraph()
DBG_RTF(t << "{\\comment (startInclDepGraph)}" << endl)
}
-void RTFGenerator::endInclDepGraph(const DotInclDepGraph &g)
+void RTFGenerator::endInclDepGraph(DotInclDepGraph &g)
{
newParagraph();
@@ -2543,7 +2552,7 @@ void RTFGenerator::startGroupCollaboration()
{
}
-void RTFGenerator::endGroupCollaboration(const DotGroupCollaboration &)
+void RTFGenerator::endGroupCollaboration(DotGroupCollaboration &)
{
}
@@ -2552,7 +2561,7 @@ void RTFGenerator::startCallGraph()
DBG_RTF(t << "{\\comment (startCallGraph)}" << endl)
}
-void RTFGenerator::endCallGraph(const DotCallGraph &g)
+void RTFGenerator::endCallGraph(DotCallGraph &g)
{
newParagraph();
@@ -2575,7 +2584,7 @@ void RTFGenerator::startDirDepGraph()
DBG_RTF(t << "{\\comment (startDirDepGraph)}" << endl)
}
-void RTFGenerator::endDirDepGraph(const DotDirDeps &g)
+void RTFGenerator::endDirDepGraph(DotDirDeps &g)
{
newParagraph();
@@ -3037,6 +3046,22 @@ void RTFGenerator::endInlineMemberDoc()
t << "\\cell }{\\row }" << endl;
}
+void RTFGenerator::writeLineNumber(const char *,const char *,const char *,int l)
+{
+ DoxyCodeLineOpen = TRUE;
+ t << QString("%1").arg(l,5) << " ";
+}
+void RTFGenerator::startCodeLine(bool)
+{
+ DoxyCodeLineOpen = TRUE;
+ col=0;
+}
+void RTFGenerator::endCodeLine()
+{
+ if (DoxyCodeLineOpen) lineBreak();
+ DoxyCodeLineOpen = FALSE;
+}
+
void RTFGenerator::startLabels()
{
}
diff --git a/src/rtfgen.h b/src/rtfgen.h
index 3f05821..b5f06f0 100644
--- a/src/rtfgen.h
+++ b/src/rtfgen.h
@@ -127,9 +127,9 @@ class RTFGenerator : public OutputGenerator
void writeAnchor(const char *fileName,const char *name);
void startCodeFragment();
void endCodeFragment();
- void writeLineNumber(const char *,const char *,const char *,int l) { t << QString("%1").arg(l,5) << " "; }
- void startCodeLine(bool) { col=0; }
- void endCodeLine() { lineBreak(); }
+ void writeLineNumber(const char *,const char *,const char *,int l);
+ void startCodeLine(bool);
+ void endCodeLine();
void startEmphasis() { t << "{\\i "; }
void endEmphasis() { t << "}"; }
void startBold() { t << "{\\b "; }
@@ -202,16 +202,16 @@ class RTFGenerator : public OutputGenerator
void endDescTableData();
void startDotGraph();
- void endDotGraph(const DotClassGraph &);
+ void endDotGraph(DotClassGraph &);
void startInclDepGraph();
- void endInclDepGraph(const DotInclDepGraph &);
+ void endInclDepGraph(DotInclDepGraph &);
void startGroupCollaboration();
- void endGroupCollaboration(const DotGroupCollaboration &g);
+ void endGroupCollaboration(DotGroupCollaboration &g);
void startCallGraph();
- void endCallGraph(const DotCallGraph &);
+ void endCallGraph(DotCallGraph &);
void startDirDepGraph();
- void endDirDepGraph(const DotDirDeps &g);
- void writeGraphicalHierarchy(const DotGfxHierarchyTable &) {}
+ void endDirDepGraph(DotDirDeps &g);
+ void writeGraphicalHierarchy(DotGfxHierarchyTable &) {}
void startMemberGroupHeader(bool);
void endMemberGroupHeader();
diff --git a/src/scanner.l b/src/scanner.l
index f94e4f8..fcc7b8e 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -78,6 +78,7 @@ static int lastHereDocContext;
static int lastDefineContext;
static int lastAlignAsContext;
static int lastC11AttributeContext;
+static int lastModifierContext;
static Protection protection;
static Protection baseProt;
static int sharpCount = 0 ;
@@ -246,7 +247,7 @@ static void initEntry()
// //printf("Appending group %s\n",autoGroupStack.top()->groupname.data());
// current->groups->append(new Grouping(*autoGroupStack.top()));
//}
- initGroupInfo(current);
+ Doxygen::docGroup.initGroupInfo(current);
isTypedef=FALSE;
}
@@ -766,6 +767,7 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP})
/** Slice states */
+%x SliceOptional
%x SliceMetadata
%x SliceSequence
%x SliceSequenceName
@@ -1390,6 +1392,10 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP})
current->explicitExternal = TRUE;
lineCount();
}
+<FindMembers>{B}*"const"{BN}+ { current->type += " const ";
+ if (insideCS) current->stat = TRUE;
+ lineCount();
+ }
<FindMembers>{B}*"virtual"{BN}+ { current->type += " virtual ";
current->virt = Virtual;
lineCount();
@@ -2435,6 +2441,19 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP})
current->type+=yytext;
BEGIN(DeclType);
}
+ else if (insideSlice && qstrcmp(yytext,"optional")==0)
+ {
+ if (current->type.isEmpty())
+ {
+ current->type = "optional";
+ }
+ else
+ {
+ current->type += " optional";
+ }
+ lastModifierContext = YY_START;
+ BEGIN(SliceOptional);
+ }
else
{
if (YY_START==FindMembers)
@@ -2835,12 +2854,12 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP})
if (previous && previous->section==Entry::GROUPDOC_SEC)
{
// link open command to the group defined in the previous entry
- openGroup(previous,yyFileName,yyLineNr);
+ Doxygen::docGroup.open(previous,yyFileName,yyLineNr);
}
else
{
// link open command to the current entry
- openGroup(current,yyFileName,yyLineNr);
+ Doxygen::docGroup.open(current,yyFileName,yyLineNr);
}
//current = tmp;
initEntry();
@@ -2884,7 +2903,7 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP})
}
<FindMembers,FindFields,ReadInitializer>"//"([!/]?){B}*{CMD}"}".*|"/*"([!*]?){B}*{CMD}"}"[^*]*"*/" {
bool insideEnum = YY_START==FindFields || (YY_START==ReadInitializer && lastInitializerContext==FindFields); // see bug746226
- closeGroup(current,yyFileName,yyLineNr,insideEnum);
+ Doxygen::docGroup.close(current,yyFileName,yyLineNr,insideEnum);
}
<FindMembers>"=" { // in PHP code this could also be due to "<?="
current->bodyLine = yyLineNr;
@@ -3589,6 +3608,20 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP})
BEGIN (lastSquareContext);
}
}
+<SliceOptional>"(" {
+ current->type += "(";
+ roundCount++;
+ }
+<SliceOptional>[0-9]+ {
+ current->type += yytext;
+ }
+<SliceOptional>")" {
+ current->type += ")";
+ if(--roundCount<=0)
+ {
+ BEGIN (lastModifierContext);
+ }
+ }
<IDLAttribute>"]" {
// end of IDL function attribute
if (--squareCount<=0)
@@ -7174,13 +7207,13 @@ static void parseCompounds(Entry *rt)
//memberGroupId = DOX_NOGROUP;
//memberGroupRelates.resize(0);
//memberGroupInside.resize(0);
- groupEnterCompound(yyFileName,yyLineNr,ce->name);
+ Doxygen::docGroup.enterCompound(yyFileName,yyLineNr,ce->name);
scannerYYlex() ;
g_lexInit=TRUE;
//forceEndGroup();
- groupLeaveCompound(yyFileName,yyLineNr,ce->name);
+ Doxygen::docGroup.leaveCompound(yyFileName,yyLineNr,ce->name);
delete current; current=0;
ce->program.resize(0);
@@ -7240,7 +7273,7 @@ static void parseMain(const char *fileName,
current_root = rt ;
initParser();
- groupEnterFile(yyFileName,yyLineNr);
+ Doxygen::docGroup.enterFile(yyFileName,yyLineNr);
current = new Entry;
//printf("current=%p current_root=%p\n",current,current_root);
int sec=guessSection(yyFileName);
@@ -7272,7 +7305,7 @@ static void parseMain(const char *fileName,
}
//forceEndGroup();
- groupLeaveFile(yyFileName,yyLineNr);
+ Doxygen::docGroup.leaveFile(yyFileName,yyLineNr);
//if (depthIf>0)
//{
diff --git a/src/searchindex.cpp b/src/searchindex.cpp
index babefe9..e5f9ac8 100644
--- a/src/searchindex.cpp
+++ b/src/searchindex.cpp
@@ -956,6 +956,7 @@ void createJavascriptSearchIndex()
void writeJavascriptSearchIndex()
{
int i;
+ int cnt = 0;
// write index files
QCString searchDirName = Config_getString(HTML_OUTPUT)+"/search";
@@ -1043,7 +1044,7 @@ void writeJavascriptSearchIndex()
}
firstEntry=FALSE;
- ti << " ['" << dl->id() << "',['" << convertToXML(dl->name()) << "',[";
+ ti << " ['" << dl->id() << "_" << cnt++ << "',['" << convertToXML(dl->name()) << "',[";
if (dl->count()==1) // item with a unique name
{
diff --git a/src/sqlite3gen.cpp b/src/sqlite3gen.cpp
index 9805d7f..5a8e81d 100644
--- a/src/sqlite3gen.cpp
+++ b/src/sqlite3gen.cpp
@@ -2009,7 +2009,11 @@ static void generateSqlite3ForClass(const ClassDef *cd)
if (nm.isEmpty() && ii->fileDef) nm = ii->fileDef->docName();
if (!nm.isEmpty())
{
- int header_id=insertPath(ii->fileDef->absFilePath(),!ii->fileDef->isReference());
+ int header_id=-1;
+ if (ii->fileDef)
+ {
+ insertPath(ii->fileDef->absFilePath(),!ii->fileDef->isReference());
+ }
DBG_CTX(("-----> ClassDef includeInfo for %s\n", nm.data()));
DBG_CTX((" local : %d\n", ii->local));
DBG_CTX((" imported : %d\n", ii->imported));
diff --git a/src/tclscanner.l b/src/tclscanner.l
index df52201..9ec512a 100644
--- a/src/tclscanner.l
+++ b/src/tclscanner.l
@@ -486,7 +486,7 @@ Entry* tcl_entry_new()
// myEntry->stat = FALSE;
myEntry->fileName = tcl.file_name;
myEntry->lang = SrcLangExt_Tcl;
- initGroupInfo(myEntry);
+ Doxygen::docGroup.initGroupInfo(myEntry);
// collect entries
if (!tcl.code)
{
@@ -2950,7 +2950,7 @@ tcl_inf("%s\n",fileName);
printlex(yy_flex_debug, TRUE, __FILE__, fileName);
msg("Parsing %s...\n",fileName);
- groupEnterFile(fileName,yylineno);
+ Doxygen::docGroup.enterFile(fileName,yylineno);
tcl_init();
tcl.code = NULL;
@@ -2958,7 +2958,7 @@ tcl_inf("%s\n",fileName);
tcl.this_parser = this;
tcl.entry_main = root; /* toplevel entry */
tcl_parse("","");
- groupLeaveFile(tcl.file_name,yylineno);
+ Doxygen::docGroup.leaveFile(tcl.file_name,yylineno);
root->program.resize(0);
myFile.close();
printlex(yy_flex_debug, FALSE, __FILE__, fileName);
diff --git a/src/textdocvisitor.h b/src/textdocvisitor.h
index bbc70e8..c4ba3d7 100644
--- a/src/textdocvisitor.h
+++ b/src/textdocvisitor.h
@@ -125,8 +125,6 @@ class TextDocVisitor : public DocVisitor
void visitPost(DocXRefItem *) {}
void visitPre(DocInternalRef *) {}
void visitPost(DocInternalRef *) {}
- void visitPre(DocCopy *) {}
- void visitPost(DocCopy *) {}
void visitPre(DocText *) {}
void visitPost(DocText *) {}
void visitPre(DocHtmlBlockQuote *) {}
diff --git a/src/util.cpp b/src/util.cpp
index a6ae004..bf02d7e 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -1675,6 +1675,7 @@ struct CharAroundSpace
charMap['='].after=FALSE;
charMap[' '].after=FALSE;
+ charMap['['].after=FALSE;
charMap[']'].after=FALSE;
charMap['\t'].after=FALSE;
charMap['\n'].after=FALSE;
@@ -5129,7 +5130,7 @@ FileDef *findFileDef(const FileNameDict *fnDict,const char *n,bool &ambig)
if (fn->count()==1)
{
FileDef *fd = fn->getFirst();
-#if defined(_WIN32) || defined(__MACOSX__) // Windows or MacOSX
+#if defined(_WIN32) || defined(__MACOSX__) || defined(__CYGWIN__) // Windows or MacOSX
bool isSamePath = fd->getPath().right(path.length()).lower()==path.lower();
#else // Unix
bool isSamePath = fd->getPath().right(path.length())==path;
@@ -5475,10 +5476,12 @@ QCString escapeCharsInString(const char *name,bool allowDots,bool allowUnderscor
case '(': growBuf.addStr("_07"); break;
case ')': growBuf.addStr("_08"); break;
case '+': growBuf.addStr("_09"); break;
- case '=': growBuf.addStr("_0A"); break;
- case '$': growBuf.addStr("_0B"); break;
- case '\\': growBuf.addStr("_0C"); break;
- case '@': growBuf.addStr("_0D"); break;
+ case '=': growBuf.addStr("_0a"); break;
+ case '$': growBuf.addStr("_0b"); break;
+ case '\\': growBuf.addStr("_0c"); break;
+ case '@': growBuf.addStr("_0d"); break;
+ case ']': growBuf.addStr("_0e"); break;
+ case '[': growBuf.addStr("_0f"); break;
default:
if (c<0)
{
@@ -7079,6 +7082,7 @@ QCString latexFilterURL(const char *s)
switch (c)
{
case '#': t << "\\#"; break;
+ case '%': t << "\\%"; break;
default:
t << c;
break;
@@ -7414,24 +7418,28 @@ void addCodeOnlyMappings()
SrcLangExt getLanguageFromFileName(const QCString& fileName)
{
- int i = fileName.findRev('.');
- if (i!=-1) // name has an extension
- {
- QCString extStr=fileName.right(fileName.length()-i).lower();
- if (!extStr.isEmpty()) // non-empty extension
+ QFileInfo fi(fileName);
+ QCString extName = fi.extension().lower().data();
+ if (extName.isEmpty()) extName=".no_extension";
+ if (extName.at(0)!='.') extName.prepend(".");
+ int *pVal=g_extLookup.find(extName.data());
+ if (pVal) // listed extension
{
- int *pVal=g_extLookup.find(extStr);
- if (pVal) // listed extension
- {
- //printf("getLanguageFromFileName(%s)=%x\n",extStr.data(),*pVal);
- return (SrcLangExt)*pVal;
- }
+ //printf("getLanguageFromFileName(%s)=%x\n",fi.extension().data(),*pVal);
+ return (SrcLangExt)*pVal;
}
- }
//printf("getLanguageFromFileName(%s) not found!\n",fileName.data());
return SrcLangExt_Cpp; // not listed => assume C-ish language.
}
+QCString getFileNameExtension(QCString fn)
+{
+ if (fn.isEmpty()) return "";
+ int lastDot = fn.findRev('.');
+ if (lastDot!=-1) return fn.mid(lastDot);
+ return "";
+}
+
//--------------------------------------------------------------------------
MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fileScope,
@@ -8138,7 +8146,7 @@ bool patternMatch(const QFileInfo &fi,const QStrList *patList)
bool found = FALSE;
// For Windows/Mac, always do the case insensitive match
-#if defined(_WIN32) || defined(__MACOSX__)
+#if defined(_WIN32) || defined(__MACOSX__) || defined(__CYGWIN__)
caseSenseNames = FALSE;
#endif
diff --git a/src/util.h b/src/util.h
index 1cd7c9d..81a3b0b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -389,6 +389,7 @@ QCString stripLeadingAndTrailingEmptyLines(const QCString &s,int &docLine);
bool updateLanguageMapping(const QCString &extension,const QCString &parser);
SrcLangExt getLanguageFromFileName(const QCString& fileName);
+QCString getFileNameExtension(QCString fn);
void initDefaultExtensionMapping();
void addCodeOnlyMappings();
diff --git a/src/vhdljjparser.cpp b/src/vhdljjparser.cpp
index 4a312bd..aeed048 100644
--- a/src/vhdljjparser.cpp
+++ b/src/vhdljjparser.cpp
@@ -146,7 +146,7 @@ void VHDLLanguageScanner::parseInput(const char *fileName,const char *fileBuf,En
oldEntry = 0;
VhdlParser::current=new Entry();
VhdlParser::initEntry(VhdlParser::current);
- groupEnterFile(fileName,yyLineNr);
+ Doxygen::docGroup.enterFile(fileName,yyLineNr);
vhdlFileName = fileName;
lineParse=new int[200]; // Dimitri: dangerous constant: should be bigger than largest token id in VhdlParserConstants.h
VhdlParserIF::parseVhdlfile(fileBuf,inLine);
@@ -193,7 +193,7 @@ void VhdlParser::initEntry(Entry *e)
e->fileName = yyFileName;
e->lang = SrcLangExt_VHDL;
isVhdlDocPending();
- initGroupInfo(e);
+ Doxygen::docGroup.initGroupInfo(e);
}
void VhdlParser::newEntry()
diff --git a/src/xmldocvisitor.cpp b/src/xmldocvisitor.cpp
index 1005719..c8d23a8 100644
--- a/src/xmldocvisitor.cpp
+++ b/src/xmldocvisitor.cpp
@@ -415,20 +415,22 @@ void XmlDocVisitor::visit(DocIncOperator *op)
pushEnabled();
m_hide = TRUE;
}
- SrcLangExt langExt = getLanguageFromFileName(m_langExt);
+ QCString locLangExt = getFileNameExtension(op->includeFileName());
+ if (locLangExt.isEmpty()) locLangExt = m_langExt;
+ SrcLangExt langExt = getLanguageFromFileName(locLangExt);
if (op->type()!=DocIncOperator::Skip)
{
popEnabled();
if (!m_hide)
{
- FileDef *fd;
+ FileDef *fd = 0;
if (!op->includeFileName().isEmpty())
{
QFileInfo cfi( op->includeFileName() );
fd = createFileDef( cfi.dirPath().utf8(), cfi.fileName().utf8() );
}
- Doxygen::parserManager->getParser(m_langExt)
+ Doxygen::parserManager->getParser(locLangExt)
->parseCode(m_ci,op->context(),
op->text(),langExt,op->isExample(),
op->exampleFile(),
@@ -1078,18 +1080,6 @@ void XmlDocVisitor::visitPost(DocInternalRef *)
m_t << " ";
}
-void XmlDocVisitor::visitPre(DocCopy *c)
-{
- if (m_hide) return;
- m_t << "<copydoc link=\"" << convertToXML(c->link()) << "\">";
-}
-
-void XmlDocVisitor::visitPost(DocCopy *)
-{
- if (m_hide) return;
- m_t << "</copydoc>" << endl;
-}
-
void XmlDocVisitor::visitPre(DocText *)
{
}
diff --git a/src/xmldocvisitor.h b/src/xmldocvisitor.h
index c2c6537..6fa1392 100644
--- a/src/xmldocvisitor.h
+++ b/src/xmldocvisitor.h
@@ -130,8 +130,6 @@ class XmlDocVisitor : public DocVisitor
void visitPost(DocXRefItem *);
void visitPre(DocInternalRef *);
void visitPost(DocInternalRef *);
- void visitPre(DocCopy *);
- void visitPost(DocCopy *);
void visitPre(DocText *);
void visitPost(DocText *);
void visitPre(DocHtmlBlockQuote *);
diff --git a/src/xmlgen.cpp b/src/xmlgen.cpp
index fe324be..b992b81 100644
--- a/src/xmlgen.cpp
+++ b/src/xmlgen.cpp
@@ -29,6 +29,8 @@
#include "defargs.h"
#include "outputgen.h"
#include "dot.h"
+#include "dotclassgraph.h"
+#include "dotincldepgraph.h"
#include "pagedef.h"
#include "filename.h"
#include "version.h"
@@ -1407,14 +1409,14 @@ static void generateXMLForClass(const ClassDef *cd,FTextStream &ti)
t << " <detaileddescription>" << endl;
writeXMLDocBlock(t,cd->docFile(),cd->docLine(),cd,0,cd->documentation());
t << " </detaileddescription>" << endl;
- DotClassGraph inheritanceGraph(cd,DotNode::Inheritance);
+ DotClassGraph inheritanceGraph(cd,Inheritance);
if (!inheritanceGraph.isTrivial())
{
t << " <inheritancegraph>" << endl;
inheritanceGraph.writeXML(t);
t << " </inheritancegraph>" << endl;
}
- DotClassGraph collaborationGraph(cd,DotNode::Collaboration);
+ DotClassGraph collaborationGraph(cd,Collaboration);
if (!collaborationGraph.isTrivial())
{
t << " <collaborationgraph>" << endl;