diff options
Diffstat (limited to 'src')
107 files changed, 7039 insertions, 9153 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 92a302a..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 @@ -43,6 +45,10 @@ add_custom_command( OUTPUT ${GENERATED_SRC}/configvalues.h ) set_source_files_properties(${GENERATED_SRC}/configvalues.h PROPERTIES GENERATED 1) +add_custom_target( + generate_configvalues_header + DEPENDS ${GENERATED_SRC}/configvalues.h +) # configvalues.cpp add_custom_command( @@ -181,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 @@ -203,7 +219,6 @@ add_library(_doxygen STATIC latexdocvisitor.cpp latexgen.cpp layout.cpp - lodepng.cpp mandocvisitor.cpp mangen.cpp sqlite3gen.cpp @@ -239,6 +254,7 @@ add_library(_doxygen STATIC xmlgen.cpp docbookvisitor.cpp docbookgen.cpp + docgroup.cpp ) add_executable(doxygen main.cpp) @@ -268,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 c6d1bca..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(); @@ -3646,7 +3652,8 @@ void ClassDefImpl::mergeMembers() m_impl->membersMerged=TRUE; //printf(" mergeMembers for %s\n",name().data()); - bool inlineInheritedMembers = Config_getBool(INLINE_INHERITED_MEMB); + static bool inlineInheritedMembers = Config_getBool(INLINE_INHERITED_MEMB); + static bool extractPrivate = Config_getBool(EXTRACT_PRIVATE); if (baseClasses()) { //printf(" => has base classes!\n"); @@ -3697,8 +3704,8 @@ void ClassDefImpl::mergeMembers() if (srcCd==dstCd || dstCd->isBaseClass(srcCd,TRUE)) // member is in the same or a base class { - ArgumentList *srcAl = srcMd->argumentList(); - ArgumentList *dstAl = dstMd->argumentList(); + const ArgumentList *srcAl = srcMd->argumentList(); + const ArgumentList *dstAl = dstMd->argumentList(); found=matchArguments2( srcMd->getOuterScope(),srcMd->getFileDef(),srcAl, dstMd->getOuterScope(),dstMd->getFileDef(),dstAl, @@ -3842,7 +3849,7 @@ void ClassDefImpl::mergeMembers() // name().data(),mi->memberDef->name().data(),mi->prot, // bcd->prot,prot); - if (mi->prot!=Private) + if (prot!=Private || extractPrivate) { Specifier virt=mi->virt; if (mi->virt==Normal && bcd->virt!=Normal) virt=bcd->virt; @@ -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 { @@ -677,10 +677,10 @@ static void addUsingDirective(const char *name) static void setParameterList(const MemberDef *md) { g_classScope = md->getClassDef() ? md->getClassDef()->name().data() : ""; - ArgumentList *al = md->argumentList(); + const ArgumentList *al = md->argumentList(); if (al==0) return; ArgumentListIterator it(*al); - Argument *a; + const Argument *a; for (;(a=it.current());++it) { g_parmName = a->name.copy(); @@ -860,7 +860,7 @@ static bool getLinkInScope(const QCString &c, // scope bool varOnly=FALSE ) { - MemberDef *md = 0; + const MemberDef *md = 0; const ClassDef *cd = 0; const FileDef *fd = 0; const NamespaceDef *nd = 0; @@ -878,7 +878,7 @@ static bool getLinkInScope(const QCString &c, // scope anchor.sprintf("a%d",g_anchorCount); //printf("addExampleFile(%s,%s,%s)\n",anchor.data(),g_exampleName.data(), // g_exampleFile.data()); - if (md->addExample(anchor,g_exampleName,g_exampleFile)) + if (const_cast<MemberDef*>(md)->addExample(anchor,g_exampleName,g_exampleFile)) { ol.writeCodeAnchor(anchor); g_anchorCount++; @@ -897,7 +897,7 @@ static bool getLinkInScope(const QCString &c, // scope if (g_currentDefinition && g_currentMemberDef && md!=g_currentMemberDef && g_insideBody && g_collectXRefs) { - addDocCrossReference(g_currentMemberDef,md); + addDocCrossReference(g_currentMemberDef,const_cast<MemberDef*>(md)); } //printf("d->getReference()=`%s' d->getOutputBase()=`%s' name=`%s' member name=`%s'\n",d->getReference().data(),d->getOutputFileBase().data(),d->name().data(),md->name().data()); @@ -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())); @@ -3553,6 +3553,24 @@ RAWEND ")"[^ \t\(\)\\]{0,16}\" BEGIN(SkipComment); } } +<*>^{B}*"/**"[*]+/[^/] { // special C "banner" comment block at a new line + if (Config_getBool(JAVADOC_BANNER) && Config_getBool(STRIP_CODE_COMMENTS)) + { + g_lastSpecialCContext = YY_START; + BEGIN(RemoveSpecialCComment); + } + else + { + // check is to prevent getting stuck in skipping C++ comments + if (YY_START != SkipCxxComment) + { + g_lastCContext = YY_START ; + } + startFontClass("comment"); + g_code->codify(yytext); + BEGIN(SkipComment); + } + } <*>^{B}*"/*"[!*]/[^/*] { // special C comment block at a new line if (Config_getBool(STRIP_CODE_COMMENTS)) { @@ -3626,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 6b08d74..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)); @@ -399,8 +399,12 @@ void replaceComment(int offset); copyToOutput(yytext,(int)yyleng); } <Scan>"/*"[*!]? { /* start of a C comment */ + if ((g_lang==SrcLangExt_Python) || (g_lang==SrcLangExt_Tcl)) + { + 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); @@ -414,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)); @@ -429,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)); @@ -443,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)); @@ -660,25 +664,30 @@ void replaceComment(int offset); } } <CComment>"/"+"*" { /* nested C comment */ + if ((g_lang==SrcLangExt_Python) || (g_lang==SrcLangExt_Tcl)) + { + REJECT; + } g_nestingCount++; g_commentStack.push(new CommentCtx(g_lineNr)); copyToOutput(yytext,(int)yyleng); } <CComment>"*"+"/" { /* end of C comment */ - if (g_lang==SrcLangExt_Python) + if ((g_lang==SrcLangExt_Python) || (g_lang==SrcLangExt_Tcl)) { REJECT; } else { copyToOutput(yytext,(int)yyleng); + g_nestingCount--; if (g_nestingCount<=0) { BEGIN(Scan); } else { - g_nestingCount--; + //g_nestingCount--; delete g_commentStack.pop(); } } @@ -1111,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 0ca293c..b283a8b 100644 --- a/src/commentscan.l +++ b/src/commentscan.l @@ -147,30 +147,30 @@ static DocCmdMap docCmdMap[] = // command name handler function ends brief description { "brief", &handleBrief, FALSE }, { "short", &handleBrief, FALSE }, - { "fn", &handleFn, FALSE }, - { "var", &handleFn, FALSE }, - { "typedef", &handleFn, FALSE }, - { "property", &handleFn, FALSE }, - { "def", &handleDef, FALSE }, + { "fn", &handleFn, TRUE }, + { "var", &handleFn, TRUE }, + { "typedef", &handleFn, TRUE }, + { "property", &handleFn, TRUE }, + { "def", &handleDef, TRUE }, { "overload", &handleOverload, FALSE }, - { "enum", &handleEnum, FALSE }, - { "defgroup", &handleDefGroup, FALSE }, - { "addtogroup", &handleAddToGroup, FALSE }, - { "weakgroup", &handleWeakGroup, FALSE }, - { "namespace", &handleNamespace, FALSE }, - { "package", &handlePackage, FALSE }, - { "class", &handleClass, FALSE }, + { "enum", &handleEnum, TRUE }, + { "defgroup", &handleDefGroup, TRUE }, + { "addtogroup", &handleAddToGroup, TRUE }, + { "weakgroup", &handleWeakGroup, TRUE }, + { "namespace", &handleNamespace, TRUE }, + { "package", &handlePackage, TRUE }, + { "class", &handleClass, TRUE }, { "headerfile", &handleHeaderFile, FALSE }, - { "protocol", &handleProtocol, FALSE }, - { "category", &handleCategory, FALSE }, - { "union", &handleUnion, FALSE }, - { "struct", &handleStruct, FALSE }, - { "interface", &handleInterface, FALSE }, - { "idlexcept", &handleIdlException, FALSE }, - { "page", &handlePage, FALSE }, - { "mainpage", &handleMainpage, FALSE }, - { "file", &handleFile, FALSE }, - { "dir", &handleDir, FALSE }, + { "protocol", &handleProtocol, TRUE }, + { "category", &handleCategory, TRUE }, + { "union", &handleUnion, TRUE }, + { "struct", &handleStruct, TRUE }, + { "interface", &handleInterface, TRUE }, + { "idlexcept", &handleIdlException, TRUE }, + { "page", &handlePage, TRUE }, + { "mainpage", &handleMainpage, TRUE }, + { "file", &handleFile, TRUE }, + { "dir", &handleDir, TRUE }, { "example", &handleExample, FALSE }, { "details", &handleDetails, TRUE }, { "name", &handleName, FALSE }, @@ -210,7 +210,7 @@ static DocCmdMap docCmdMap[] = { "elseif", &handleElseIf, FALSE }, { "else", &handleElse, FALSE }, { "endif", &handleEndIf, FALSE }, - { "ingroup", &handleIngroup, FALSE }, + { "ingroup", &handleIngroup, TRUE }, { "nosubgrouping", &handleNoSubGrouping, FALSE }, { "showinitializer", &handleShowInitializer, FALSE }, { "hideinitializer", &handleHideInitializer, FALSE }, @@ -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,"@<","<"),"@>",">"); tmp = substitute(substitute(tmp,"\\<","<"),"\\>",">"); - 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); @@ -1895,17 +1890,20 @@ RCSTAG "$"{ID}":"[^\n$]+"$" addOutput('\n'); } <FormatBlock>"/*" { // start of a C-comment - g_commentCount++; + if (!(blockName=="code" || blockName=="verbatim")) g_commentCount++; addOutput(yytext); } <FormatBlock>"*/" { // end of a C-comment addOutput(yytext); - g_commentCount--; - if (g_commentCount<0 && blockName!="verbatim") - { - warn(yyFileName,yyLineNr, + if (!(blockName=="code" || blockName=="verbatim")) + { + g_commentCount--; + if (g_commentCount<0) + { + warn(yyFileName,yyLineNr, "found */ without matching /* while inside a \\%s block! Perhaps a missing \\end%s?\n",blockName.data(),blockName.data()); - } + } + } } <FormatBlock>. { addOutput(*yytext); @@ -1965,6 +1963,7 @@ RCSTAG "$"{ID}":"[^\n$]+"$" addOutput(*yytext); } <GuardParamEnd>{B}*{DOCNL} { + lineCount(); g_spaceBeforeIf.resize(0); BEGIN(Comment); } @@ -2119,10 +2118,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 +2297,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 +2358,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 +2462,7 @@ static bool handleMainpage(const QCString &, const QCStringList &) { current->name = "mainpage"; } + current->name = ""; BEGIN( PageDocArg2 ); return stop; } @@ -2540,11 +2541,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 +3193,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 +3209,7 @@ bool parseCommentBlock(/* in */ ParserInterface *parser, checkFormula(); prot = protection; - groupAddDocs(curEntry); + Doxygen::docGroup.addDocs(curEntry); newEntryNeeded = needNewEntry; @@ -3227,184 +3228,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 8820fe7..8e19d67 100644 --- a/src/config.xml +++ b/src/config.xml @@ -105,7 +105,6 @@ PROJECT_NAME = Example INPUT = example.cc example.h WARNINGS = YES TAGFILES = qt.tag -PERL_PATH = /usr/local/bin/perl SEARCHENGINE = NO \endverbatim @@ -120,7 +119,6 @@ INPUT = examples/examples.doc src FILE_PATTERNS = *.cc *.h INCLUDE_PATH = examples TAGFILES = qt.tag -PERL_PATH = /usr/bin/perl SEARCHENGINE = YES \endverbatim @@ -480,6 +478,17 @@ Go to the <a href="commands.html">next</a> section or return to the ]]> </docs> </option> + <option type='bool' id='JAVADOC_BANNER' defval='0'> + <docs> +<![CDATA[ + If the \c JAVADOC_BANNER tag is set to \c YES then doxygen + will interpret a line such as "/***************" as being the + beginning of a Javadoc-style comment "banner". If set to \c NO, the + Javadoc-style will behave just like regular comments and it will + not be interpreted by doxygen. +]]> + </docs> + </option> <option type='bool' id='QT_AUTOBRIEF' defval='0'> <docs> <![CDATA[ @@ -951,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> @@ -3229,14 +3238,6 @@ where `loc1` and `loc2` can be relative or absolute paths or URLs. ]]> </docs> </option> - <option type='string' id='PERL_PATH' format='file' defval='/usr/bin/perl' abspath='1'> - <docs> -<![CDATA[ - The \c PERL_PATH should be the absolute path and name of the perl script - interpreter (i.e. the result of `'which perl'`). -]]> - </docs> - </option> </group> <group name='Dot' docs='Configuration options related to the dot tool'> <option type='bool' id='CLASS_DIAGRAMS' defval='1'> @@ -3250,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[ @@ -3639,5 +3629,7 @@ remove the intermediate dot files that are used to generate the various graphs. <option type='obsolete' id='SYMBOL_CACHE_SIZE'/> <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/constexp.h b/src/constexp.h index d84e94e..8bf582e 100644 --- a/src/constexp.h +++ b/src/constexp.h @@ -22,12 +22,21 @@ #include "cppvalue.h" #include <qcstring.h> -extern bool parseconstexp(const char *fileName,int line,const QCString &s); -extern int constexpYYparse(); -extern int constexpYYdebug; -extern QCString g_strToken; -extern CPPValue g_resultValue; -extern QCString g_constExpFileName; -extern int g_constExpLineNr; +#define YYSTYPE CPPValue +typedef void* yyscan_t; +struct constexpYY_state +{ + QCString g_strToken; + CPPValue g_resultValue; + int g_constExpLineNr; + QCString g_constExpFileName; + + const char *g_inputString; + int g_inputPosition; +}; +extern bool parseconstexp(const char *fileName,int line,const QCString &s); +extern int constexpYYparse(yyscan_t); +extern int constexpYYlex(YYSTYPE *lvalp, yyscan_t); +struct constexpYY_state* constexpYYget_extra (yyscan_t yyscanner ); #endif diff --git a/src/constexp.l b/src/constexp.l index 8a7db04..bd42104 100644 --- a/src/constexp.l +++ b/src/constexp.l @@ -17,44 +17,30 @@ */ %option never-interactive %option prefix="constexpYY" +%option nounput +%option reentrant bison-bridge +%option extra-type="struct constexpYY_state *" %{ #include "constexp.h" #include "cppvalue.h" -#include "ce_parse.h" // generated header file +#include "ce_parse.hpp" // generated header file #include "message.h" #define YY_NO_INPUT 1 #define YY_NO_UNISTD_H 1 - -QCString g_strToken; -CPPValue g_resultValue; -int g_constExpLineNr; -QCString g_constExpFileName; -static const char *g_inputString; -static int g_inputPosition; -#undef YY_INPUT -#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); +static int yyread(char *buf,int max_size,yyscan_t yyscanner); -static int yyread(char *buf,int max_size) -{ - int c=0; - while( c < max_size && g_inputString[g_inputPosition] ) - { - *buf = g_inputString[g_inputPosition++] ; - c++; buf++; - } - return c; -} +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size,yyscanner); %} CONSTSUFFIX ([uU][lL]?[lL]?)|([lL][lL]?[uU]?) -%option nounput %% @@ -83,44 +69,73 @@ CONSTSUFFIX ([uU][lL]?[lL]?)|([lL][lL]?[uU]?) "(" { return TOK_LPAREN; } ")" { return TOK_RPAREN; } "'"(([^\'\n\r\\]+)|(\\(([ntvbrfa\\?'\"])|([0-9]+)|([xX][0-9a-fA-F]+))))"'" { - g_strToken=yytext; + yyextra->g_strToken=yytext; return TOK_CHARACTER; } -0[0-7]*{CONSTSUFFIX}? { g_strToken=yytext; +0[0-7]*{CONSTSUFFIX}? { yyextra->g_strToken=yytext; return TOK_OCTALINT; } -[1-9][0-9]*{CONSTSUFFIX}? { g_strToken=yytext; +[1-9][0-9]*{CONSTSUFFIX}? { yyextra->g_strToken=yytext; return TOK_DECIMALINT; } -(0x|0X)[0-9a-fA-F]+{CONSTSUFFIX}? { g_strToken=yytext+2; +(0x|0X)[0-9a-fA-F]+{CONSTSUFFIX}? { yyextra->g_strToken=yytext+2; return TOK_HEXADECIMALINT; } (([0-9]+\.[0-9]*)|([0-9]*\.[0-9]+))([eE]([\-\+])?[0-9]+)?([fFlL])? { - g_strToken=yytext; return TOK_FLOAT; + yyextra->g_strToken=yytext; return TOK_FLOAT; } ([0-9]+[eE])([\-\+])?[0-9]+([fFlL])? { - g_strToken=yytext; return TOK_FLOAT; + yyextra->g_strToken=yytext; return TOK_FLOAT; } . \n %% +static int yyread(char *buf,int max_size,yyscan_t yyscanner) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + int c=0; + while( c < max_size && yyextra->g_inputString[yyextra->g_inputPosition] ) + { + *buf = yyextra->g_inputString[yyextra->g_inputPosition++] ; + c++; buf++; + } + return c; +} + + +static yyscan_t yyscanner; +static struct constexpYY_state constexpYY_extra; + bool parseconstexp(const char *fileName,int lineNr,const QCString &s) { + constexpYYlex_init_extra(&constexpYY_extra, &yyscanner); + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + +#ifdef FLEX_DEBUG + yyset_debug(1,yyscanner); +#endif + + yyextra->g_constExpFileName = fileName; + yyextra->g_constExpLineNr = lineNr; + yyextra->g_inputString = s; + yyextra->g_inputPosition = 0; + constexpYYrestart( yyin, yyscanner ); + printlex(yy_flex_debug, TRUE, __FILE__, fileName); //printf("Expression: `%s'\n",s.data()); - g_constExpFileName = fileName; - g_constExpLineNr = lineNr; - g_inputString = s; - g_inputPosition = 0; - constexpYYrestart( constexpYYin ); - constexpYYparse(); + + constexpYYparse(yyscanner); + //printf("Result: %ld\n",(long)g_resultValue); printlex(yy_flex_debug, FALSE, __FILE__, fileName); - return (long)g_resultValue!=0; + bool result = (long)yyextra->g_resultValue!=0; + + constexpYYlex_destroy(yyscanner); + return result; } extern "C" { - int constexpYYwrap() { return 1; } + int constexpYYwrap(yyscan_t yyscanner) { return 1; } } diff --git a/src/constexp.y b/src/constexp.y index f87ebf3..62a51f3 100644 --- a/src/constexp.y +++ b/src/constexp.y @@ -26,24 +26,24 @@ #define MSDOS #endif -#define YYSTYPE CPPValue #include <stdio.h> #include <stdlib.h> -int constexpYYerror(const char *s) +int constexpYYerror(yyscan_t yyscanner, const char *s) { - warn(g_constExpFileName,g_constExpLineNr, + struct constexpYY_state* yyextra = constexpYYget_extra(yyscanner); + warn(yyextra->g_constExpFileName, yyextra->g_constExpLineNr, "preprocessing issue while doing constant expression evaluation: %s",s); return 0; } -int constexpYYlex(); - %} -%no-lines -%name-prefix="constexpYY" +%name-prefix "constexpYY" +%define api.pure full +%lex-param {yyscan_t yyscanner} +%parse-param {yyscan_t yyscanner} %token TOK_QUESTIONMARK %token TOK_COLON @@ -78,7 +78,10 @@ int constexpYYlex(); %% start: constant_expression - { g_resultValue = $1; return 0; } + { + struct constexpYY_state* yyextra = constexpYYget_extra(yyscanner); + yyextra->g_resultValue = $1; return 0; + } ; constant_expression: logical_or_expression @@ -267,15 +270,30 @@ primary_expression: constant ; constant: TOK_OCTALINT - { $$ = parseOctal(); } + { + struct constexpYY_state* yyextra = constexpYYget_extra(yyscanner); + $$ = parseOctal(yyextra->g_strToken); + } | TOK_DECIMALINT - { $$ = parseDecimal(); } + { + struct constexpYY_state* yyextra = constexpYYget_extra(yyscanner); + $$ = parseDecimal(yyextra->g_strToken); + } | TOK_HEXADECIMALINT - { $$ = parseHexadecimal(); } + { + struct constexpYY_state* yyextra = constexpYYget_extra(yyscanner); + $$ = parseHexadecimal(yyextra->g_strToken); + } | TOK_CHARACTER - { $$ = parseCharacter(); } + { + struct constexpYY_state* yyextra = constexpYYget_extra(yyscanner); + $$ = parseCharacter(yyextra->g_strToken); + } | TOK_FLOAT - { $$ = parseFloat(); } + { + struct constexpYY_state* yyextra = constexpYYget_extra(yyscanner); + $$ = parseFloat(yyextra->g_strToken); + } ; %% diff --git a/src/context.cpp b/src/context.cpp index 6ecf142..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(); } @@ -4638,7 +4644,7 @@ class MemberContext::Private : public DefinitionContext<MemberContext::Private> return createLinkedText(m_memberDef,relPathAsString(), m_memberDef->displayDefinition()); } - ArgumentList *getDefArgList() const + const ArgumentList *getDefArgList() const { return (m_memberDef->isDocsForDefinition()) ? m_memberDef->argumentList() : m_memberDef->declArgumentList(); @@ -4648,7 +4654,7 @@ class MemberContext::Private : public DefinitionContext<MemberContext::Private> Cachable &cache = getCache(); if (!cache.arguments) { - ArgumentList *defArgList = getDefArgList(); + const ArgumentList *defArgList = getDefArgList(); if (defArgList && !m_memberDef->isProperty()) { cache.arguments.reset(ArgumentListContext::alloc(defArgList,m_memberDef,relPathAsString())); @@ -4666,27 +4672,27 @@ class MemberContext::Private : public DefinitionContext<MemberContext::Private> } TemplateVariant hasConstQualifier() const { - ArgumentList *al = getDefArgList(); + const ArgumentList *al = getDefArgList(); return al ? al->constSpecifier : FALSE; } TemplateVariant hasVolatileQualifier() const { - ArgumentList *al = getDefArgList(); + const ArgumentList *al = getDefArgList(); return al ? al->volatileSpecifier : FALSE; } TemplateVariant hasRefQualifierLValue() const { - ArgumentList *al = getDefArgList(); + const ArgumentList *al = getDefArgList(); return al ? al->refQualifier==RefQualifierLValue : FALSE; } TemplateVariant hasRefQualifierRValue() const { - ArgumentList *al = getDefArgList(); + const ArgumentList *al = getDefArgList(); return al ? al->refQualifier==RefQualifierRValue : FALSE; } TemplateVariant trailingReturnType() const { - ArgumentList *al = getDefArgList(); + const ArgumentList *al = getDefArgList(); if (al && !al->trailingReturnType.isEmpty()) { return createLinkedText(m_memberDef,relPathAsString(), diff --git a/src/cppvalue.cpp b/src/cppvalue.cpp index 176931d..1543498 100644 --- a/src/cppvalue.cpp +++ b/src/cppvalue.cpp @@ -21,44 +21,44 @@ #include "cppvalue.h" #include "constexp.h" -CPPValue parseOctal() +CPPValue parseOctal(const QCString& token) { long val = 0; - for (const char *p = g_strToken.data(); *p != 0; p++) + for (const char *p = token.data(); *p != 0; p++) { if (*p >= '0' && *p <= '7') val = val * 8 + *p - '0'; } return CPPValue(val); } -CPPValue parseDecimal() +CPPValue parseDecimal(const QCString& token) { long val = 0; - for (const char *p = g_strToken.data(); *p != 0; p++) + for (const char *p = token.data(); *p != 0; p++) { if (*p >= '0' && *p <= '9') val = val * 10 + *p - '0'; } return CPPValue(val); } -CPPValue parseHexadecimal() +CPPValue parseHexadecimal(const QCString& token) { long val = 0; - for (const char *p = g_strToken.data(); *p != 0; p++) + for (const char *p = token.data(); *p != 0; p++) { if (*p >= '0' && *p <= '9') val = val * 16 + *p - '0'; else if (*p >= 'a' && *p <= 'f') val = val * 16 + *p - 'a' + 10; else if (*p >= 'A' && *p <= 'F') val = val * 16 + *p - 'A' + 10; } - //printf("parseHexadecimal %s->%x\n",g_strToken.data(),val); + //printf("parseHexadecimal %s->%x\n",token.data(),val); return CPPValue(val); } -CPPValue parseCharacter() // does not work for '\n' and the alike +CPPValue parseCharacter(const QCString& token) // does not work for '\n' and the alike { - if (g_strToken[1]=='\\') + if (token[1]=='\\') { - switch(g_strToken[2]) + switch(token[2]) { case 'n': return CPPValue((long)'\n'); case 't': return CPPValue((long)'\t'); @@ -79,17 +79,17 @@ CPPValue parseCharacter() // does not work for '\n' and the alike case '5': // fall through case '6': // fall through case '7': // fall through - return parseOctal(); + return parseOctal(token); case 'x': - case 'X': return parseHexadecimal(); - default: printf("Invalid escape sequence %s found!\n",g_strToken.data()); + case 'X': return parseHexadecimal(token); + default: printf("Invalid escape sequence %s found!\n",token.data()); return CPPValue(0L); } } - return CPPValue((long)g_strToken[1]); + return CPPValue((long)token[1]); } -CPPValue parseFloat() +CPPValue parseFloat(const QCString& token) { - return CPPValue(atof(g_strToken)); + return CPPValue(atof(token)); } diff --git a/src/cppvalue.h b/src/cppvalue.h index 59dd594..cde033d 100644 --- a/src/cppvalue.h +++ b/src/cppvalue.h @@ -21,6 +21,7 @@ #include <stdio.h> #include <qglobal.h> +#include <qcstring.h> /** A class representing a C-preprocessor value. */ class CPPValue @@ -52,10 +53,10 @@ class CPPValue } v; }; -extern CPPValue parseOctal(); -extern CPPValue parseDecimal(); -extern CPPValue parseHexadecimal(); -extern CPPValue parseCharacter(); -extern CPPValue parseFloat(); +extern CPPValue parseOctal(const QCString& token); +extern CPPValue parseDecimal(const QCString& token); +extern CPPValue parseHexadecimal(const QCString& token); +extern CPPValue parseCharacter(const QCString& token); +extern CPPValue parseFloat(const QCString& token); #endif diff --git a/src/declinfo.l b/src/declinfo.l index a91f832..d7f8743 100644 --- a/src/declinfo.l +++ b/src/declinfo.l @@ -16,6 +16,10 @@ */ %option never-interactive %option prefix="declinfoYY" +%option nounput +%option noyywrap +%option reentrant +%option extra-type="struct declinfoYY_state *" %{ @@ -33,79 +37,43 @@ #define YY_NO_INPUT 1 #define YY_NO_UNISTD_H 1 +#define YY_NEVER_INTERACTIVE 1 /* ----------------------------------------------------------------- * * statics */ - -static const char * inputString; -static int inputPosition; -static QCString scope; -static QCString className; -static QCString classTempList; -static QCString funcTempList; -static QCString type; -static QCString name; -static QCString args; -static int sharpCount; -static bool classTempListFound; -static bool funcTempListFound; -static QCString exceptionString; -static bool insideObjC; - -static void addType() -{ - //printf("addType() type=`%s' scope=`%s' name=`%s'\n", - // type.data(),scope.data(),name.data()); - if (name.isEmpty() && scope.isEmpty()) return; - if (!type.isEmpty()) type+=" "; - if (!scope.isEmpty()) type+=scope+"::"; - type+=name; - scope.resize(0); - name.resize(0); -} - -static void addTypeName() +struct declinfoYY_state { - //printf("addTypeName() type=`%s' scope=`%s' name=`%s'\n", - // type.data(),scope.data(),name.data()); - if (name.isEmpty() || - name.at(name.length()-1)==':') // end of Objective-C keyword => append to name not type - { - return; - } - if (!type.isEmpty()) type+=' '; - type+=name; - name.resize(0); -} - -#define YY_NEVER_INTERACTIVE 1 - + const char *inputString; + int inputPosition; + QCString scope; + QCString className; + QCString classTempList; + QCString funcTempList; + QCString type; + QCString name; + QCString args; + int sharpCount; + bool classTempListFound; + bool funcTempListFound; + QCString exceptionString; + bool insideObjC; +}; + +static void addType(yyscan_t yyscanner); +static void addTypeName(yyscan_t yyscanner); +static int yyread(char *buf,int max_size, yyscan_t yyscanner); + /* ----------------------------------------------------------------- */ #undef YY_INPUT -#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); - -static int yyread(char *buf,int max_size) -{ - int c=0; - while( c < max_size && inputString[inputPosition] ) - { - *buf = inputString[inputPosition++] ; - c++; buf++; - } - return c; -} - +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size,yyscanner); %} B [ \t] ID "$"?([a-z_A-Z\x80-\xFF][a-z_A-Z0-9\x80-\xFF]*)|(@[0-9]+) -%option nounput -%option noyywrap - %x Start %x Template %x ReadArgs @@ -119,17 +87,17 @@ ID "$"?([a-z_A-Z\x80-\xFF][a-z_A-Z0-9\x80-\xFF]*)|(@[0-9]+) %% <Start>"operator"/({B}*"["{B}*"]")* { // operator rule must be before {ID} rule - name += yytext; + yyextra->name += yytext; BEGIN(Operator); } <Start>{ID}{B}*"("{B}*{ID}{B}*")" { // Objective-C class categories - if (!insideObjC) + if (!yyextra->insideObjC) { REJECT; } else { - name += yytext; + yyextra->name += yytext; } } <Start>([~!]{B}*)?{ID}/({B}*"["{B}*"]")* { // the []'s are for Java, @@ -137,143 +105,195 @@ ID "$"?([a-z_A-Z\x80-\xFF][a-z_A-Z0-9\x80-\xFF]*)|(@[0-9]+) // dimensional C++ arrays like A[][15] // the leading ~ is for a destructor // the leading ! is for a C++/CLI finalizer (see bug 456475 and 635198) - addTypeName(); - name += yytext; + addTypeName(yyscanner); + yyextra->name += yytext; } -<Start>{B}*"::"{B}* { // found a scope specifier - if (!scope.isEmpty()) +<Start>{B}*"::"{B}* { // found a yyextra->scope specifier + if (!yyextra->scope.isEmpty()) { - scope+="::"+name; // add name to scope + yyextra->scope+="::"+yyextra->name; // add yyextra->name to yyextra->scope } else { - scope = name.copy(); // scope becomes name + yyextra->scope = yyextra->name.copy(); // yyextra->scope becomes yyextra->name } - name.resize(0); + yyextra->name.resize(0); } <Start>{B}*":" { // Objective-C argument separator - name+=yytext; + yyextra->name+=yytext; } <Start>[*&]+ { - addType(); - type+=yytext; + addType(yyscanner); + yyextra->type+=yytext; } <Start>{B}+ { - addType(); + addType(yyscanner); } <Start>{B}*"("({ID}"::")*{B}*[&*]({B}*("const"|"volatile"){B}+)? { - addType(); + addType(yyscanner); QCString text=yytext; - type+=text.stripWhiteSpace(); + yyextra->type+=text.stripWhiteSpace(); } <Start>{B}*")" { - type+=")"; + yyextra->type+=")"; } <Start>{B}*"(" { // TODO: function pointers - args+="("; + yyextra->args+="("; BEGIN(ReadArgs); } <Start>{B}*"[" { - args+="["; + yyextra->args+="["; BEGIN(ReadArgs); } <Start>{B}*"<" { - name+="<"; - sharpCount=0; + yyextra->name+="<"; + yyextra->sharpCount=0; BEGIN(Template); } -<Template>"<<" { name+="<<"; } -<Template>">>" { name+=">>"; } +<Template>"<<" { yyextra->name+="<<"; } +<Template>">>" { yyextra->name+=">>"; } <Template>"<" { - name+="<"; - sharpCount++; + yyextra->name+="<"; + yyextra->sharpCount++; } <Template>">" { - name+=">"; - if (sharpCount) - --sharpCount; + yyextra->name+=">"; + if (yyextra->sharpCount) + --yyextra->sharpCount; else { BEGIN(Start); } } <Template>. { - name+=*yytext; + yyextra->name+=*yytext; } <Operator>{B}*"("{B}*")"{B}*"<>"{B}*/"(" { - name+="() <>"; + yyextra->name+="() <>"; BEGIN(ReadArgs); } <Operator>{B}*"("{B}*")"{B}*/"(" { - name+="()"; + yyextra->name+="()"; BEGIN(ReadArgs); } <Operator>[^(]*{B}*("<>"{B}*)?/"(" { - name+=yytext; + yyextra->name+=yytext; BEGIN(ReadArgs); } <ReadArgs>"throw"{B}*"(" { - exceptionString="throw("; + yyextra->exceptionString="throw("; BEGIN(ReadExceptions); } <ReadArgs>. { - args+=*yytext; + yyextra->args+=*yytext; } <ReadExceptions>. { - exceptionString+=*yytext; + yyextra->exceptionString+=*yytext; } <*>. <*>\n %% -/*@ ---------------------------------------------------------------------------- +static void addType(yyscan_t yyscanner) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + //printf("addType() yyextra->type=`%s' yyextra->scope=`%s' yyextra->name=`%s'\n", + // yyextra->type.data(),yyextra->scope.data(),yyextra->name.data()); + if (yyextra->name.isEmpty() && yyextra->scope.isEmpty()) return; + if (!yyextra->type.isEmpty()) yyextra->type+=" "; + if (!yyextra->scope.isEmpty()) yyextra->type+=yyextra->scope+"::"; + yyextra->type+=yyextra->name; + yyextra->scope.resize(0); + yyextra->name.resize(0); +} + +static void addTypeName(yyscan_t yyscanner) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + //printf("addTypeName() yyextra->type=`%s' yyextra->scope=`%s' yyextra->name=`%s'\n", + // yyextra->type.data(),yyextra->scope.data(),yyextra->name.data()); + if (yyextra->name.isEmpty() || + yyextra->name.at(yyextra->name.length()-1)==':') // end of Objective-C keyword => append to yyextra->name not yyextra->type + { + return; + } + if (!yyextra->type.isEmpty()) yyextra->type+=' '; + yyextra->type+=yyextra->name; + yyextra->name.resize(0); +} + +static int yyread(char *buf,int max_size, yyscan_t yyscanner) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + int c=0; + while( c < max_size && yyextra->inputString[yyextra->inputPosition] ) + { + *buf = yyextra->inputString[yyextra->inputPosition++] ; + c++; buf++; + } + return c; +} + +/*@ public interface------------------------------------------------------------ */ +static yyscan_t g_yyscanner; +static struct declinfoYY_state g_declinfo_extra; void parseFuncDecl(const QCString &decl,bool objC,QCString &cl,QCString &t, QCString &n,QCString &a,QCString &ftl,QCString &exc) { + if (decl.isEmpty()) + { + return; + } + declinfoYYlex_init_extra(&g_declinfo_extra, &g_yyscanner); + struct yyguts_t *yyg = (struct yyguts_t*)g_yyscanner; + +#ifdef FLEX_DEBUG + yyset_debug(1,g_yyscanner); +#endif + printlex(yy_flex_debug, TRUE, __FILE__, NULL); - inputString = decl; - //printf("Input=`%s'\n",inputString); - if (inputString==0) return; - inputPosition = 0; - classTempListFound = FALSE; - funcTempListFound = FALSE; - insideObjC = objC; - scope.resize(0); - className.resize(0); - classTempList.resize(0); - funcTempList.resize(0); - name.resize(0); - type.resize(0); - args.resize(0); - exceptionString.resize(0); - // first we try to find the type, scope, name and arguments - declinfoYYrestart( declinfoYYin ); + yyextra->inputString = decl; + //printf("Input=`%s'\n",yyextra->inputString); + yyextra->inputPosition = 0; + yyextra->classTempListFound = FALSE; + yyextra->funcTempListFound = FALSE; + yyextra->insideObjC = objC; + yyextra->scope.resize(0); + yyextra->className.resize(0); + yyextra->classTempList.resize(0); + yyextra->funcTempList.resize(0); + yyextra->name.resize(0); + yyextra->type.resize(0); + yyextra->args.resize(0); + yyextra->exceptionString.resize(0); + // first we try to find the yyextra->type, yyextra->scope, yyextra->name and arguments + declinfoYYrestart( yyin, g_yyscanner ); BEGIN( Start ); - declinfoYYlex(); + declinfoYYlex(g_yyscanner); - //printf("type=`%s' class=`%s' name=`%s' args=`%s'\n", - // type.data(),scope.data(),name.data(),args.data()); + //printf("yyextra->type=`%s' class=`%s' yyextra->name=`%s' yyextra->args=`%s'\n", + // yyextra->type.data(),yyextra->scope.data(),yyextra->name.data(),yyextra->args.data()); - int nb = name.findRev('['); - if (nb!=-1 && args.isEmpty()) // correct for [] in name ambigity (due to Java return type allowing []) + int nb = yyextra->name.findRev('['); + if (nb!=-1 && yyextra->args.isEmpty()) // correct for [] in yyextra->name ambigity (due to Java return yyextra->type allowing []) { - args.prepend(name.right(name.length()-nb)); - name=name.left(nb); + yyextra->args.prepend(yyextra->name.right(yyextra->name.length()-nb)); + yyextra->name=yyextra->name.left(nb); } #if 0 { - int l=scope.length(); + int l=yyextra->scope.length(); int i=0; int skipCount=0; cl.resize(0); ctl.resize(0); for (i=0;i<l;i++) { - char c=scope.at(i); + char c=yyextra->scope.at(i); if (c=='<') skipCount++; else if (c=='>') @@ -282,12 +302,12 @@ void parseFuncDecl(const QCString &decl,bool objC,QCString &cl,QCString &t, cl+=c; } } - cl=stripTemplateSpecifiersFromScope(removeRedundantWhiteSpace(scope),FALSE); + cl=stripTemplateSpecifiersFromScope(removeRedundantWhiteSpace(yyextra->scope),FALSE); ctl.resize(0); #endif - cl=scope; - n=removeRedundantWhiteSpace(name); + cl=yyextra->scope; + n=removeRedundantWhiteSpace(yyextra->name); int il,ir; if ((il=n.find('<'))!=-1 && (ir=n.findRev('>'))!=-1) // TODO: handle cases like where n="operator<< <T>" @@ -296,24 +316,23 @@ void parseFuncDecl(const QCString &decl,bool objC,QCString &cl,QCString &t, n=n.left(il); } - //ctl=classTempList.copy(); - //ftl=funcTempList.copy(); - t=removeRedundantWhiteSpace(type); - a=removeRedundantWhiteSpace(args); - exc=removeRedundantWhiteSpace(exceptionString); + //ctl=yyextra->classTempList.copy(); + //ftl=yyextra->funcTempList.copy(); + t=removeRedundantWhiteSpace(yyextra->type); + a=removeRedundantWhiteSpace(yyextra->args); + exc=removeRedundantWhiteSpace(yyextra->exceptionString); if (!t.isEmpty() && t.at(t.length()-1)==')') // for function pointers { a.prepend(")"); t=t.left(t.length()-1); } - //printf("type=`%s' class=`%s' name=`%s' args=`%s'\n", + //printf("yyextra->type=`%s' class=`%s' yyextra->name=`%s' yyextra->args=`%s'\n", // t.data(),cl.data(),n.data(),a.data()); printlex(yy_flex_debug, FALSE, __FILE__, NULL); + declinfoYYlex_destroy(g_yyscanner); return; - - } //extern "C" { // some bogus code to keep the compiler happy @@ -324,18 +343,18 @@ void parseFuncDecl(const QCString &decl,bool objC,QCString &cl,QCString &t, #if 0 void dumpDecl(const char *s) { - QCString className; + QCString yyextra->className; QCString classTNames; - QCString type; - QCString name; - QCString args; + QCString yyextra->type; + QCString yyextra->name; + QCString yyextra->args; QCString funcTNames; msg("-----------------------------------------\n"); - parseFuncDecl(s,className,classTNames,type,name,args,funcTNames); - msg("type=`%s' class=`%s' classTempl=`%s' name=`%s' " - "funcTemplateNames=`%s' args=`%s'\n", - type.data(),className.data(),classTNames.data(), - name.data(),funcTNames.data(),args.data() + parseFuncDecl(s,yyextra->className,classTNames,yyextra->type,yyextra->name,yyextra->args,funcTNames); + msg("yyextra->type=`%s' class=`%s' classTempl=`%s' yyextra->name=`%s' " + "funcTemplateNames=`%s' yyextra->args=`%s'\n", + yyextra->type.data(),yyextra->className.data(),classTNames.data(), + yyextra->name.data(),funcTNames.data(),yyextra->args.data() ); } @@ -346,11 +365,11 @@ int main() dumpDecl("const A<T>::Value* A<T>::getValue<S>(const A<T>&a)"); dumpDecl("func()"); dumpDecl("friend void bla<>()"); - dumpDecl("name< T > :: operator () (int bla)"); - dumpDecl("name< T > :: operator << (int bla)"); - dumpDecl("name< T > :: operator << <> (int bla)"); - dumpDecl("className::func()"); - dumpDecl("void ( * Name < T > :: bla ) ( int, char * )"); + dumpDecl("yyextra->name< T > :: operator () (int bla)"); + dumpDecl("yyextra->name< T > :: operator << (int bla)"); + dumpDecl("yyextra->name< T > :: operator << <> (int bla)"); + dumpDecl("yyextra->className::func()"); + dumpDecl("void ( * yyextra->Name < T > :: bla ) ( int, char * )"); } #endif diff --git a/src/defgen.cpp b/src/defgen.cpp index de11057..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" @@ -144,7 +145,7 @@ void generateDEFForMember(MemberDef *md, if (isFunc) //function { ArgumentList *declAl = new ArgumentList; - ArgumentList *defAl = md->argumentList(); + const ArgumentList *defAl = md->argumentList(); stringToArgumentList(md->argsString(),declAl); QCString fcnPrefix = " " + memPrefix + "param-"; @@ -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 3d7ae33..be0d60b 100644 --- a/src/docparser.cpp +++ b/src/docparser.cpp @@ -406,9 +406,9 @@ static void checkArgumentName(const QCString &name,bool isParam) { if (!Config_getBool(WARN_IF_DOC_ERROR)) return; if (g_memberDef==0) return; // not a member - ArgumentList *al=g_memberDef->isDocsForDefinition() ? - g_memberDef->argumentList() : - g_memberDef->declArgumentList(); + const ArgumentList *al=g_memberDef->isDocsForDefinition() ? + g_memberDef->argumentList() : + g_memberDef->declArgumentList(); SrcLangExt lang = g_memberDef->getLanguage(); //printf("isDocsForDefinition()=%d\n",g_memberDef->isDocsForDefinition()); if (al==0) return; // no argument list @@ -421,7 +421,7 @@ static void checkArgumentName(const QCString &name,bool isParam) if (lang==SrcLangExt_Fortran) aName=aName.lower(); //printf("aName=`%s'\n",aName.data()); ArgumentListIterator ali(*al); - Argument *a; + const Argument *a; bool found=FALSE; for (ali.toFirst();(a=ali.current());++ali) { @@ -476,14 +476,14 @@ static void checkUnOrMultipleDocumentedParams() { if (g_memberDef && g_hasParamCommand && Config_getBool(WARN_IF_DOC_ERROR)) { - ArgumentList *al=g_memberDef->isDocsForDefinition() ? + const ArgumentList *al=g_memberDef->isDocsForDefinition() ? g_memberDef->argumentList() : g_memberDef->declArgumentList(); SrcLangExt lang = g_memberDef->getLanguage(); if (al!=0) { ArgumentListIterator ali(*al); - Argument *a; + const Argument *a; bool found=FALSE; for (ali.toFirst();(a=ali.current());++ali) { @@ -689,7 +689,7 @@ static bool findDocsForMemberOrCompound(const char *commandName, QCString args=cmdArg.right(l-funcStart); // try if the link is to a member - MemberDef *md=0; + const MemberDef *md=0; const ClassDef *cd=0; const FileDef *fd=0; const NamespaceDef *nd=0; @@ -816,8 +816,8 @@ static int handleStyleArgument(DocNode *parent,QList<DocNode> &children, int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", - qPrint(cmdName)); + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", + qPrint(saveCmdName)); return tok; } while ((tok=doctokenizerYYlex()) && @@ -852,7 +852,7 @@ static int handleStyleArgument(DocNode *parent,QList<DocNode> &children, break; } } - DBG(("handleStyleArgument(%s) end tok=%x\n",qPrint(cmdName),tok)); + DBG(("handleStyleArgument(%s) end tok=%x\n",qPrint(saveCmdName),tok)); return (tok==TK_NEWPARA || tok==TK_LISTITEM || tok==TK_ENDLIST ) ? tok : RetVal_OK; } @@ -1162,7 +1162,7 @@ static DocInternalRef *handleInternalRef(DocNode *parent) QCString tokenName = g_token->name; if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", qPrint(tokenName)); return 0; } @@ -1182,7 +1182,7 @@ static DocAnchor *handleAnchor(DocNode *parent) int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", qPrint(g_token->name)); return 0; } @@ -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) { @@ -2775,8 +2681,9 @@ DocDotFile::DocDotFile(DocNode *parent,const QCString &name,const QCString &cont m_parent = parent; } -void DocDotFile::parse() +bool DocDotFile::parse() { + bool ok = false; defaultHandleTitleAndSize(CMD_DOTFILE,this,m_children,m_width,m_height); bool ambig; @@ -2788,6 +2695,7 @@ void DocDotFile::parse() if (fd) { m_file = fd->absFilePath(); + ok = true; } else if (ambig) { @@ -2801,6 +2709,7 @@ void DocDotFile::parse() warn_doc_error(g_fileName,doctokenizerYYlineno,"included dot file %s is not found " "in any of the paths specified via DOTFILE_DIRS!",qPrint(m_name)); } + return ok; } DocMscFile::DocMscFile(DocNode *parent,const QCString &name,const QCString &context) : @@ -2809,8 +2718,9 @@ DocMscFile::DocMscFile(DocNode *parent,const QCString &name,const QCString &cont m_parent = parent; } -void DocMscFile::parse() +bool DocMscFile::parse() { + bool ok = false; defaultHandleTitleAndSize(CMD_MSCFILE,this,m_children,m_width,m_height); bool ambig; @@ -2822,6 +2732,7 @@ void DocMscFile::parse() if (fd) { m_file = fd->absFilePath(); + ok = true; } else if (ambig) { @@ -2835,6 +2746,7 @@ void DocMscFile::parse() warn_doc_error(g_fileName,doctokenizerYYlineno,"included msc file %s is not found " "in any of the paths specified via MSCFILE_DIRS!",qPrint(m_name)); } + return ok; } //--------------------------------------------------------------------------- @@ -2845,8 +2757,9 @@ DocDiaFile::DocDiaFile(DocNode *parent,const QCString &name,const QCString &cont m_parent = parent; } -void DocDiaFile::parse() +bool DocDiaFile::parse() { + bool ok = false; defaultHandleTitleAndSize(CMD_DIAFILE,this,m_children,m_width,m_height); bool ambig; @@ -2858,6 +2771,7 @@ void DocDiaFile::parse() if (fd) { m_file = fd->absFilePath(); + ok = true; } else if (ambig) { @@ -2871,6 +2785,7 @@ void DocDiaFile::parse() warn_doc_error(g_fileName,doctokenizerYYlineno,"included dia file %s is not found " "in any of the paths specified via DIAFILE_DIRS!",qPrint(m_name)); } + return ok; } //--------------------------------------------------------------------------- @@ -2919,7 +2834,11 @@ DocImage::DocImage(DocNode *parent,const HtmlAttribList &attribs,const QCString bool DocImage::isSVG() const { - return m_url.isEmpty() ? m_name.right(4)==".svg" : m_url.right(4)==".svg"; + QCString locName = m_url.isEmpty() ? m_name : m_url; + int len = locName.length(); + int fnd = locName.find('?'); // ignore part from ? until end + if (fnd!=-1) fnd=len; + return fnd>=4 && locName.mid(fnd-4,4)==".svg"; } void DocImage::parse() @@ -4639,8 +4558,8 @@ int DocParamList::parse(const QCString &cmdName) int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", - qPrint(cmdName)); + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", + qPrint(saveCmdName)); retval=0; goto endparamlist; } @@ -4678,7 +4597,7 @@ int DocParamList::parse(const QCString &cmdName) if (tok==0) /* premature end of comment block */ { warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the " - "argument of command %s",qPrint(cmdName)); + "argument of command %s",qPrint(saveCmdName)); retval=0; goto endparamlist; } @@ -4876,7 +4795,7 @@ void DocPara::handleCite() int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", qPrint("cite")); return; } @@ -4908,7 +4827,7 @@ void DocPara::handleEmoji() int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", qPrint("emoji")); return; } @@ -4955,12 +4874,13 @@ int DocPara::handleXRefItem() void DocPara::handleIncludeOperator(const QCString &cmdName,DocIncOperator::Type t) { - DBG(("handleIncludeOperator(%s)\n",qPrint(cmdName))); + QCString saveCmdName = cmdName; + DBG(("handleIncludeOperator(%s)\n",qPrint(saveCmdName))); int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", - qPrint(cmdName)); + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", + qPrint(saveCmdName)); return; } doctokenizerYYsetStatePattern(); @@ -4969,13 +4889,13 @@ void DocPara::handleIncludeOperator(const QCString &cmdName,DocIncOperator::Type if (tok==0) { warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the " - "argument of command %s", qPrint(cmdName)); + "argument of command %s", qPrint(saveCmdName)); return; } else if (tok!=TK_WORD) { warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s", - tokToString(tok),qPrint(cmdName)); + tokToString(tok),qPrint(saveCmdName)); return; } DocIncOperator *op = new DocIncOperator(this,t,g_token->name,g_context,g_isExample,g_exampleName); @@ -5041,7 +4961,7 @@ void DocPara::handleImage(const QCString &cmdName) tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command with option", + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command with option", qPrint(saveCmdName)); return; } @@ -5049,7 +4969,7 @@ void DocPara::handleImage(const QCString &cmdName) } else { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", qPrint(saveCmdName)); return; } @@ -5064,7 +4984,7 @@ void DocPara::handleImage(const QCString &cmdName) tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", qPrint(saveCmdName)); return; } @@ -5099,11 +5019,12 @@ void DocPara::handleImage(const QCString &cmdName) template<class T> void DocPara::handleFile(const QCString &cmdName) { + QCString saveCmdName = cmdName; int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", - qPrint(cmdName)); + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", + qPrint(saveCmdName)); return; } doctokenizerYYsetStateFile(); @@ -5112,13 +5033,19 @@ void DocPara::handleFile(const QCString &cmdName) if (tok!=TK_WORD) { warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s", - tokToString(tok),qPrint(cmdName)); + tokToString(tok),qPrint(saveCmdName)); return; } QCString name = g_token->name; T *df = new T(this,name,g_context); - m_children.append(df); - df->parse(); + if (df->parse()) + { + m_children.append(df); + } + else + { + delete df; + } } void DocPara::handleVhdlFlow() @@ -5130,11 +5057,12 @@ void DocPara::handleVhdlFlow() void DocPara::handleLink(const QCString &cmdName,bool isJavaLink) { + QCString saveCmdName = cmdName; int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", - qPrint(cmdName)); + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", + qPrint(saveCmdName)); return; } doctokenizerYYsetStateLink(); @@ -5142,7 +5070,7 @@ void DocPara::handleLink(const QCString &cmdName,bool isJavaLink) if (tok!=TK_WORD) { warn_doc_error(g_fileName,doctokenizerYYlineno,"%s as the argument of %s", - tokToString(tok),qPrint(cmdName)); + tokToString(tok),qPrint(saveCmdName)); return; } doctokenizerYYsetStatePara(); @@ -5157,12 +5085,13 @@ void DocPara::handleLink(const QCString &cmdName,bool isJavaLink) void DocPara::handleRef(const QCString &cmdName) { - DBG(("handleRef(%s)\n",qPrint(cmdName))); + QCString saveCmdName = cmdName; + DBG(("handleRef(%s)\n",qPrint(saveCmdName))); int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", - qPrint(cmdName)); + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", + qPrint(saveCmdName)); return; } doctokenizerYYsetStateRef(); @@ -5171,7 +5100,7 @@ void DocPara::handleRef(const QCString &cmdName) if (tok!=TK_WORD) { warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s", - tokToString(tok),qPrint(cmdName)); + tokToString(tok),qPrint(saveCmdName)); goto endref; } ref = new DocRef(this,g_token->name,g_context); @@ -5184,6 +5113,7 @@ endref: void DocPara::handleInclude(const QCString &cmdName,DocInclude::Type t) { DBG(("handleInclude(%s)\n",qPrint(cmdName))); + QCString saveCmdName = cmdName; int tok=doctokenizerYYlex(); bool isBlock = false; if (tok==TK_WORD && g_token->name=="{") @@ -5224,8 +5154,8 @@ void DocPara::handleInclude(const QCString &cmdName,DocInclude::Type t) } else if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", - qPrint(cmdName)); + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", + qPrint(saveCmdName)); return; } doctokenizerYYsetStateFile(); @@ -5234,13 +5164,13 @@ void DocPara::handleInclude(const QCString &cmdName,DocInclude::Type t) if (tok==0) { warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the " - "argument of command %s",qPrint(cmdName)); + "argument of command %s",qPrint(saveCmdName)); return; } else if (tok!=TK_WORD) { warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s", - tokToString(tok),qPrint(cmdName)); + tokToString(tok),qPrint(saveCmdName)); return; } QCString fileName = g_token->name; @@ -5254,7 +5184,7 @@ void DocPara::handleInclude(const QCString &cmdName,DocInclude::Type t) if (tok!=TK_WORD) { warn_doc_error(g_fileName,doctokenizerYYlineno,"expected block identifier, but found token %s instead while parsing the %s command", - tokToString(tok),qPrint(cmdName)); + tokToString(tok),qPrint(saveCmdName)); return; } blockId = "["+g_token->name+"]"; @@ -5288,25 +5218,26 @@ void DocPara::handleInclude(const QCString &cmdName,DocInclude::Type t) void DocPara::handleSection(const QCString &cmdName) { + QCString saveCmdName = cmdName; // get the argument of the section command. int tok=doctokenizerYYlex(); if (tok!=TK_WHITESPACE) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after %s command", - qPrint(cmdName)); + warn_doc_error(g_fileName,doctokenizerYYlineno,"expected whitespace after \\%s command", + qPrint(saveCmdName)); return; } tok=doctokenizerYYlex(); if (tok==0) { warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected end of comment block while parsing the " - "argument of command %s\n", qPrint(cmdName)); + "argument of command %s\n", qPrint(saveCmdName)); return; } else if (tok!=TK_WORD && tok!=TK_LNKWORD) { warn_doc_error(g_fileName,doctokenizerYYlineno,"unexpected token %s as the argument of %s", - tokToString(tok),qPrint(cmdName)); + tokToString(tok),qPrint(saveCmdName)); return; } g_token->sectionId = g_token->name; @@ -6881,54 +6812,55 @@ int DocSection::parse() //printf("m_level=%d <-> %d\n",m_level,Doxygen::subpageNestingLevel); - if (retval==RetVal_Subsection && m_level==Doxygen::subpageNestingLevel+1) + while (true) { - // then parse any number of nested sections - while (retval==RetVal_Subsection) // more sections follow + if (retval==RetVal_Subsection && m_level<=Doxygen::subpageNestingLevel+1) { - //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; - DocSection *s=new DocSection(this, - QMIN(2+Doxygen::subpageNestingLevel,5),g_token->sectionId); - m_children.append(s); - retval = s->parse(); + // then parse any number of nested sections + while (retval==RetVal_Subsection) // more sections follow + { + //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; + DocSection *s=new DocSection(this, + QMIN(2+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + break; } - } - else if (retval==RetVal_Subsubsection && m_level==Doxygen::subpageNestingLevel+2) - { - // then parse any number of nested sections - while (retval==RetVal_Subsubsection) // more sections follow + else if (retval==RetVal_Subsubsection && m_level<=Doxygen::subpageNestingLevel+2) { - //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; - DocSection *s=new DocSection(this, - QMIN(3+Doxygen::subpageNestingLevel,5),g_token->sectionId); - m_children.append(s); - retval = s->parse(); + if ((m_level<=1+Doxygen::subpageNestingLevel) && !QString(g_token->sectionId).startsWith("autotoc_md")) + warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected subsubsection command found inside %s!",sectionLevelToName[m_level]); + // then parse any number of nested sections + while (retval==RetVal_Subsubsection) // more sections follow + { + //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; + DocSection *s=new DocSection(this, + QMIN(3+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + if (!(m_level<Doxygen::subpageNestingLevel+2 && retval == RetVal_Subsection)) break; } - } - else if (retval==RetVal_Paragraph && m_level==QMIN(5,Doxygen::subpageNestingLevel+3)) - { - // then parse any number of nested sections - while (retval==RetVal_Paragraph) // more sections follow + else if (retval==RetVal_Paragraph && m_level<=QMIN(5,Doxygen::subpageNestingLevel+3)) { - //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; - DocSection *s=new DocSection(this, - QMIN(4+Doxygen::subpageNestingLevel,5),g_token->sectionId); - m_children.append(s); - retval = s->parse(); + if ((m_level<=2+Doxygen::subpageNestingLevel) && !QString(g_token->sectionId).startsWith("autotoc_md")) + warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected paragraph command found inside %s!",sectionLevelToName[m_level]); + // then parse any number of nested sections + while (retval==RetVal_Paragraph) // more sections follow + { + //SectionInfo *sec=Doxygen::sectionDict[g_token->sectionId]; + DocSection *s=new DocSection(this, + QMIN(4+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + if (!(m_level<Doxygen::subpageNestingLevel+3 && (retval == RetVal_Subsection || retval == RetVal_Subsubsection))) break; + } + else + { + break; } - } - else if ((m_level<=1+Doxygen::subpageNestingLevel && retval==RetVal_Subsubsection) || - (m_level<=2+Doxygen::subpageNestingLevel && retval==RetVal_Paragraph) - ) - { - int level = (retval==RetVal_Subsubsection) ? 3 : 4; - warn_doc_error(g_fileName,doctokenizerYYlineno,"Unexpected %s " - "command found inside %s!", - sectionLevelToName[level],sectionLevelToName[m_level]); - retval=0; // stop parsing - } - else - { } INTERNAL_ASSERT(retval==0 || @@ -7082,21 +7014,72 @@ void DocRoot::parse() { delete par; } - if (retval==TK_LISTITEM) + if (retval==RetVal_Paragraph) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found"); + if (!QString(g_token->sectionId).startsWith("autotoc_md")) + warn_doc_error(g_fileName,doctokenizerYYlineno,"found paragraph command outside of subsubsection context!"); + while (retval==RetVal_Paragraph) + { + SectionInfo *sec=Doxygen::sectionDict->find(g_token->sectionId); + if (sec) + { + DocSection *s=new DocSection(this, + QMIN(4+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid paragraph id `%s'; ignoring paragraph",qPrint(g_token->sectionId)); + retval = 0; + } + } } - else if (retval==RetVal_Subsection) + if (retval==RetVal_Subsubsection) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"found subsection command outside of section context!"); + if (!(QString(g_token->sectionId).startsWith("autotoc_md"))) + warn_doc_error(g_fileName,doctokenizerYYlineno,"found subsubsection command outside of subsection context!"); + while (retval==RetVal_Subsubsection) + { + SectionInfo *sec=Doxygen::sectionDict->find(g_token->sectionId); + if (sec) + { + DocSection *s=new DocSection(this, + QMIN(3+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid subsubsection id `%s'; ignoring subsubsection",qPrint(g_token->sectionId)); + retval = 0; + } + } } - else if (retval==RetVal_Subsubsection) + if (retval==RetVal_Subsection) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"found subsubsection command outside of subsection context!"); + if (!(QString(g_token->sectionId).startsWith("autotoc_md"))) + warn_doc_error(g_fileName,doctokenizerYYlineno,"found subsection command outside of section context!"); + while (retval==RetVal_Subsection) + { + SectionInfo *sec=Doxygen::sectionDict->find(g_token->sectionId); + if (sec) + { + DocSection *s=new DocSection(this, + QMIN(2+Doxygen::subpageNestingLevel,5),g_token->sectionId); + m_children.append(s); + retval = s->parse(); + } + else + { + warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid subsection id `%s'; ignoring subsection",qPrint(g_token->sectionId)); + retval = 0; + } + } } - else if (retval==RetVal_Paragraph) + if (retval==TK_LISTITEM) { - warn_doc_error(g_fileName,doctokenizerYYlineno,"found paragraph command outside of subsubsection context!"); + warn_doc_error(g_fileName,doctokenizerYYlineno,"Invalid list item found"); } if (retval==RetVal_Internal) { diff --git a/src/docparser.h b/src/docparser.h index ef01089..cab1589 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> { @@ -816,7 +798,7 @@ class DocDotFile : public CompAccept<DocDotFile> { public: DocDotFile(DocNode *parent,const QCString &name,const QCString &context); - void parse(); + bool parse(); Kind kind() const { return Kind_DotFile; } QCString name() const { return m_name; } QCString file() const { return m_file; } @@ -839,7 +821,7 @@ class DocMscFile : public CompAccept<DocMscFile> { public: DocMscFile(DocNode *parent,const QCString &name,const QCString &context); - void parse(); + bool parse(); Kind kind() const { return Kind_MscFile; } QCString name() const { return m_name; } QCString file() const { return m_file; } @@ -862,7 +844,7 @@ class DocDiaFile : public CompAccept<DocDiaFile> { public: DocDiaFile(DocNode *parent,const QCString &name,const QCString &context); - void parse(); + bool parse(); Kind kind() const { return Kind_DiaFile; } QCString name() const { return m_name; } QCString file() const { return m_file; } diff --git a/src/doctokenizer.l b/src/doctokenizer.l index 7b402ca..5346c0a 100644 --- a/src/doctokenizer.l +++ b/src/doctokenizer.l @@ -658,7 +658,7 @@ REFWORD_NOCV {FILEMASK}|{LABELID}|{REFWORD2_NOCV}|{REFWORD3}|{REFWORD4_NOCV} g_token->isEMailAddr=TRUE; return TK_URL; } -<St_Para>"$"{ID}":"[^\n$]+"$" { /* RCS tag */ +<St_Para>"$"{ID}":"[^:\n$][^\n$]*"$" { /* RCS tag */ QCString tagName(yytext+1); int index=tagName.find(':'); g_token->name = tagName.left(index); @@ -902,6 +902,7 @@ REFWORD_NOCV {FILEMASK}|{LABELID}|{REFWORD2_NOCV}|{REFWORD3}|{REFWORD4_NOCV} g_token->sectionId = QCString(yytext).stripWhiteSpace(); return RetVal_OK; } +<St_PlantUMLOpt>"\n" | <St_PlantUMLOpt>. { g_token->sectionId = ""; unput(*yytext); 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 f61acec..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,390 +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 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 << "\"" << convertToXML(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; @@ -448,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) { @@ -636,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)) @@ -678,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,TRUE); - 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; @@ -1280,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(); @@ -1310,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) @@ -1367,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) { @@ -1381,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)) @@ -1449,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) @@ -1492,2756 +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; - 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 ? ¨EdgeProps : &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 -{ - 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) - { - node->clearWriteFlag(); - } - } - for (dnli2.toFirst();(node=dnli2.current());++dnli2) - { - if (node->m_subgraphId==n->m_subgraphId) - { - node->write(md5stream,DotNode::Hierarchy,GOF_BITMAP,FALSE,TRUE,TRUE); - } - } - 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)) - { - if (regenerate) - { - DotManager::instance()->addSVGConversion(absImgName,QCString(), - FALSE,QCString(),FALSE,0); - } - int mapId = DotManager::instance()->addSVGObject(fileName,baseName, - absImgName,QCString()); - out << "<!-- SVG " << mapId << " -->" << endl; - } - } - else // normal bitmap - { - out << "<img src=\"" << imgName << "\" border=\"0\" alt=\"\" usemap=\"#" - << mapLabel << "\"/>" << endl; - - if (regenerate || !insertMapFile(out,absMapName,QCString(),mapLabel)) - { - int mapId = DotManager::instance()->addMap(fileName,absMapName,QCString(), - FALSE,QCString(),mapLabel); - out << "<!-- MAP " << mapId << " -->" << endl; - } - } -} - -void DotGfxHierarchyTable::writeGraph(FTextStream &out, - const char *path,const char *fileName) const -{ - //printf("DotGfxHierarchyTable::writeGraph(%s)\n",name); - //printf("m_rootNodes=%p count=%d\n",m_rootNodes,m_rootNodes->count()); - - if (m_rootSubgraphs->count()==0) return; - - QDir d(path); - // 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(m_curNodeNumber++, - 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(m_curNodeNumber++, - 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_curNodeNumber(1) -{ - 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(m_curNodeNumber++, - 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) +class GraphLegendDotGraph : public DotGraph { - 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 + private: + virtual QCString getBaseName() const { - 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); - } - } + return "graph_legend"; } - } -} - -int DotClassGraph::m_curNodeNumber = 0; -void DotClassGraph::resetNumbering() -{ - m_curNodeNumber = 0; -} - -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()) + virtual void computeTheGraph() { - tmp_url+="#"+cd->anchor(); + 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); } - } - QCString className = cd->displayName(); - QCString tooltip = cd->briefDescriptionAsTooltip(); - m_startNode = new DotNode(m_curNodeNumber++, - 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) + virtual QCString getMapLabel() const { - 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? - ); + return ""; } - } - 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( - m_curNodeNumber++, // 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); - } - } -} - -int DotInclDepGraph::m_curNodeNumber = 0; - -void DotInclDepGraph::resetNumbering() -{ - m_curNodeNumber = 0; -} - -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(m_curNodeNumber++, - 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) - { - 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( - m_curNodeNumber++, - 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); - } - } -} - -int DotCallGraph::m_curNodeNumber = 0; - -void DotCallGraph::resetNumbering() -{ - m_curNodeNumber = 0; -} - -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(m_curNodeNumber++, - 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); - } - - 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 + GraphLegendDotGraph dg; + FTextStream ts; + dg.writeGraph(ts, GOF_BITMAP, EOF_Html, path, "", "", FALSE, 0); - 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()); } } @@ -4256,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 { @@ -4282,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 @@ -4312,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()) @@ -4331,7 +565,7 @@ void writeDotImageMapFromFile(FTextStream &t, QCString svgName=outDir+"/"+baseName+".svg"; writeSVGFigureLink(t,relPath,baseName,svgName); DotFilePatcher patcher(svgName); - patcher.addSVGConversion(relPath,TRUE,context,TRUE,graphId); + patcher.addSVGConversion("",TRUE,context,TRUE,graphId); patcher.run(); } else // bitmap graphics @@ -4351,628 +585,3 @@ void writeDotImageMapFromFile(FTextStream &t, } d.remove(absOutFile); } - -//------------------------------------------------------------- - -int DotGroupCollaboration::m_curNodeNumber = 0; - -void DotGroupCollaboration::resetNumbering() -{ - m_curNodeNumber = 0; -} - -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(m_curNodeNumber++, 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(m_curNodeNumber++, 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(m_curNodeNumber++, 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(m_curNodeNumber++, 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 << "\"" << convertToXML(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"; -} - -void resetDotNodeNumbering() -{ - DotClassGraph::resetNumbering(); - DotInclDepGraph::resetNumbering(); - DotCallGraph::resetNumbering(); - DotGroupCollaboration::resetNumbering(); -} - @@ -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,466 +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; -}; - -/** Represents a graphical class hierarchy */ -class DotGfxHierarchyTable -{ - 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; - int m_curNodeNumber; - DotNodeList *m_rootSubgraphs; -}; - -/** Representation of a class inheritance or dependency graph */ -class DotClassGraph -{ - 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); - static void resetNumbering(); - - 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; - static int m_curNodeNumber; - DotNode::GraphType m_graphType; - QCString m_collabFileName; - QCString m_inheritFileName; - bool m_lrRank; -}; - -/** Representation of an include dependency graph */ -class DotInclDepGraph -{ - 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); - static void resetNumbering(); - - 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; - static int m_curNodeNumber; - QCString m_inclDepFileName; - QCString m_inclByDepFileName; - bool m_inverse; -}; - -/** Representation of an call graph */ -class DotCallGraph -{ - 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); - static void resetNumbering(); - - private: - DotNode *m_startNode; - static int m_curNodeNumber; - QDict<DotNode> *m_usedNodes; - bool m_inverse; - QCString m_diskName; - const Definition * m_scope; -}; - -/** Representation of an directory dependency graph */ -class DotDirDeps -{ - 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 : - 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; - static void resetNumbering(); - - 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; - static int m_curNodeNumber; - 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); @@ -500,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); @@ -517,7 +74,10 @@ void writeDotImageMapFromFile(FTextStream &t, const QCString& inFile, const QCString& outDir, const QCString& relPath,const QCString& baseName, const QCString& context,int graphId=-1); - -void resetDotNodeNumbering(); +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) ? ¨EdgeProps : &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 2900f49..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); @@ -766,6 +767,16 @@ static void buildFileList(Entry *root) { bool ambig; FileDef *fd=findFileDef(Doxygen::inputNameDict,root->name,ambig); + if (!fd || ambig) + { + int save_ambig = ambig; + // use the directory of the file to see if the described file is in the same + // directory as the describing file. + QCString fn = root->fileName; + int newIndex=fn.findRev('/'); + fd=findFileDef(Doxygen::inputNameDict,fn.left(newIndex) + "/" + root->name,ambig); + if (!fd) ambig = save_ambig; + } //printf("**************** root->name=%s fd=%p\n",root->name.data(),fd); if (fd && !ambig) { @@ -795,7 +806,7 @@ static void buildFileList(Entry *root) const char *fn = root->fileName.data(); QCString text(4096); text.sprintf("the name `%s' supplied as " - "the second argument in the \\file statement ", + "the argument in the \\file statement ", qPrint(root->name)); if (ambig) // name is ambiguous { @@ -2150,8 +2161,8 @@ static void findUsingDeclImports(Entry *root) { fileName = root->tagInfo->tagName; } - ArgumentList *templAl = md->templateArguments(); - ArgumentList *al = md->templateArguments(); + const ArgumentList *templAl = md->templateArguments(); + const ArgumentList *al = md->templateArguments(); newMd = createMemberDef( fileName,root->startLine,root->startColumn, md->typeString(),memName,md->argsString(), @@ -3586,146 +3597,149 @@ static void buildFunctionList(Entry *root) MemberNameIterator mni(*mn); for (mni.toFirst();(!found && (md=mni.current()));++mni) { - const NamespaceDef *mnd = md->getNamespaceDef(); - NamespaceDef *rnd = 0; - //printf("root namespace=%s\n",rootNav->parent()->name().data()); - QCString fullScope = scope; - QCString parentScope = root->parent()->name; - if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope)) + if (!md->isAlias()) { - if (!scope.isEmpty()) fullScope.prepend("::"); - fullScope.prepend(parentScope); - } - //printf("fullScope=%s\n",fullScope.data()); - rnd = getResolvedNamespace(fullScope); - const FileDef *mfd = md->getFileDef(); - QCString nsName,rnsName; - if (mnd) nsName = mnd->name().copy(); - if (rnd) rnsName = rnd->name().copy(); - //printf("matching arguments for %s%s %s%s\n", - // md->name().data(),md->argsString(),rname.data(),argListToString(root->argList).data()); - ArgumentList *mdAl = md->argumentList(); - ArgumentList *mdTempl = md->templateArguments(); - - // in case of template functions, we need to check if the - // functions have the same number of template parameters - bool sameNumTemplateArgs = TRUE; - bool matchingReturnTypes = TRUE; - if (mdTempl!=0 && root->tArgLists) - { - if (mdTempl->count()!=root->tArgLists->getLast()->count()) + const NamespaceDef *mnd = md->getNamespaceDef(); + NamespaceDef *rnd = 0; + //printf("root namespace=%s\n",rootNav->parent()->name().data()); + QCString fullScope = scope; + QCString parentScope = root->parent()->name; + if (!parentScope.isEmpty() && !leftScopeMatch(parentScope,scope)) { - sameNumTemplateArgs = FALSE; + if (!scope.isEmpty()) fullScope.prepend("::"); + fullScope.prepend(parentScope); } - if (md->typeString()!=removeRedundantWhiteSpace(root->type)) + //printf("fullScope=%s\n",fullScope.data()); + rnd = getResolvedNamespace(fullScope); + const FileDef *mfd = md->getFileDef(); + QCString nsName,rnsName; + if (mnd) nsName = mnd->name().copy(); + if (rnd) rnsName = rnd->name().copy(); + //printf("matching arguments for %s%s %s%s\n", + // md->name().data(),md->argsString(),rname.data(),argListToString(root->argList).data()); + ArgumentList *mdAl = md->argumentList(); + const ArgumentList *mdTempl = md->templateArguments(); + + // in case of template functions, we need to check if the + // functions have the same number of template parameters + bool sameNumTemplateArgs = TRUE; + bool matchingReturnTypes = TRUE; + if (mdTempl!=0 && root->tArgLists) { - matchingReturnTypes = FALSE; + if (mdTempl->count()!=root->tArgLists->getLast()->count()) + { + sameNumTemplateArgs = FALSE; + } + if (md->typeString()!=removeRedundantWhiteSpace(root->type)) + { + matchingReturnTypes = FALSE; + } } - } - bool staticsInDifferentFiles = - root->stat && md->isStatic() && root->fileName!=md->getDefFileName(); + bool staticsInDifferentFiles = + root->stat && md->isStatic() && root->fileName!=md->getDefFileName(); - if ( - matchArguments2(md->getOuterScope(),mfd,mdAl, - rnd ? rnd : Doxygen::globalScope,rfd,root->argList, - FALSE) && - sameNumTemplateArgs && - matchingReturnTypes && - !staticsInDifferentFiles - ) - { - GroupDef *gd=0; - if (root->groups->getFirst()!=0) - { - gd = Doxygen::groupSDict->find(root->groups->getFirst()->groupname.data()); - } - //printf("match!\n"); - //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,nsName.data(),rnsName.data()); - // see if we need to create a new member - found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace - ((mnd==0 && rnd==0 && mfd!=0 && // no external reference and - mfd->absFilePath()==root->fileName // prototype in the same file - ) - ); - // otherwise, allow a duplicate global member with the same argument list - if (!found && gd && gd==md->getGroupDef() && nsName==rnsName) + if ( + matchArguments2(md->getOuterScope(),mfd,mdAl, + rnd ? rnd : Doxygen::globalScope,rfd,root->argList, + FALSE) && + sameNumTemplateArgs && + matchingReturnTypes && + !staticsInDifferentFiles + ) { - // member is already in the group, so we don't want to add it again. - found=TRUE; - } + GroupDef *gd=0; + if (root->groups->getFirst()!=0) + { + gd = Doxygen::groupSDict->find(root->groups->getFirst()->groupname.data()); + } + //printf("match!\n"); + //printf("mnd=%p rnd=%p nsName=%s rnsName=%s\n",mnd,rnd,nsName.data(),rnsName.data()); + // see if we need to create a new member + found=(mnd && rnd && nsName==rnsName) || // members are in the same namespace + ((mnd==0 && rnd==0 && mfd!=0 && // no external reference and + mfd->absFilePath()==root->fileName // prototype in the same file + ) + ); + // otherwise, allow a duplicate global member with the same argument list + if (!found && gd && gd==md->getGroupDef() && nsName==rnsName) + { + // member is already in the group, so we don't want to add it again. + found=TRUE; + } - //printf("combining function with prototype found=%d in namespace %s\n", - // found,nsName.data()); + //printf("combining function with prototype found=%d in namespace %s\n", + // found,nsName.data()); - if (found) - { - // merge argument lists - mergeArguments(mdAl,root->argList,!root->doc.isEmpty()); - // merge documentation - if (md->documentation().isEmpty() && !root->doc.isEmpty()) + if (found) { - ArgumentList *argList = new ArgumentList; - stringToArgumentList(root->args,argList); - if (root->proto) + // merge argument lists + mergeArguments(mdAl,root->argList,!root->doc.isEmpty()); + // merge documentation + if (md->documentation().isEmpty() && !root->doc.isEmpty()) { - //printf("setDeclArgumentList to %p\n",argList); - md->setDeclArgumentList(argList); + ArgumentList *argList = new ArgumentList; + stringToArgumentList(root->args,argList); + if (root->proto) + { + //printf("setDeclArgumentList to %p\n",argList); + md->setDeclArgumentList(argList); + } + else + { + md->setArgumentList(argList); + } } - else + + md->setDocumentation(root->doc,root->docFile,root->docLine); + md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); + md->setDocsForDefinition(!root->proto); + if (md->getStartBodyLine()==-1 && root->bodyLine!=-1) { - md->setArgumentList(argList); + md->setBodySegment(root->bodyLine,root->endBodyLine); + md->setBodyDef(rfd); } - } - - md->setDocumentation(root->doc,root->docFile,root->docLine); - md->setInbodyDocumentation(root->inbodyDocs,root->inbodyFile,root->inbodyLine); - md->setDocsForDefinition(!root->proto); - if (md->getStartBodyLine()==-1 && root->bodyLine!=-1) - { - md->setBodySegment(root->bodyLine,root->endBodyLine); - md->setBodyDef(rfd); - } - if (md->briefDescription().isEmpty() && !root->brief.isEmpty()) - { - md->setArgsString(root->args); - } - md->setBriefDescription(root->brief,root->briefFile,root->briefLine); + if (md->briefDescription().isEmpty() && !root->brief.isEmpty()) + { + md->setArgsString(root->args); + } + md->setBriefDescription(root->brief,root->briefFile,root->briefLine); - md->addSectionsToDefinition(root->anchors); + md->addSectionsToDefinition(root->anchors); - md->enableCallGraph(md->hasCallGraph() || root->callGraph); - md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph); - md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation); - md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation); + md->enableCallGraph(md->hasCallGraph() || root->callGraph); + md->enableCallerGraph(md->hasCallerGraph() || root->callerGraph); + md->enableReferencedByRelation(md->hasReferencedByRelation() || root->referencedByRelation); + md->enableReferencesRelation(md->hasReferencesRelation() || root->referencesRelation); - // merge ingroup specifiers - if (md->getGroupDef()==0 && root->groups->getFirst()!=0) - { - addMemberToGroups(root,md); - } - else if (md->getGroupDef()!=0 && root->groups->count()==0) - { - //printf("existing member is grouped, new member not\n"); - root->groups->append(new Grouping(md->getGroupDef()->name(), md->getGroupPri())); - } - else if (md->getGroupDef()!=0 && root->groups->getFirst()!=0) - { - //printf("both members are grouped\n"); - } + // merge ingroup specifiers + if (md->getGroupDef()==0 && root->groups->getFirst()!=0) + { + addMemberToGroups(root,md); + } + else if (md->getGroupDef()!=0 && root->groups->count()==0) + { + //printf("existing member is grouped, new member not\n"); + root->groups->append(new Grouping(md->getGroupDef()->name(), md->getGroupPri())); + } + else if (md->getGroupDef()!=0 && root->groups->getFirst()!=0) + { + //printf("both members are grouped\n"); + } - // if md is a declaration and root is the corresponding - // definition, then turn md into a definition. - if (md->isPrototype() && !root->proto) - { - md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn()); - md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn); - } - // if md is already the definition, then add the declaration info - else if (!md->isPrototype() && root->proto) - { - md->setDeclFile(root->fileName,root->startLine,root->startColumn); + // if md is a declaration and root is the corresponding + // definition, then turn md into a definition. + if (md->isPrototype() && !root->proto) + { + md->setDeclFile(md->getDefFileName(),md->getDefLine(),md->getDefColumn()); + md->setPrototype(FALSE,root->fileName,root->startLine,root->startColumn); + } + // if md is already the definition, then add the declaration info + else if (!md->isPrototype() && root->proto) + { + md->setDeclFile(root->fileName,root->startLine,root->startColumn); + } } } } @@ -3915,24 +3929,27 @@ static void findFriends() MemberDef *fmd; for (;(fmd=fni.current());++fni) // for each function with that name { + const MemberDef *cfmd = const_cast<const MemberDef*>(fmd); MemberNameIterator mni(*mn); MemberDef *mmd; for (;(mmd=mni.current());++mni) // for each member with that name { + const MemberDef *cmmd = const_cast<const MemberDef*>(mmd); //printf("Checking for matching arguments // mmd->isRelated()=%d mmd->isFriend()=%d mmd->isFunction()=%d\n", // mmd->isRelated(),mmd->isFriend(),mmd->isFunction()); - ArgumentList *mmdAl = mmd->argumentList(); - ArgumentList *fmdAl = fmd->argumentList(); - if ((mmd->isFriend() || (mmd->isRelated() && mmd->isFunction())) && - matchArguments2(mmd->getOuterScope(), mmd->getFileDef(), mmdAl, - fmd->getOuterScope(), fmd->getFileDef(), fmdAl, + if ((cmmd->isFriend() || (cmmd->isRelated() && cmmd->isFunction())) && + !fmd->isAlias() && !mmd->isAlias() && + matchArguments2(cmmd->getOuterScope(), cmmd->getFileDef(), cmmd->argumentList(), + cfmd->getOuterScope(), cfmd->getFileDef(), cfmd->argumentList(), TRUE ) ) // if the member is related and the arguments match then the // function is actually a friend. { + ArgumentList *mmdAl = mmd->argumentList(); + ArgumentList *fmdAl = fmd->argumentList(); mergeArguments(mmdAl,fmdAl); if (!fmd->documentation().isEmpty()) { @@ -4013,7 +4030,10 @@ static void transferFunctionDocumentation() MemberNameIterator mni2(*mn); for (;(mdef=mni2.current());++mni2) { - combineDeclarationAndDefinition(mdec,mdef); + if (!mdec->isAlias() && !mdef->isAlias()) + { + combineDeclarationAndDefinition(mdec,mdef); + } } } } @@ -4136,12 +4156,11 @@ static void transferRelatedFunctionDocumentation() MemberNameIterator rmni(*rmn); for (rmni.toFirst();(rmd=rmni.current());++rmni) // for each member with the same name { - ArgumentList *mdAl = md->argumentList(); - ArgumentList *rmdAl = rmd->argumentList(); //printf(" Member found: related=`%d'\n",rmd->isRelated()); if ((rmd->isRelated() || rmd->isForeign()) && // related function - matchArguments2( md->getOuterScope(), md->getFileDef(), mdAl, - rmd->getOuterScope(),rmd->getFileDef(),rmdAl, + !md->isAlias() && !rmd->isAlias() && + matchArguments2( md->getOuterScope(), md->getFileDef(), md->argumentList(), + rmd->getOuterScope(),rmd->getFileDef(),rmd->argumentList(), TRUE ) ) @@ -5533,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 */ @@ -5546,7 +5565,7 @@ static bool findGlobalMember(Entry *root, // different functions. if (matching && root->tArgLists) { - ArgumentList *mdTempl = md->templateArguments(); + const ArgumentList *mdTempl = md->templateArguments(); if (mdTempl) { if (root->tArgLists->getLast()->count()!=mdTempl->count()) @@ -5606,7 +5625,7 @@ static bool findGlobalMember(Entry *root, warnMsg+=" '"; warnMsg+=substitute(md->declaration(),"%","%%"); warnMsg+="' at line "+QCString().setNum(md->getDefLine())+ - " of file"+md->getDefFileName()+"\n"; + " of file "+md->getDefFileName()+"\n"; } } warn(root->fileName,root->startLine,warnMsg); @@ -6165,7 +6184,7 @@ static void findMember(Entry *root, // get the template parameter lists found at the member declaration QList<ArgumentList> declTemplArgs; cd->getTemplateParameterLists(declTemplArgs); - ArgumentList *templAl = md->templateArguments(); + const ArgumentList *templAl = md->templateArguments(); if (templAl) { declTemplArgs.append(templAl); @@ -6283,6 +6302,8 @@ static void findMember(Entry *root, // TODO: copy other aspects? root->protection=md->protection(); // copy protection level + root->stat=md->isStatic(); + root->virt=md->virtualness(); addMethodToClass(root,cd,md->name(),isFriend); return; } @@ -6322,10 +6343,14 @@ static void findMember(Entry *root, //printf("ccd->name()==%s className=%s\n",ccd->name().data(),className.data()); if (ccd!=0 && rightScopeMatch(ccd->name(),className)) { - ArgumentList *templAl = md->templateArguments(); + const ArgumentList *templAl = md->templateArguments(); if (root->tArgLists && templAl!=0 && root->tArgLists->getLast()->count()<=templAl->count()) { + Debug::print(Debug::FindMembers,0,"7. add template specialization\n"); + root->protection=md->protection(); + root->stat=md->isStatic(); + root->virt=md->virtualness(); addMethodToClass(root,ccd,md->name(),isFriend); return; } @@ -6399,7 +6424,7 @@ static void findMember(Entry *root, const ClassDef *cd=md->getClassDef(); if (cd!=0 && rightScopeMatch(cd->name(),className)) { - ArgumentList *templAl = md->templateArguments(); + const ArgumentList *templAl = md->templateArguments(); if (templAl!=0) { warnMsg+=" 'template "; @@ -6697,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, @@ -7950,7 +7975,7 @@ static void generateFileSources() { QStrList filesInSameTu; fd->startParsing(); - if (fd->generateSourceFile() && !g_useOutputTemplate) // sources need to be shown in the output + if (fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate) // sources need to be shown in the output { msg("Generating code for file %s...\n",fd->docName().data()); fd->writeSource(*g_outputList,FALSE,filesInSameTu); @@ -7980,7 +8005,7 @@ static void generateFileSources() { QStrList filesInSameTu; fd->startParsing(); - if (fd->generateSourceFile() && !g_useOutputTemplate) // sources need to be shown in the output + if (fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate) // sources need to be shown in the output { msg("Generating code for file %s...\n",fd->docName().data()); fd->writeSource(*g_outputList,FALSE,filesInSameTu); @@ -10122,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"); @@ -11246,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) { @@ -11628,6 +11651,7 @@ void generateOutput() } initSearchIndexer(); + initDot(); bool generateHtml = Config_getBool(GENERATE_HTML); bool generateLatex = Config_getBool(GENERATE_LATEX); @@ -11682,7 +11706,7 @@ void generateOutput() QCString htmldir = Config_getString(HTML_OUTPUT); if (!Htags::execute(htmldir)) err("USE_HTAGS is YES but htags(1) failed. \n"); - if (!Htags::loadFilemap(htmldir)) + else if (!Htags::loadFilemap(htmldir)) err("htags(1) ended normally but failed to load the filemap. \n"); } @@ -11729,12 +11753,9 @@ void generateOutput() generateExampleDocs(); g_s.end(); - if (!Htags::useHtags) - { - g_s.begin("Generating file sources...\n"); - generateFileSources(); - g_s.end(); - } + g_s.begin("Generating file sources...\n"); + generateFileSources(); + g_s.end(); g_s.begin("Generating file documentation...\n"); generateFileDocs(); 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 47da514..1076e68 100644 --- a/src/fortranscanner.l +++ b/src/fortranscanner.l @@ -698,6 +698,14 @@ private { typeMode = false; yy_pop_state(); } +^{BS}"end"{BS}/(\n|!|;) { /* incorrect end type definition */ + warn(yyFileName,yyLineNr, "Found 'END' instead of 'END TYPE'"); + last_entry->parent()->endBodyLine = yyLineNr; + if (!endScope(current_root)) + yyterminate(); + typeMode = false; + yy_pop_state(); + } } /*------- module/global/typedef variable ---------------------------------------------------*/ @@ -2296,7 +2304,7 @@ static void initEntry() current->virt = virt; current->stat = gstat; current->lang = SrcLangExt_Fortran; - initGroupInfo(current); + Doxygen::docGroup.initGroupInfo(current); } /** @@ -2704,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; @@ -2721,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 @@ -2819,7 +2827,7 @@ static void scanner_abort() // dummy call to avoid compiler warning (void)yy_top_state(); - + return; //exit(-1); } diff --git a/src/groupdef.cpp b/src/groupdef.cpp index e151f5a..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" @@ -384,10 +385,10 @@ bool GroupDefImpl::insertMember(MemberDef *md,bool docOnly) if ((mni=(*allMemberNameInfoSDict)[md->name()])) { // member with this name already found MemberNameInfoIterator srcMnii(*mni); - MemberInfo *srcMi; + const MemberInfo *srcMi; for ( ; (srcMi=srcMnii.current()) ; ++srcMnii ) { - MemberDef *srcMd = srcMi->memberDef; + const MemberDef *srcMd = srcMi->memberDef; if (srcMd==md) return FALSE; // already added before! bool sameScope = srcMd->getOuterScope()==md->getOuterScope() || // same class or namespace @@ -395,10 +396,10 @@ bool GroupDefImpl::insertMember(MemberDef *md,bool docOnly) (srcMd->getOuterScope()->definitionType()==Definition::TypeFile && md->getOuterScope()->definitionType()==Definition::TypeFile); - ArgumentList *srcMdAl = srcMd->argumentList(); - ArgumentList *mdAl = md->argumentList(); - ArgumentList *tSrcMdAl = srcMd->templateArguments(); - ArgumentList *tMdAl = md->templateArguments(); + const ArgumentList *srcMdAl = srcMd->argumentList(); + const ArgumentList *mdAl = md->argumentList(); + const ArgumentList *tSrcMdAl = srcMd->templateArguments(); + const ArgumentList *tMdAl = md->templateArguments(); if (srcMd->isFunction() && md->isFunction() && // both are a function ((tSrcMdAl==0 && tMdAl==0) || diff --git a/src/htags.cpp b/src/htags.cpp index 77b1f8d..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; @@ -128,7 +132,7 @@ bool Htags::loadFilemap(const QCString &htmlDir) int len; while ((len=f.readLine(line.rawData(),maxlen))>0) { - line.resize(len+1); + line.at(len)='\0'; //printf("Read line: %s",line.data()); int sep = line.find('\t'); if (sep!=-1) 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 744976d..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" @@ -285,7 +286,6 @@ void startFile(OutputList &ol,const char *name,const char *manName, } ol.writeSplitBar(altSidebarName ? altSidebarName : name); ol.writeSearchInfo(); - resetDotNodeNumbering(); } void endFile(OutputList &ol,bool skipNavIndex,bool skipEndContents, @@ -4731,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 6b5a894..8670642 100644 --- a/src/markdown.cpp +++ b/src/markdown.cpp @@ -107,13 +107,7 @@ static action_t g_actions[256]; static Entry *g_current; static QCString g_fileName; static int g_lineNr; - -// In case a markdown page starts with a level1 header, that header is used -// as a title of the page, in effect making it a level0 header, so the -// level of all other sections needs to be corrected as well. -// This flag is TRUE if corrections are needed. -//static bool g_correctSectionLevel; - +static int g_indentLevel=0; // 0 is outside markdown, -1=page level //---------- @@ -915,7 +909,7 @@ static int processLink(GrowBuf &out,const char *data,int,int size) { SrcLangExt lang = getLanguageFromFileName(link); int lp=-1; - if ((lp=link.find("@ref "))!=-1 || (lp=link.find("\\ref "))!=-1 || lang==SrcLangExt_Markdown) + if ((lp=link.find("@ref "))!=-1 || (lp=link.find("\\ref "))!=-1 || (lang==SrcLangExt_Markdown && !isURL(link))) // assume doxygen symbol link { if (lp==-1) // link to markdown page @@ -1103,7 +1097,7 @@ static void processInline(GrowBuf &out,const char *data,int size) } /** returns whether the line is a setext-style hdr underline */ -static int isHeaderline(const char *data, int size) +static int isHeaderline(const char *data, int size, bool allowAdjustLevel) { int i=0, c=0; while (i<size && data[i]==' ') i++; @@ -1113,14 +1107,24 @@ static int isHeaderline(const char *data, int size) { while (i<size && data[i]=='=') i++,c++; while (i<size && data[i]==' ') i++; - return (c>1 && (i>=size || data[i]=='\n')) ? 1 : 0; + int level = (c>1 && (i>=size || data[i]=='\n')) ? 1 : 0; + if (allowAdjustLevel && level==1 && g_indentLevel==-1) + { + // In case a page starts with a header line we use it as title, promoting it to @page. + // We set g_indentLevel to -1 to promoting the other sections if they have a deeper + // nesting level than the page header, i.e. @section..@subsection becomes @page..@section. + // In case a section at the same level is found (@section..@section) however we need + // to undo this (and the result will be @page..@section). + g_indentLevel=0; + } + return g_indentLevel+level; } // test of level 2 header if (data[i]=='-') { while (i<size && data[i]=='-') i++,c++; while (i<size && data[i]==' ') i++; - return (c>1 && (i>=size || data[i]=='\n')) ? 2 : 0; + return (c>1 && (i>=size || data[i]=='\n')) ? g_indentLevel+2 : 0; } return 0; } @@ -1295,7 +1299,7 @@ static QCString extractTitleId(QCString &title, int level) static int isAtxHeader(const char *data,int size, - QCString &header,QCString &id) + QCString &header,QCString &id,bool allowAdjustLevel) { int i = 0, end; int level = 0, blanks=0; @@ -1328,7 +1332,29 @@ static int isAtxHeader(const char *data,int size, header=header.left(i+1); } - return level; + if (allowAdjustLevel && level==1 && g_indentLevel==-1) + { + // in case we find a `# Section` on a markdown page that started with the same level + // header, we no longer need to artificially decrease the paragraph level. + // So both + // ------------------- + // # heading 1 <-- here we set g_indentLevel to -1 + // # heading 2 <-- here we set g_indentLevel back to 0 such that this will be a @section + // ------------------- + // and + // ------------------- + // # heading 1 <-- here we set g_indentLevel to -1 + // ## heading 2 <-- here we keep g_indentLevel at -1 such that @subsection will be @section + // ------------------- + // will convert to + // ------------------- + // @page md_page Heading 1 + // @section autotoc_md1 Heading 2 + // ------------------- + + g_indentLevel=0; + } + return level+g_indentLevel; } static int isEmptyLine(const char *data,int size) @@ -1913,10 +1939,8 @@ void writeOneLineHeaderOrRuler(GrowBuf &out,const char *data,int size) { out.addStr("\n<hr>\n"); } - else if ((level=isAtxHeader(data,size,header,id))) + else if ((level=isAtxHeader(data,size,header,id,TRUE))) { - //if (level==1) g_correctSectionLevel=FALSE; - //if (g_correctSectionLevel) level--; QCString hTag; if (level<5 && !id.isEmpty()) { @@ -2258,10 +2282,8 @@ static QCString processBlocks(const QCString &s,int indent) QCString lang; blockIndent = indent; //printf("isHeaderLine(%s)=%d\n",QCString(data+i).left(size-i).data(),level); - if ((level=isHeaderline(data+i,size-i))>0) + if ((level=isHeaderline(data+i,size-i,TRUE))>0) { - //if (level==1) g_correctSectionLevel=FALSE; - //if (g_correctSectionLevel) level--; //printf("Found header at %d-%d\n",i,end); while (pi<size && data[pi]==' ') pi++; QCString header,id; @@ -2402,7 +2424,7 @@ static QCString extractPageTitle(QCString &docs,QCString &id) // second line form end1..end2 int end2=end1+1; while (end2<size && data[end2-1]!='\n') end2++; - if (isHeaderline(data+end1,size-end1)) + if (isHeaderline(data+end1,size-end1,FALSE)) { convertStringFragment(title,data+i,end1-i-1); QCString lns; @@ -2413,10 +2435,14 @@ static QCString extractPageTitle(QCString &docs,QCString &id) return title; } } - if (i<end1 && isAtxHeader(data+i,end1-i,title,id)>0) + if (i<end1 && isAtxHeader(data+i,end1-i,title,id,FALSE)>0) { docs=docs.mid(end1); } + else + { + id = extractTitleId(title, 0); + } //printf("extractPageTitle(title='%s' docs='%s' id='%s')\n",title.data(),docs.data(),id.data()); return title; } @@ -2453,19 +2479,32 @@ static QCString detab(const QCString &s,int &refIndent) col++; break; default: // non-whitespace => update minIndent - out.addChar(c); if (c<0 && i<size) // multibyte sequence { - out.addChar(data[i++]); // >= 2 bytes - if (((uchar)c&0xE0)==0xE0 && i<size) + // special handling of the UTF-8 nbsp character 0xc2 0xa0 + if (c == '\xc2' && data[i] == '\xa0') { - out.addChar(data[i++]); // 3 bytes + out.addStr(" "); + i++; } - if (((uchar)c&0xF0)==0xF0 && i<size) + else { - out.addChar(data[i++]); // 4 byres + out.addChar(c); + out.addChar(data[i++]); // >= 2 bytes + if (((uchar)c&0xE0)==0xE0 && i<size) + { + out.addChar(data[i++]); // 3 bytes + } + if (((uchar)c&0xF0)==0xF0 && i<size) + { + out.addChar(data[i++]); // 4 byres + } } } + else + { + out.addChar(c); + } if (col<minIndent) minIndent=col; col++; } @@ -2536,6 +2575,7 @@ QCString markdownFileNameToId(const QCString &fileName) return "md_"+baseName; } + void MarkdownFileParser::parseInput(const char *fileName, const char *fileBuf, Entry *root, @@ -2550,6 +2590,7 @@ void MarkdownFileParser::parseInput(const char *fileName, QCString docs = fileBuf; QCString id; QCString title=extractPageTitle(docs,id).stripWhiteSpace(); + g_indentLevel=title.isEmpty() ? 0 : -1; QCString titleFn = QFileInfo(fileName).baseName().utf8(); QCString fn = QFileInfo(fileName).fileName().utf8(); static QCString mdfileAsMainPage = Config_getString(USE_MDFILE_AS_MAINPAGE); @@ -2616,7 +2657,7 @@ void MarkdownFileParser::parseInput(const char *fileName, // restore setting Doxygen::markdownSupport = markdownEnabled; - //g_correctSectionLevel = FALSE; + g_indentLevel=0; } void MarkdownFileParser::parseCode(CodeOutputInterface &codeOutIntf, diff --git a/src/memberdef.cpp b/src/memberdef.cpp index 24257da..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" @@ -200,10 +201,11 @@ class MemberDefImpl : public DefinitionImpl, public MemberDef virtual bool hasExamples() const; virtual ExampleSDict *getExamples() const; virtual bool isPrototype() const; - virtual ArgumentList *argumentList() const; - virtual ArgumentList *declArgumentList() const; - virtual ArgumentList *templateArguments() const; - virtual QList<ArgumentList> *definitionTemplateParameterLists() const; + virtual const ArgumentList *argumentList() const; + virtual ArgumentList *argumentList(); + virtual const ArgumentList *declArgumentList() const; + virtual const ArgumentList *templateArguments() const; + virtual const QList<ArgumentList> *definitionTemplateParameterLists() const; virtual int getMemberGroupId() const; virtual MemberGroup *getMemberGroup() const; virtual bool fromAnonymousScope() const; @@ -224,7 +226,7 @@ class MemberDefImpl : public DefinitionImpl, public MemberDef virtual MemberDef *memberDefinition() const; virtual MemberDef *memberDeclaration() const; virtual MemberDef *inheritsDocsFrom() const; - virtual MemberDef *getGroupAlias() const; + virtual const MemberDef *getGroupAlias() const; virtual ClassDef *category() const; virtual MemberDef *categoryRelation() const; virtual QCString displayName(bool=TRUE) const; @@ -292,7 +294,7 @@ class MemberDefImpl : public DefinitionImpl, public MemberDef virtual void setTemplateMaster(MemberDef *mt); virtual void addListReference(Definition *d); virtual void setDocsForDefinition(bool b); - virtual void setGroupAlias(MemberDef *md); + virtual void setGroupAlias(const MemberDef *md); virtual void cacheTypedefVal(ClassDef *val,const QCString &templSpec,const QCString &resolvedType); virtual void invalidateTypedefValCache(); virtual void invalidateCachedArgumentTypes(); @@ -655,13 +657,13 @@ class MemberDefAliasImpl : public DefinitionAliasImpl, public MemberDef { return getMdAlias()->getExamples(); } virtual bool isPrototype() const { return getMdAlias()->isPrototype(); } - virtual ArgumentList *argumentList() const + virtual const ArgumentList *argumentList() const { return getMdAlias()->argumentList(); } - virtual ArgumentList *declArgumentList() const + virtual const ArgumentList *declArgumentList() const { return getMdAlias()->declArgumentList(); } - virtual ArgumentList *templateArguments() const + virtual const ArgumentList *templateArguments() const { return getMdAlias()->templateArguments(); } - virtual QList<ArgumentList> *definitionTemplateParameterLists() const + virtual const QList<ArgumentList> *definitionTemplateParameterLists() const { return getMdAlias()->definitionTemplateParameterLists(); } virtual int getMemberGroupId() const { return getMdAlias()->getMemberGroupId(); } @@ -703,7 +705,7 @@ class MemberDefAliasImpl : public DefinitionAliasImpl, public MemberDef { return getMdAlias()->memberDeclaration(); } virtual MemberDef *inheritsDocsFrom() const { return getMdAlias()->inheritsDocsFrom(); } - virtual MemberDef *getGroupAlias() const + virtual const MemberDef *getGroupAlias() const { return getMdAlias()->getGroupAlias(); } virtual ClassDef *category() const { return getMdAlias()->category(); } @@ -741,6 +743,8 @@ class MemberDefAliasImpl : public DefinitionAliasImpl, public MemberDef { err("non-const getNamespaceDef() called on aliased member. Please report as a bug.\n"); return 0; } virtual GroupDef *getGroupDef() { err("non-const getGroupDef() called on aliased member. Please report as a bug.\n"); return 0; } + virtual ArgumentList *argumentList() + { err("non-const argumentList() called on aliased member. Please report as bug.\n"); return 0; } virtual void setEnumBaseType(const QCString &type) {} virtual void setMemberType(MemberType t) {} @@ -797,7 +801,7 @@ class MemberDefAliasImpl : public DefinitionAliasImpl, public MemberDef virtual void setTemplateMaster(MemberDef *mt) {} virtual void addListReference(Definition *d) {} virtual void setDocsForDefinition(bool b) {} - virtual void setGroupAlias(MemberDef *md) {} + virtual void setGroupAlias(const MemberDef *md) {} virtual void cacheTypedefVal(ClassDef *val,const QCString &templSpec,const QCString &resolvedType) {} virtual void invalidateTypedefValCache() {} virtual void invalidateCachedArgumentTypes() {} @@ -923,7 +927,7 @@ static QCString addTemplateNames(const QCString &s,const QCString &n,const QCStr static bool writeDefArgumentList(OutputList &ol,const Definition *scope,const MemberDef *md) { - ArgumentList *defArgList=(md->isDocsForDefinition()) ? + const ArgumentList *defArgList=(md->isDocsForDefinition()) ? md->argumentList() : md->declArgumentList(); //printf("writeDefArgumentList `%s' isDocsForDefinition()=%d\n",md->name().data(),md->isDocsForDefinition()); if (defArgList==0 || md->isProperty()) @@ -1350,7 +1354,7 @@ class MemberDefImpl::IMPL // cached here. SDict<MemberList> *classSectionSDict; // not accessible - MemberDef *groupAlias; // Member containing the definition + const MemberDef *groupAlias; // Member containing the definition int grpId; // group id MemberGroup *memberGroup; // group's member definition GroupDef *group; // group in which this member is in @@ -2942,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); @@ -4133,8 +4137,8 @@ void MemberDefImpl::detectUndocumentedParams(bool hasParamCommand,bool hasReturn } else if (!m_impl->hasDocumentedParams) { - ArgumentList *al = argumentList(); - ArgumentList *declAl = declArgumentList(); + const ArgumentList *al = argumentList(); + const ArgumentList *declAl = declArgumentList(); bool allDoc=TRUE; // no parameter => all parameters are documented if ( // member has parameters al!=0 && // but the member has a parameter list @@ -5542,22 +5546,27 @@ bool MemberDefImpl::isPrototype() const return m_impl->proto; } -ArgumentList *MemberDefImpl::argumentList() const +const ArgumentList *MemberDefImpl::argumentList() const { return m_impl->defArgList; } -ArgumentList *MemberDefImpl::declArgumentList() const +ArgumentList *MemberDefImpl::argumentList() +{ + return m_impl->defArgList; +} + +const ArgumentList *MemberDefImpl::declArgumentList() const { return m_impl->declArgList; } -ArgumentList *MemberDefImpl::templateArguments() const +const ArgumentList *MemberDefImpl::templateArguments() const { return m_impl->tArgList; } -QList<ArgumentList> *MemberDefImpl::definitionTemplateParameterLists() const +const QList<ArgumentList> *MemberDefImpl::definitionTemplateParameterLists() const { return m_impl->defTmpArgLists; } @@ -5648,7 +5657,7 @@ MemberDef *MemberDefImpl::inheritsDocsFrom() const return m_impl->docProvider; } -MemberDef *MemberDefImpl::getGroupAlias() const +const MemberDef *MemberDefImpl::getGroupAlias() const { return m_impl->groupAlias; } @@ -5848,7 +5857,7 @@ void MemberDefImpl::setDocsForDefinition(bool b) m_impl->docsForDefinition = b; } -void MemberDefImpl::setGroupAlias(MemberDef *md) +void MemberDefImpl::setGroupAlias(const MemberDef *md) { m_impl->groupAlias = md; } @@ -5911,28 +5920,32 @@ void MemberDefImpl::cacheTypedefVal(ClassDef*val, const QCString & templSpec, co void MemberDefImpl::copyArgumentNames(MemberDef *bmd) { { - ArgumentList *arguments = bmd->argumentList(); + const ArgumentList *arguments = bmd->argumentList(); if (m_impl->defArgList && arguments) { ArgumentListIterator aliDst(*m_impl->defArgList); ArgumentListIterator aliSrc(*arguments); - Argument *argDst, *argSrc; + Argument *argDst; + const Argument *argSrc; for (;(argDst=aliDst.current()) && (argSrc=aliSrc.current());++aliDst,++aliSrc) { argDst->name = argSrc->name; + argDst->docs = argSrc->docs; } } } { - ArgumentList *arguments = bmd->declArgumentList(); + const ArgumentList *arguments = bmd->declArgumentList(); if (m_impl->declArgList && arguments) { ArgumentListIterator aliDst(*m_impl->declArgList); ArgumentListIterator aliSrc(*arguments); - Argument *argDst, *argSrc; + Argument *argDst; + const Argument *argSrc; for (;(argDst=aliDst.current()) && (argSrc=aliSrc.current());++aliDst,++aliSrc) { argDst->name = argSrc->name; + argDst->docs = argSrc->docs; } } } @@ -6030,12 +6043,14 @@ void combineDeclarationAndDefinition(MemberDef *mdec,MemberDef *mdef) // mdef, mdef ? mdef->name().data() : "", // mdec, mdec ? mdec->name().data() : ""); + const MemberDef *cmdec = const_cast<const MemberDef*>(mdec); + const MemberDef *cmdef = const_cast<const MemberDef*>(mdef); ArgumentList *mdefAl = mdef->argumentList(); ArgumentList *mdecAl = mdec->argumentList(); - if (matchArguments2(mdef->getOuterScope(),mdef->getFileDef(),mdefAl, - mdec->getOuterScope(),mdec->getFileDef(),mdecAl, - TRUE - ) + if (matchArguments2(cmdef->getOuterScope(),cmdef->getFileDef(),mdefAl, + cmdec->getOuterScope(),cmdec->getFileDef(),mdecAl, + TRUE + ) ) /* match found */ { //printf("Found member %s: definition in %s (doc=`%s') and declaration in %s (doc=`%s')\n", diff --git a/src/memberdef.h b/src/memberdef.h index ecaebcd..a742117 100644 --- a/src/memberdef.h +++ b/src/memberdef.h @@ -220,10 +220,11 @@ class MemberDef : virtual public Definition virtual bool isPrototype() const = 0; // argument related members - virtual ArgumentList *argumentList() const = 0; - virtual ArgumentList *declArgumentList() const = 0; - virtual ArgumentList *templateArguments() const = 0; - virtual QList<ArgumentList> *definitionTemplateParameterLists() const = 0; + virtual const ArgumentList *argumentList() const = 0; + virtual ArgumentList *argumentList() = 0; + virtual const ArgumentList *declArgumentList() const = 0; + virtual const ArgumentList *templateArguments() const = 0; + virtual const QList<ArgumentList> *definitionTemplateParameterLists() const = 0; // member group related members virtual int getMemberGroupId() const = 0; @@ -254,7 +255,7 @@ class MemberDef : virtual public Definition virtual MemberDef *memberDefinition() const = 0; virtual MemberDef *memberDeclaration() const = 0; virtual MemberDef *inheritsDocsFrom() const = 0; - virtual MemberDef *getGroupAlias() const = 0; + virtual const MemberDef *getGroupAlias() const = 0; virtual ClassDef *category() const = 0; virtual MemberDef *categoryRelation() const = 0; @@ -354,7 +355,7 @@ class MemberDef : virtual public Definition virtual void setTemplateMaster(MemberDef *mt) = 0; virtual void addListReference(Definition *d) = 0; virtual void setDocsForDefinition(bool b) = 0; - virtual void setGroupAlias(MemberDef *md) = 0; + virtual void setGroupAlias(const MemberDef *md) = 0; virtual void cacheTypedefVal(ClassDef *val,const QCString &templSpec,const QCString &resolvedType) = 0; virtual void invalidateTypedefValCache() = 0; diff --git a/src/message.cpp b/src/message.cpp index 2f3a06f..ddf757a 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -167,17 +167,29 @@ static void format_warn(const char *file,int line,const char *text) static void do_warn(bool enabled, const char *file, int line, const char *prefix, const char *fmt, va_list args) { if (!enabled) return; // warning type disabled - const int bufSize = 40960; - char text[bufSize]; + + va_list argsCopy; + va_copy(argsCopy, args); + int l=0; if (prefix) { - qstrncpy(text,prefix,bufSize); l=strlen(prefix); } - vsnprintf(text+l, bufSize-l, fmt, args); + // determine needed buffersize based on: + // format + arguments + // prefix + // 1 position for `\0` + int bufSize = vsnprintf(NULL, 0, fmt, args) + l + 1; + char *text = (char *)malloc(sizeof(char) * bufSize); + if (prefix) + { + qstrncpy(text,prefix,bufSize); + } + vsnprintf(text+l, bufSize-l, fmt, argsCopy); text[bufSize-1]='\0'; format_warn(file,line,text); + free(text); } void warn(const char *file,int line,const char *fmt, ...) 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/pagedef.cpp b/src/pagedef.cpp index 8272cf6..9f11eb0 100644 --- a/src/pagedef.cpp +++ b/src/pagedef.cpp @@ -268,16 +268,16 @@ void PageDefImpl::writeDocumentation(OutputList &ol) } writePageDocumentation(ol); + ol.endContents(); ol.endPageDoc(); if (generateTreeView && getOuterScope()!=Doxygen::globalScope && !Config_getBool(DISABLE_INDEX)) { - ol.endContents(); endFileWithNavPath(getOuterScope(),ol); } else { - endFile(ol); + endFile(ol,FALSE,TRUE); } ol.popGeneratorState(); diff --git a/src/perlmodgen.cpp b/src/perlmodgen.cpp index d6e2af6..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 *) { } @@ -1617,23 +1607,23 @@ void PerlModGenerator::generatePerlModForMember(const MemberDef *md,const Defini md->memberType()!=MemberType_Enumeration) m_output.addFieldQuotedString("type", md->typeString()); - ArgumentList *al = md->argumentList(); + const ArgumentList *al = md->argumentList(); if (isFunc) //function { m_output.addFieldBoolean("const", al!=0 && al->constSpecifier) .addFieldBoolean("volatile", al!=0 && al->volatileSpecifier); m_output.openList("parameters"); - ArgumentList *declAl = md->declArgumentList(); - ArgumentList *defAl = md->argumentList(); + const ArgumentList *declAl = md->declArgumentList(); + const ArgumentList *defAl = md->argumentList(); if (declAl && defAl && declAl->count()>0) { ArgumentListIterator declAli(*declAl); ArgumentListIterator defAli(*defAl); - Argument *a; + const Argument *a; for (declAli.toFirst();(a=declAli.current());++declAli) { - Argument *defArg = defAli.current(); + const Argument *defArg = defAli.current(); m_output.openHash(); if (!a->name.isEmpty()) @@ -1665,7 +1655,7 @@ void PerlModGenerator::generatePerlModForMember(const MemberDef *md,const Defini { m_output.openList("parameters"); ArgumentListIterator ali(*al); - Argument *a; + const Argument *a; for (ali.toFirst();(a=ali.current());++ali) { m_output.openHash() 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(); @@ -73,9 +73,10 @@ struct CondCtx struct FileState { - FileState(int size) : lineNr(1), fileBuf(size), + FileState(int size) : lineNr(1), curlyCount(0),fileBuf(size), oldFileBuf(0), oldFileBufPos(0), bufState(0) {} int lineNr; + int curlyCount; BufStr fileBuf; BufStr *oldFileBuf; int oldFileBufPos; @@ -1088,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; @@ -1117,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 { @@ -1606,6 +1607,8 @@ static void readIncludeFile(const QCString &inc) fs->bufState = YY_CURRENT_BUFFER; fs->lineNr = oldLineNr; fs->fileName = oldFileName; + fs->curlyCount = g_curlyCount; + g_curlyCount = 0; // push the state on the stack g_includeStack.push(fs); // set the scanner to the include file @@ -2949,6 +2952,7 @@ CHARLIT (("'"\\[0-7]{1,3}"'")|("'"\\."'")|("'"[^'\\\n]{1,4}"'")) //preYYin = fs->oldYYin; g_inputBuf = fs->oldFileBuf; g_inputBufPos = fs->oldFileBufPos; + g_curlyCount = fs->curlyCount; setFileName(fs->fileName); DBG_CTX((stderr,"######## FileName %s\n",g_yyFileName.data())); 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/pycode.l b/src/pycode.l index b0ec68e..1a87bca 100644 --- a/src/pycode.l +++ b/src/pycode.l @@ -544,7 +544,7 @@ static bool getLinkInScope(const QCString &c, // scope const char *text ) { - MemberDef *md = 0; + const MemberDef *md = 0; const ClassDef *cd = 0; const FileDef *fd = 0; const NamespaceDef *nd = 0; @@ -569,7 +569,7 @@ static bool getLinkInScope(const QCString &c, // scope if (g_currentDefinition && g_currentMemberDef && md!=g_currentMemberDef && g_collectXRefs) { - addDocCrossReference(g_currentMemberDef,md); + addDocCrossReference(g_currentMemberDef,const_cast<MemberDef*>(md)); } //printf("d->getReference()=`%s' d->getOutputBase()=`%s' name=`%s' member name=`%s'\n",d->getReference().data(),d->getOutputFileBase().data(),d->name().data(),md->name().data()); diff --git a/src/pyscanner.l b/src/pyscanner.l index 41422bd..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; } @@ -537,6 +537,8 @@ STARTDOCSYMS "##" %x SingleQuoteString %x DoubleQuoteString %x TripleString +%x SingleQuoteStringIgnore +%x DoubleQuoteStringIgnore /* import */ %x FromMod @@ -1282,8 +1284,28 @@ STARTDOCSYMS "##" ); //Has base class-do stuff } + "'" { // start of a single quoted string + g_stringContext=YY_START; + BEGIN( SingleQuoteStringIgnore ); + } + "\"" { // start of a double quoted string + g_stringContext=YY_START; + BEGIN( DoubleQuoteStringIgnore ); + } } +<SingleQuoteStringIgnore>{ + "'" { // end of a single quoted string + BEGIN(g_stringContext); + } + . { } +} +<DoubleQuoteStringIgnore>{ + "\"" { // end of a double quoted string + BEGIN(g_stringContext); + } + . { } +} <ClassCaptureIndent>{ "\n"|({BB}"\n") { @@ -1702,6 +1724,10 @@ STARTDOCSYMS "##" lineCount(); } +<*>"'" { + fprintf(stderr,"Quote: %d\n",YY_START); + } + <*>. { //printf("[pyscanner] '%s' [ state %d ] [line %d] no match\n", // yytext, YY_START, yyLineNr); @@ -1746,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); @@ -1813,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(); @@ -1822,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 d3902b3..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) @@ -3941,6 +3974,7 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP}) else { current->endBodyLine = yyLineNr; + Entry * original_root = current_root; // save root this namespace is in if (current->section == Entry::NAMESPACE_SEC && current->type == "namespace") { int split_point; @@ -4005,6 +4039,7 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP}) ) { // namespaces and interfaces and java classes ends with a closing bracket without semicolon current->reset(); + current_root = original_root; // restore scope from before namespace descent initEntry(); memspecEntry = 0; BEGIN( FindMembers ) ; @@ -6252,6 +6287,43 @@ OPERATOR "operator"{B}*({ARITHOP}|{ASSIGNOP}|{LOGICOP}|{BITOP}) startCommentBlock(FALSE); BEGIN( DocBlock ); } +<FindMembers,FindFields,MemberSpec,FuncQual,SkipCurly,Operator,ClassVar,SkipInits,Bases,OldStyleArgs>"/**"[*]+{BL} { + + static bool javadocBanner = Config_getBool(JAVADOC_BANNER); + + if( javadocBanner ) { + lastDocContext = YY_START; + + //printf("Found comment banner at %s:%d\n",yyFileName,yyLineNr); + if (current_root->section & Entry::SCOPE_MASK) + { + current->inside = current_root->name+"::"; + } + current->docLine = yyLineNr; + current->docFile = yyFileName; + docBlockContext = YY_START; + docBlockInBody = YY_START==SkipCurly; + static bool javadocAutoBrief = Config_getBool(JAVADOC_AUTOBRIEF); + docBlockAutoBrief = javadocAutoBrief; + + QCString indent; + indent.fill(' ',computeIndent(yytext,g_column)); + docBlock=indent; + + if (docBlockAutoBrief) + { + current->briefLine = yyLineNr; + current->briefFile = yyFileName; + } + startCommentBlock(FALSE); + BEGIN( DocBlock ); + } else { + current->program += yytext ; + lastContext = YY_START ; + BEGIN( Comment ) ; + } + + } <FindMembers,FindFields,MemberSpec,FuncQual,SkipCurly,Operator,ClassVar,SkipInits,Bases,OldStyleArgs>("//"{B}*)?"/**"/[^/*] { removeSlashes=(yytext[1]=='/'); lastDocContext = YY_START; @@ -7135,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); @@ -7201,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); @@ -7233,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/sqlcode.l b/src/sqlcode.l index 33579b0..eec9732 100644 --- a/src/sqlcode.l +++ b/src/sqlcode.l @@ -15,6 +15,10 @@ %option never-interactive %option prefix="sqlcodeYY" +%option noyywrap +%option nounput +%option reentrant +%option extra-type="struct sqlcodeYY_state *" %{ @@ -31,133 +35,261 @@ #include "config.h" #include "filedef.h" #include "tooltip.h" +#include "message.h" #define YY_NEVER_INTERACTIVE 1 #define YY_NO_INPUT 1 #define YY_NO_UNISTD_H 1 -static CodeOutputInterface * g_code; -static QCString g_curClassName; -static QCString g_parmType; -static QCString g_parmName; -static const char * g_inputString; //!< the code fragment as text -static int g_inputPosition; //!< read offset during parsing -static int g_inputLines; //!< number of line in the code fragment -static int g_yyLineNr; //!< current line number -static bool g_needsTermination; -static const Definition *g_searchCtx; - -static bool g_exampleBlock; -static QCString g_exampleName; -static QCString g_exampleFile; - -static QCString g_type; -static QCString g_name; -static QCString g_args; -static QCString g_classScope; - -static QCString g_CurrScope; - -static FileDef * g_sourceFileDef; -static Definition * g_currentDefinition; -static MemberDef * g_currentMemberDef; -static bool g_includeCodeFragment; -static const char * g_currentFontClass; - -static void codify(const char* text) +struct sqlcodeYY_state +{ + CodeOutputInterface * code; + const char *inputString; //!< the code fragment as text + int inputPosition; //!< read offset during parsing + int inputLines; //!< number of line in the code fragment + int yyLineNr; //!< current line number + bool needsTermination; + const Definition *searchCtx; + + bool exampleBlock; + QCString exampleName; + QCString classScope; + + FileDef *sourceFileDef; + Definition *currentDefinition; + MemberDef *currentMemberDef; + bool includeCodeFragment; + const char *currentFontClass; +}; + +static void codify(const char* text); +static void setCurrentDoc(const QCString &anchor,yyscan_t yyscanner); +static void startCodeLine(yyscan_t yyscanner); +static void endFontClass(yyscan_t yyscanner); +static void endCodeLine(yyscan_t yyscanner); +static void nextCodeLine(yyscan_t yyscanner); +static void codifyLines(char *text,yyscan_t yyscanner); +static void startFontClass(const char *s,yyscan_t yyscanner); +static int countLines(yyscan_t yyscanner); +static int yyread(char *buf,int max_size,yyscan_t yyscanner); + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size,yyscanner); + +%} + +nl (\r\n|\r|\n) +ws [ \t]+ +idchar [A-Za-z0-9\-_]+ +keywords1 ("ADD"|"ALL"|"ALLOCATE"|"ALTER"|"AND"|"ANY"|"ARE"|"AS"|"ASENSITIVE"|"ASYMMETRIC"|"AT"|"ATOMIC"|"AUTHORIZATION"|"BETWEEN"|"BOTH"|"BY"|"CALL"|"CALLED"|"CASCADED"|"CAST") +keywords2 ("CHECK"|"CLOSE"|"COLLATE"|"COLUMN"|"COMMIT"|"CONNECT"|"CONSTRAINT"|"CONTINUE"|"CORRESPONDING"|"CREATE"|"CROSS"|"CUBE"|"CURRENT"|"CURRENT_DATE"|"CURRENT_DEFAULT_TRANSFORM_GROUP") +keywords3 ("CURRENT_PATH"|"CURRENT_ROLE"|"CURRENT_TIME"|"CURRENT_TIMESTAMP"|"CURRENT_TRANSFORM_GROUP_FOR_TYPE"|"CURRENT_USER") +keywords4 ("CURSOR"|"CYCLE"|"DAY"|"DEALLOCATE"|"DECLARE"|"DEFAULT"|"DELETE"|"DEREF"|"DESCRIBE"|"DETERMINISTIC"|"DISCONNECT"|"DISTINCT"|"DROP"|"DYNAMIC") +keywords5 ("EACH"|"ELEMENT"|"END-EXEC"|"ESCAPE"|"EXCEPT"|"EXEC"|"EXECUTE"|"EXISTS"|"EXTERNAL"|"FETCH"|"FILTER"|"FOR"|"FOREIGN"|"FREE"|"FROM"|"FULL"|"FUNCTION") +keywords6 ("GET"|"GLOBAL"|"GRANT"|"GROUP"|"GROUPING"|"HAVING"|"HOLD"|"HOUR"|"IDENTITY"|"IMMEDIATE"|"IN"|"INDICATOR"|"INNER"|"INOUT"|"INPUT"|"INSENSITIVE"|"INSERT"|"INTERSECT") +keywords7 ("INTERVAL"|"INTO"|"IS"|"ISOLATION"|"JOIN"|"LANGUAGE"|"LARGE"|"LATERAL"|"LEADING"|"LEFT"|"LIKE"|"LOCAL"|"LOCALTIME"|"LOCALTIMESTAMP"|"MATCH"|"MEMBER"|"MERGE"|"METHOD"|"MINUTE") +keywords8 ("MODIFIES"|"MODULE"|"MONTH"|"MULTISET"|"NATIONAL"|"NATURAL"|"NEW"|"NO"|"NONE"|"NOT"|"OF"|"OLD"|"ON"|"ONLY"|"OPEN"|"OR"|"ORDER"|"OUT"|"OUTER"|"OUTPUT") +keywords9 ("OVER"|"OVERLAPS"|"PARAMETER"|"PARTITION"|"PRECISION"|"PREPARE"|"PRIMARY"|"PROCEDURE"|"RANGE"|"READS"|"RECURSIVE"|"REF"|"REFERENCES"|"REFERENCING"|"REGR_AVGX"|"REGR_AVGY") +keywords10 ("REGR_COUNT"|"REGR_INTERCEPT"|"REGR_R2"|"REGR_SLOPE"|"REGR_SXX"|"REGR_SXY"|"REGR_SYY"|"RELEASE"|"RESULT"|"RETURN"|"RETURNS"|"REVOKE"|"RIGHT"|"ROLLBACK"|"ROLLUP"|"ROW"|"ROWS"|"SAVEPOINT") +keywords11 ("SCROLL"|"SEARCH"|"SECOND"|"SELECT"|"SENSITIVE"|"SESSION_USER"|"SET"|"SIMILAR"|"SOME"|"SPECIFIC"|"SPECIFICTYPE"|"SQL"|"SQLEXCEPTION"|"SQLSTATE"|"SQLWARNING"|"START"|"STATIC") +keywords12 ("SUBMULTISET"|"SYMMETRIC"|"SYSTEM"|"SYSTEM_USER"|"TABLE"|"THEN"|"TIMEZONE_HOUR"|"TIMEZONE_MINUTE"|"TO"|"TRAILING"|"TRANSLATION"|"TREAT"|"TRIGGER"|"UESCAPE"|"UNION") +keywords13 ("UNIQUE"|"UNNEST"|"UPDATE"|"UPPER"|"USER"|"USING"|"VALUE"|"VALUES"|"VAR_POP"|"VAR_SAMP"|"VARYING"|"WHEN"|"WHENEVER"|"WHERE"|"WIDTH_BUCKET"|"WINDOW"|"WITH"|"WITHIN"|"WITHOUT"|"YEAR") + +/* Need multiple keyword definitions due to max length */ +keyword (?i:{keywords1}|{keywords2}|{keywords3}|{keywords4}|{keywords5}|{keywords6}|{keywords7}|{keywords8}|{keywords9}|{keywords10}|{keywords11}|{keywords12}|{keywords13}) + +typekeyword (?i:"ARRAY"|"BIGINT"|"BINARY"|"BLOB"|"BOOLEAN"|"CHAR"|"CHARACTER"|"CLOB"|"DATE"|"DEC"|"DECIMAL"|"DOUBLE"|"FLOAT"|"INT"|"INTEGER"|"NCHAR"|"NCLOB"|"NUMERIC"|"NVARCHAR"|"REAL"|"SMALLINT"|"TIME"|"TIMESTAMP"|"VARCHAR") + +flowkeyword (?i:"CASE"|"IF"|"ELSE"|"BEGIN"|"END"|"WHILE") + +literalkeyword (?i:"FALSE"|"TRUE"|"NULL"|"UNKNOWN") +stringliteral (\"[^"]*\")|('[^']*') +number [0-9]+ +literals ({literalkeyword}|{stringliteral}|{number}) + +variable @{idchar}+ + +simplecomment --.* +commentopen "/\*" +commentclose "\*/" + +%x COMMENT + +%% + +{literals} { + startFontClass("stringliteral",yyscanner); + codifyLines(yytext,yyscanner); + endFontClass(yyscanner); + } + + +{keyword} { + startFontClass("keyword",yyscanner); + codifyLines(yytext,yyscanner); + endFontClass(yyscanner); + } + +{flowkeyword} { + startFontClass("keywordflow",yyscanner); + codifyLines(yytext,yyscanner); + endFontClass(yyscanner); + } + +{typekeyword} { + startFontClass("keywordtype",yyscanner); + codifyLines(yytext,yyscanner); + endFontClass(yyscanner); + } + +{variable} { + startFontClass("preprocessor",yyscanner); + codifyLines(yytext,yyscanner); + endFontClass(yyscanner); + } + +{simplecomment} { + startFontClass("comment",yyscanner); + codifyLines(yytext,yyscanner); + endFontClass(yyscanner); + } + +{commentopen} { + startFontClass("comment",yyscanner); + codifyLines(yytext,yyscanner); + BEGIN(COMMENT); + } + +<COMMENT>. { + codifyLines(yytext,yyscanner); + + } +<COMMENT>{nl} { + codifyLines(yytext,yyscanner); + } + +<COMMENT>{commentclose} { + codifyLines(yytext,yyscanner); + endFontClass(yyscanner); + BEGIN(INITIAL); + } + +{idchar} { + codifyLines(yytext,yyscanner); + } + +{nl} { + codifyLines(yytext,yyscanner); + } + +. { + codifyLines(yytext,yyscanner); + } + +%% + + +static void codify(const char* text, yyscan_t yyscanner) { - g_code->codify(text); + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + yyextra->code->codify(text); } -static void setCurrentDoc(const QCString &anchor) +static void setCurrentDoc(const QCString &anchor, yyscan_t yyscanner) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; if (Doxygen::searchIndex) { - if (g_searchCtx) + if (yyextra->searchCtx) { - Doxygen::searchIndex->setCurrentDoc(g_searchCtx,g_searchCtx->anchor(),FALSE); + Doxygen::searchIndex->setCurrentDoc(yyextra->searchCtx,yyextra->searchCtx->anchor(),FALSE); } else { - Doxygen::searchIndex->setCurrentDoc(g_sourceFileDef,anchor,TRUE); + Doxygen::searchIndex->setCurrentDoc(yyextra->sourceFileDef,anchor,TRUE); } } } -/*! start a new line of code, inserting a line number if g_sourceFileDef +/*! start a new line of code, inserting a line number if yyextra->sourceFileDef * is TRUE. If a definition starts at the current line, then the line * number is linked to the documentation of that definition. */ -static void startCodeLine() +static void startCodeLine(yyscan_t yyscanner) { - if (g_sourceFileDef) + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if (yyextra->sourceFileDef) { - Definition *d = g_sourceFileDef->getSourceDefinition(g_yyLineNr); + Definition *d = yyextra->sourceFileDef->getSourceDefinition(yyextra->yyLineNr); - if (!g_includeCodeFragment && d && d->isLinkableInProject()) + if (!yyextra->includeCodeFragment && d && d->isLinkableInProject()) { - g_currentDefinition = d; - g_currentMemberDef = g_sourceFileDef->getSourceMember(g_yyLineNr); - g_classScope = d->name().copy(); + yyextra->currentDefinition = d; + yyextra->currentMemberDef = yyextra->sourceFileDef->getSourceMember(yyextra->yyLineNr); + yyextra->classScope = d->name().copy(); QCString lineAnchor; - lineAnchor.sprintf("l%05d",g_yyLineNr); - if (g_currentMemberDef) + lineAnchor.sprintf("l%05d",yyextra->yyLineNr); + if (yyextra->currentMemberDef) { - g_code->writeLineNumber(g_currentMemberDef->getReference(), - g_currentMemberDef->getOutputFileBase(), - g_currentMemberDef->anchor(),g_yyLineNr); - setCurrentDoc(lineAnchor); + yyextra->code->writeLineNumber(yyextra->currentMemberDef->getReference(), + yyextra->currentMemberDef->getOutputFileBase(), + yyextra->currentMemberDef->anchor(),yyextra->yyLineNr); + setCurrentDoc(lineAnchor,yyscanner); } else { - g_code->writeLineNumber(d->getReference(), + yyextra->code->writeLineNumber(d->getReference(), d->getOutputFileBase(), - 0,g_yyLineNr); - setCurrentDoc(lineAnchor); + 0,yyextra->yyLineNr); + setCurrentDoc(lineAnchor,yyscanner); } } else { - g_code->writeLineNumber(0,0,0,g_yyLineNr); + yyextra->code->writeLineNumber(0,0,0,yyextra->yyLineNr); } } - g_code->startCodeLine(g_sourceFileDef); + yyextra->code->startCodeLine(yyextra->sourceFileDef); - if (g_currentFontClass) + if (yyextra->currentFontClass) { - g_code->startFontClass(g_currentFontClass); + yyextra->code->startFontClass(yyextra->currentFontClass); } } -static void endFontClass() +static void endFontClass(yyscan_t yyscanner) { - if (g_currentFontClass) + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if (yyextra->currentFontClass) { - g_code->endFontClass(); - g_currentFontClass=0; + yyextra->code->endFontClass(); + yyextra->currentFontClass=0; } } -static void endCodeLine() +static void endCodeLine(yyscan_t yyscanner) { - endFontClass(); - g_code->endCodeLine(); + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + endFontClass(yyscanner); + yyextra->code->endCodeLine(); } -static void nextCodeLine() +static void nextCodeLine(yyscan_t yyscanner) { - const char *fc = g_currentFontClass; - endCodeLine(); - if (g_yyLineNr<g_inputLines) + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + const char *fc = yyextra->currentFontClass; + endCodeLine(yyscanner); + if (yyextra->yyLineNr<yyextra->inputLines) { - g_currentFontClass = fc; - startCodeLine(); + yyextra->currentFontClass = fc; + startCodeLine(yyscanner); } } -static void codifyLines(char *text) +static void codifyLines(char *text,yyscan_t yyscanner) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; char *p=text,*sp=p; char c; bool done=FALSE; @@ -170,30 +302,32 @@ static void codifyLines(char *text) if (c=='\n') { - g_yyLineNr++; + yyextra->yyLineNr++; *(p-1)='\0'; - g_code->codify(sp); - nextCodeLine(); + yyextra->code->codify(sp); + nextCodeLine(yyscanner); } else { - g_code->codify(sp); + yyextra->code->codify(sp); done=TRUE; } } } -static void startFontClass(const char *s) +static void startFontClass(const char *s,yyscan_t yyscanner) { - endFontClass(); - g_code->startFontClass(s); - g_currentFontClass=s; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + endFontClass(yyscanner); + yyextra->code->startFontClass(s); + yyextra->currentFontClass=s; } /*! counts the number of lines in the input */ -static int countLines() +static int countLines(yyscan_t yyscanner) { - const char *p=g_inputString; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + const char *p=yyextra->inputString; char c; int count=1; while ((c=*p)) @@ -201,142 +335,31 @@ static int countLines() p++ ; if (c=='\n') count++; } - if (p>g_inputString && *(p-1)!='\n') + if (p>yyextra->inputString && *(p-1)!='\n') { // last line does not end with a \n, so we add an extra // line and explicitly terminate the line after parsing. count++, - g_needsTermination=TRUE; + yyextra->needsTermination=TRUE; } return count; } -#undef YY_INPUT -#define YY_INPUT(buf,result,max_size) result=yyread(buf,max_size); - -static int yyread(char *buf,int max_size) +static int yyread(char *buf,int max_size,yyscan_t yyscanner) { + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; int c=0; - while( c < max_size && g_inputString[g_inputPosition] ) + while( c < max_size && yyextra->inputString[yyextra->inputPosition] ) { - *buf = g_inputString[g_inputPosition++] ; + *buf = yyextra->inputString[yyextra->inputPosition++] ; c++; buf++; } return c; } -%} - -nl (\r\n|\r|\n) -ws [ \t]+ -idchar [A-Za-z0-9\-_]+ -keywords1 ("ADD"|"ALL"|"ALLOCATE"|"ALTER"|"AND"|"ANY"|"ARE"|"AS"|"ASENSITIVE"|"ASYMMETRIC"|"AT"|"ATOMIC"|"AUTHORIZATION"|"BETWEEN"|"BOTH"|"BY"|"CALL"|"CALLED"|"CASCADED"|"CAST") -keywords2 ("CHECK"|"CLOSE"|"COLLATE"|"COLUMN"|"COMMIT"|"CONNECT"|"CONSTRAINT"|"CONTINUE"|"CORRESPONDING"|"CREATE"|"CROSS"|"CUBE"|"CURRENT"|"CURRENT_DATE"|"CURRENT_DEFAULT_TRANSFORM_GROUP") -keywords3 ("CURRENT_PATH"|"CURRENT_ROLE"|"CURRENT_TIME"|"CURRENT_TIMESTAMP"|"CURRENT_TRANSFORM_GROUP_FOR_TYPE"|"CURRENT_USER") -keywords4 ("CURSOR"|"CYCLE"|"DAY"|"DEALLOCATE"|"DECLARE"|"DEFAULT"|"DELETE"|"DEREF"|"DESCRIBE"|"DETERMINISTIC"|"DISCONNECT"|"DISTINCT"|"DROP"|"DYNAMIC") -keywords5 ("EACH"|"ELEMENT"|"END-EXEC"|"ESCAPE"|"EXCEPT"|"EXEC"|"EXECUTE"|"EXISTS"|"EXTERNAL"|"FETCH"|"FILTER"|"FOR"|"FOREIGN"|"FREE"|"FROM"|"FULL"|"FUNCTION") -keywords6 ("GET"|"GLOBAL"|"GRANT"|"GROUP"|"GROUPING"|"HAVING"|"HOLD"|"HOUR"|"IDENTITY"|"IMMEDIATE"|"IN"|"INDICATOR"|"INNER"|"INOUT"|"INPUT"|"INSENSITIVE"|"INSERT"|"INTERSECT") -keywords7 ("INTERVAL"|"INTO"|"IS"|"ISOLATION"|"JOIN"|"LANGUAGE"|"LARGE"|"LATERAL"|"LEADING"|"LEFT"|"LIKE"|"LOCAL"|"LOCALTIME"|"LOCALTIMESTAMP"|"MATCH"|"MEMBER"|"MERGE"|"METHOD"|"MINUTE") -keywords8 ("MODIFIES"|"MODULE"|"MONTH"|"MULTISET"|"NATIONAL"|"NATURAL"|"NEW"|"NO"|"NONE"|"NOT"|"OF"|"OLD"|"ON"|"ONLY"|"OPEN"|"OR"|"ORDER"|"OUT"|"OUTER"|"OUTPUT") -keywords9 ("OVER"|"OVERLAPS"|"PARAMETER"|"PARTITION"|"PRECISION"|"PREPARE"|"PRIMARY"|"PROCEDURE"|"RANGE"|"READS"|"RECURSIVE"|"REF"|"REFERENCES"|"REFERENCING"|"REGR_AVGX"|"REGR_AVGY") -keywords10 ("REGR_COUNT"|"REGR_INTERCEPT"|"REGR_R2"|"REGR_SLOPE"|"REGR_SXX"|"REGR_SXY"|"REGR_SYY"|"RELEASE"|"RESULT"|"RETURN"|"RETURNS"|"REVOKE"|"RIGHT"|"ROLLBACK"|"ROLLUP"|"ROW"|"ROWS"|"SAVEPOINT") -keywords11 ("SCROLL"|"SEARCH"|"SECOND"|"SELECT"|"SENSITIVE"|"SESSION_USER"|"SET"|"SIMILAR"|"SOME"|"SPECIFIC"|"SPECIFICTYPE"|"SQL"|"SQLEXCEPTION"|"SQLSTATE"|"SQLWARNING"|"START"|"STATIC") -keywords12 ("SUBMULTISET"|"SYMMETRIC"|"SYSTEM"|"SYSTEM_USER"|"TABLE"|"THEN"|"TIMEZONE_HOUR"|"TIMEZONE_MINUTE"|"TO"|"TRAILING"|"TRANSLATION"|"TREAT"|"TRIGGER"|"UESCAPE"|"UNION") -keywords13 ("UNIQUE"|"UNNEST"|"UPDATE"|"UPPER"|"USER"|"USING"|"VALUE"|"VALUES"|"VAR_POP"|"VAR_SAMP"|"VARYING"|"WHEN"|"WHENEVER"|"WHERE"|"WIDTH_BUCKET"|"WINDOW"|"WITH"|"WITHIN"|"WITHOUT"|"YEAR") - -/* Need multiple keyword definitions due to max length */ -keyword (?i:{keywords1}|{keywords2}|{keywords3}|{keywords4}|{keywords5}|{keywords6}|{keywords7}|{keywords8}|{keywords9}|{keywords10}|{keywords11}|{keywords12}|{keywords13}) - -typekeyword (?i:"ARRAY"|"BIGINT"|"BINARY"|"BLOB"|"BOOLEAN"|"CHAR"|"CHARACTER"|"CLOB"|"DATE"|"DEC"|"DECIMAL"|"DOUBLE"|"FLOAT"|"INT"|"INTEGER"|"NCHAR"|"NCLOB"|"NUMERIC"|"NVARCHAR"|"REAL"|"SMALLINT"|"TIME"|"TIMESTAMP"|"VARCHAR") - -flowkeyword (?i:"CASE"|"IF"|"ELSE"|"BEGIN"|"END"|"WHILE") - -literalkeyword (?i:"FALSE"|"TRUE"|"NULL"|"UNKNOWN") -stringliteral (\"[^"]*\")|('[^']*') -number [0-9]+ -literals ({literalkeyword}|{stringliteral}|{number}) -variable @{idchar}+ - -simplecomment --.* -commentopen "/\*" -commentclose "\*/" - -%option noyywrap -%option nounput - -%x COMMENT - -%% - -{literals} { - startFontClass("stringliteral"); - codifyLines(yytext); - endFontClass(); - } - - -{keyword} { - startFontClass("keyword"); - codifyLines(yytext); - endFontClass(); - } - -{flowkeyword} { - startFontClass("keywordflow"); - codifyLines(yytext); - endFontClass(); - } - -{typekeyword} { - startFontClass("keywordtype"); - codifyLines(yytext); - endFontClass(); - } - -{variable} { - startFontClass("preprocessor"); - codifyLines(yytext); - endFontClass(); - } - -{simplecomment} { - startFontClass("comment"); - codifyLines(yytext); - endFontClass(); - } - -{commentopen} { - startFontClass("comment"); - codifyLines(yytext); - BEGIN(COMMENT); - } - -<COMMENT>. { - codifyLines(yytext); - } -<COMMENT>{nl} { - codifyLines(yytext); - } - -<COMMENT>{commentclose} { - codifyLines(yytext); - endFontClass(); - BEGIN(INITIAL); - } - -{idchar} { - codifyLines(yytext); - } - -{nl} { - codifyLines(yytext); - } - -. { - codifyLines(yytext); - } - -%% +// public interface ----------------------------------------------------------- +static yyscan_t yyscanner; +static struct sqlcodeYY_state sqlcode_extra; void parseSqlCode( CodeOutputInterface &od, @@ -354,68 +377,80 @@ void parseSqlCode( ) { if (s.isEmpty()) return; + + sqlcodeYYlex_init_extra(&sqlcode_extra, &yyscanner); + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + +#ifdef FLEX_DEBUG + yyset_debug(1,yyscanner); +#endif + + printlex(yy_flex_debug, TRUE, __FILE__, fd ? fd->fileName().data(): NULL); - g_code = &od; - g_inputString = s; - g_inputPosition = 0; - g_currentFontClass = 0; - g_needsTermination = FALSE; - g_searchCtx=searchCtx; + yyextra->code = &od; + yyextra->inputString = s; + yyextra->inputPosition = 0; + yyextra->currentFontClass = 0; + yyextra->needsTermination = FALSE; + yyextra->searchCtx=searchCtx; if (startLine!=-1) - g_yyLineNr = startLine; + yyextra->yyLineNr = startLine; else - g_yyLineNr = 1; + yyextra->yyLineNr = 1; if (endLine!=-1) - g_inputLines = endLine+1; + yyextra->inputLines = endLine+1; else - g_inputLines = g_yyLineNr + countLines() - 1; + yyextra->inputLines = yyextra->yyLineNr + countLines(yyscanner) - 1; - g_exampleBlock = exBlock; - g_exampleName = exName; - g_sourceFileDef = fd; + yyextra->exampleBlock = exBlock; + yyextra->exampleName = exName; + yyextra->sourceFileDef = fd; bool cleanupSourceDef = FALSE; if (exBlock && fd==0) { // create a dummy filedef for the example - g_sourceFileDef = createFileDef("",(exName?exName:"generated")); + yyextra->sourceFileDef = createFileDef("",(exName?exName:"generated")); cleanupSourceDef = TRUE; } - if (g_sourceFileDef) + if (yyextra->sourceFileDef) { - setCurrentDoc("l00001"); + setCurrentDoc("l00001",yyscanner); } - g_includeCodeFragment = inlineFragment; + yyextra->includeCodeFragment = inlineFragment; // Starts line 1 on the output - startCodeLine(); + startCodeLine(yyscanner); - sqlcodeYYrestart( sqlcodeYYin ); + sqlcodeYYrestart( yyin,yyscanner ); - sqlcodeYYlex(); + sqlcodeYYlex(yyscanner); - if (g_needsTermination) + if (yyextra->needsTermination) { - endCodeLine(); + endCodeLine(yyscanner); } if (cleanupSourceDef) { // delete the temporary file definition used for this example - delete g_sourceFileDef; - g_sourceFileDef=0; + delete yyextra->sourceFileDef; + yyextra->sourceFileDef=0; } + printlex(yy_flex_debug, FALSE, __FILE__, fd ? fd->fileName().data(): NULL); + sqlcodeYYlex_destroy(yyscanner); return; } void resetSqlCodeParserState() { - g_currentDefinition = 0; - g_currentMemberDef = 0; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + yyextra->currentDefinition = 0; + yyextra->currentMemberDef = 0; } #if !defined(YY_FLEX_SUBMINOR_VERSION) diff --git a/src/sqlite3gen.cpp b/src/sqlite3gen.cpp index eceea06..5a8e81d 100644 --- a/src/sqlite3gen.cpp +++ b/src/sqlite3gen.cpp @@ -1015,16 +1015,16 @@ static void insertMemberReference(const MemberDef *src, const MemberDef *dst, co static void insertMemberFunctionParams(int memberdef_id, const MemberDef *md, const Definition *def) { - ArgumentList *declAl = md->declArgumentList(); - ArgumentList *defAl = md->argumentList(); + const ArgumentList *declAl = md->declArgumentList(); + const ArgumentList *defAl = md->argumentList(); if (declAl!=0 && defAl!=0 && declAl->count()>0) { ArgumentListIterator declAli(*declAl); ArgumentListIterator defAli(*defAl); - Argument *a; + const Argument *a; for (declAli.toFirst();(a=declAli.current());++declAli) { - Argument *defArg = defAli.current(); + const Argument *defArg = defAli.current(); if (!a->attrib.isEmpty()) { @@ -1410,7 +1410,7 @@ static void writeTemplateArgumentList(const ArgumentList * al, static void writeMemberTemplateLists(const MemberDef *md) { - ArgumentList *templMd = md->templateArguments(); + const ArgumentList *templMd = md->templateArguments(); if (templMd) // function template prefix { writeTemplateArgumentList(templMd,md->getClassDef(),md->getFileDef()); @@ -1434,7 +1434,7 @@ QCString getSQLDocBlock(const Definition *scope, fileName, lineNr, const_cast<Definition*>(scope), - const_cast<MemberDef*>(reinterpret_cast<const MemberDef*>(def)), + dynamic_cast<const MemberDef*>(def), doc, FALSE, FALSE @@ -1675,7 +1675,7 @@ static void generateSqlite3ForMember(const MemberDef *md, struct Refid scope_ref if (isFunc) { - ArgumentList *al = md->argumentList(); + const ArgumentList *al = md->argumentList(); if (al!=0) { bindIntParameter(memberdef_insert,":const",al->constSpecifier); @@ -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 bbd5773..9cf7b82 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; @@ -2086,7 +2087,7 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, bool found=FALSE; if (!insideString) { - MemberDef *md=0; + const MemberDef *md=0; const ClassDef *cd=0; const FileDef *fd=0; const NamespaceDef *nd=0; @@ -3771,8 +3772,8 @@ static bool matchArgument2( // new algorithm for argument matching -bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,ArgumentList *srcAl, - const Definition *dstScope,const FileDef *dstFileScope,ArgumentList *dstAl, +bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,const ArgumentList *srcAl, + const Definition *dstScope,const FileDef *dstFileScope,const ArgumentList *dstAl, bool checkCV) { //printf("*** matchArguments2\n"); @@ -3799,7 +3800,7 @@ bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,Argu { // special case for finding match between func() and func(void) Argument *a=new Argument; a->type = "void"; - srcAl->append(a); + const_cast<ArgumentList*>(srcAl)->append(a); MATCH return TRUE; } @@ -3808,7 +3809,7 @@ bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,Argu { // special case for finding match between func(void) and func() Argument *a=new Argument; a->type = "void"; - dstAl->append(a); + const_cast<ArgumentList*>(dstAl)->append(a); MATCH return TRUE; } @@ -4022,7 +4023,7 @@ static void findMembersWithSpecificName(MemberName *mn, if (args && !md->isDefine() && qstrcmp(args,"()")!=0) { argList=new ArgumentList; - ArgumentList *mdAl = md->argumentList(); + const ArgumentList *mdAl = md->argumentList(); stringToArgumentList(args,argList); match=matchArguments2( md->getOuterScope(),fd,mdAl, @@ -4064,7 +4065,7 @@ static void findMembersWithSpecificName(MemberName *mn, bool getDefs(const QCString &scName, const QCString &mbName, const char *args, - MemberDef *&md, + const MemberDef *&md, const ClassDef *&cd, const FileDef *&fd, const NamespaceDef *&nd, @@ -4342,7 +4343,7 @@ bool getDefs(const QCString &scName, // namespaceName.data(),mn->count()); bool found=FALSE; MemberNameIterator mmli(*mn); - MemberDef *mmd; + const MemberDef *mmd; for (mmli.toFirst();((mmd=mmli.current()) && !found);++mmli) { //printf("mmd->getNamespaceDef()=%p fnd=%p\n", @@ -4373,7 +4374,7 @@ bool getDefs(const QCString &scName, if (args && qstrcmp(args,"()")!=0) { argList=new ArgumentList; - ArgumentList *mmdAl = mmd->argumentList(); + const ArgumentList *mmdAl = mmd->argumentList(); stringToArgumentList(args,argList); match=matchArguments2( mmd->getOuterScope(),mmd->getFileDef(),mmdAl, @@ -4406,7 +4407,7 @@ bool getDefs(const QCString &scName, } } } - if (found) + if (found) { if (!md->isLinkable()) { @@ -4712,7 +4713,7 @@ bool resolveRef(/* in */ const char *scName, QCString scopeStr=scName; - MemberDef *md = 0; + const MemberDef *md = 0; const ClassDef *cd = 0; const FileDef *fd = 0; const NamespaceDef *nd = 0; @@ -5135,7 +5136,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; @@ -5481,10 +5482,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) { @@ -6352,7 +6355,7 @@ QCString normalizeNonTemplateArgumentsInString( if (formalArgs) // check that n is not a formal template argument { ArgumentListIterator formAli(*formalArgs); - Argument *formArg; + const Argument *formArg; for (formAli.toFirst(); (formArg=formAli.current()) && !found; ++formAli @@ -7085,6 +7088,7 @@ QCString latexFilterURL(const char *s) switch (c) { case '#': t << "\\#"; break; + case '%': t << "\\%"; break; default: t << c; break; @@ -7420,24 +7424,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, @@ -7939,12 +7947,12 @@ QCString expandAlias(const QCString &aliasName,const QCString &aliasValue) return result; } -void writeTypeConstraints(OutputList &ol,const Definition *d,ArgumentList *al) +void writeTypeConstraints(OutputList &ol,const Definition *d,const ArgumentList *al) { if (al==0) return; ol.startConstraintList(theTranslator->trTypeConstraints()); ArgumentListIterator ali(*al); - Argument *a; + const Argument *a; for (;(a=ali.current());++ali) { ol.startConstraintParam(); @@ -8144,7 +8152,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 @@ -8460,16 +8468,20 @@ QCString getLanguageSpecificSeparator(SrcLangExt lang,bool classScope) return "::"; } } - +/** Checks whether the given url starts with a supported protocol */ +bool isURL(const QCString &url) +{ + QCString loc_url = url.stripWhiteSpace(); + return loc_url.left(5)=="http:" || loc_url.left(6)=="https:" || + loc_url.left(4)=="ftp:" || loc_url.left(5)=="file:"; +} /** Corrects URL \a url according to the relative path \a relPath. * Returns the corrected URL. For absolute URLs no correction will be done. */ QCString correctURL(const QCString &url,const QCString &relPath) { QCString result = url; - if (!relPath.isEmpty() && - url.left(5)!="http:" && url.left(6)!="https:" && - url.left(4)!="ftp:" && url.left(5)!="file:") + if (!relPath.isEmpty() && !isURL(url)) { result.prepend(relPath); } @@ -142,7 +142,7 @@ QCString dateToString(bool); bool getDefs(const QCString &scopeName, const QCString &memberName, const char *, - MemberDef *&md, + const MemberDef *&md, const ClassDef *&cd, const FileDef *&fd, const NamespaceDef *&nd, @@ -185,8 +185,8 @@ void writePageRef(OutputDocInterface &od,const char *cn,const char *mn); QCString getCanonicalTemplateSpec(const Definition *d,const FileDef *fs,const QCString& spec); -bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,ArgumentList *srcAl, - const Definition *dstScope,const FileDef *dstFileScope,ArgumentList *dstAl, +bool matchArguments2(const Definition *srcScope,const FileDef *srcFileScope,const ArgumentList *srcAl, + const Definition *dstScope,const FileDef *dstFileScope,const ArgumentList *dstAl, bool checkCV ); @@ -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(); @@ -416,7 +417,7 @@ int countAliasArguments(const QCString argList); QCString resolveAliasCmd(const QCString aliasCmd); QCString expandAlias(const QCString &aliasName,const QCString &aliasValue); -void writeTypeConstraints(OutputList &ol,const Definition *d,ArgumentList *al); +void writeTypeConstraints(OutputList &ol,const Definition *d,const ArgumentList *al); QCString convertCharEntitiesToUTF8(const QCString &s); @@ -451,6 +452,8 @@ bool copyFile(const QCString &src,const QCString &dest); QCString extractBlock(const QCString text,const QCString marker); int lineBlock(const QCString text,const QCString marker); +bool isURL(const QCString &url); + QCString correctURL(const QCString &url,const QCString &relPath); QCString processMarkup(const QCString &s); diff --git a/src/vhdlcode.l b/src/vhdlcode.l index f52a539..7c6cfa4 100644 --- a/src/vhdlcode.l +++ b/src/vhdlcode.l @@ -433,10 +433,10 @@ static void writeMultiLineCodeLink(CodeOutputInterface &ol, static void setParameterList(const MemberDef *md) { g_classScope = md->getClassDef() ? md->getClassDef()->name().data() : ""; - ArgumentList *al = md->argumentList(); + const ArgumentList *al = md->argumentList(); if (al==0) return; ArgumentListIterator ali(*al); - Argument *a; + const Argument *a; for (ali.toFirst();(a=ali.current());++ali) { g_parmName = a->name.copy(); diff --git a/src/vhdldocgen.cpp b/src/vhdldocgen.cpp index 55c1c17..bba249a 100644 --- a/src/vhdldocgen.cpp +++ b/src/vhdldocgen.cpp @@ -2002,7 +2002,7 @@ void VhdlDocGen::writeVHDLDeclaration(const MemberDef* mdef,OutputList &ol, QCString ltype(mdef->typeString()); QCString largs(mdef->argsString()); ClassDef *kl=0; - ArgumentList *alp = mdef->argumentList(); + const ArgumentList *alp = mdef->argumentList(); QCString nn; //VhdlDocGen::adjustRecordMember(mdef); if (gd) gd=0; 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/xmlcode.l b/src/xmlcode.l index e792ea9..42218b1 100644 --- a/src/xmlcode.l +++ b/src/xmlcode.l @@ -35,6 +35,7 @@ #include "config.h" #include "filedef.h" #include "tooltip.h" +#include "message.h" #define YY_NEVER_INTERACTIVE 1 #define YY_NO_INPUT 1 @@ -338,6 +339,7 @@ void parseXmlCode( ) { if (s.isEmpty()) return; + printlex(yy_flex_debug, TRUE, __FILE__, fd ? fd->fileName().data(): NULL); g_code = &od; g_inputString = s; @@ -393,6 +395,7 @@ void parseXmlCode( g_sourceFileDef=0; } + printlex(yy_flex_debug, FALSE, __FILE__, fd ? fd->fileName().data(): NULL); return; } 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 a96b4f9..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" @@ -336,7 +338,7 @@ void XMLCodeGenerator::finish() if (m_insideCodeLine) endCodeLine(); } -static void writeTemplateArgumentList(ArgumentList *al, +static void writeTemplateArgumentList(const ArgumentList *al, FTextStream &t, const Definition *scope, const FileDef *fileScope, @@ -348,7 +350,7 @@ static void writeTemplateArgumentList(ArgumentList *al, { t << indentStr << "<templateparamlist>" << endl; ArgumentListIterator ali(*al); - Argument *a; + const Argument *a; for (ali.toFirst();(a=ali.current());++ali) { t << indentStr << " <param>" << endl; @@ -383,7 +385,7 @@ static void writeTemplateArgumentList(ArgumentList *al, static void writeMemberTemplateLists(const MemberDef *md,FTextStream &t) { - ArgumentList *templMd = md->templateArguments(); + const ArgumentList *templMd = md->templateArguments(); if (templMd) // function template prefix { writeTemplateArgumentList(templMd,t,md->getClassDef(),md->getFileDef(),8); @@ -589,7 +591,7 @@ static void generateXMLForMember(const MemberDef *md,FTextStream &ti,FTextStream if (isFunc) { - ArgumentList *al = md->argumentList(); + const ArgumentList *al = md->argumentList(); t << " const=\""; if (al!=0 && al->constSpecifier) t << "yes"; else t << "no"; t << "\""; @@ -838,13 +840,13 @@ static void generateXMLForMember(const MemberDef *md,FTextStream &ti,FTextStream if (isFunc) //function { - ArgumentList *declAl = md->declArgumentList(); - ArgumentList *defAl = md->argumentList(); + const ArgumentList *declAl = md->declArgumentList(); + const ArgumentList *defAl = md->argumentList(); if (declAl && defAl && declAl->count()>0) { ArgumentListIterator declAli(*declAl); ArgumentListIterator defAli(*defAl); - Argument *a; + const Argument *a; for (declAli.toFirst();(a=declAli.current());++declAli) { Argument *defArg = defAli.current(); @@ -908,7 +910,7 @@ static void generateXMLForMember(const MemberDef *md,FTextStream &ti,FTextStream else { ArgumentListIterator ali(*md->argumentList()); - Argument *a; + const Argument *a; for (ali.toFirst();(a=ali.current());++ali) { t << " <param><defname>" << a->type << "</defname></param>" << endl; @@ -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; |