summaryrefslogtreecommitdiffstats
path: root/src/dot.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dot.cpp')
-rw-r--r--src/dot.cpp4537
1 files changed, 90 insertions, 4447 deletions
diff --git a/src/dot.cpp b/src/dot.cpp
index 5aca277..5cdf92c 100644
--- a/src/dot.cpp
+++ b/src/dot.cpp
@@ -1,13 +1,10 @@
/*****************************************************************************
*
- *
- *
- *
- * Copyright (C) 1997-2015 by Dimitri van Heesch.
+ * Copyright (C) 1997-2019 by Dimitri van Heesch.
*
* Permission to use, copy, modify, and distribute this software and its
- * documentation under the terms of the GNU General Public License is hereby
- * granted. No representations are made about the suitability of this software
+ * documentation under the terms of the GNU General Public License is hereby
+ * granted. No representations are made about the suitability of this software
* for any purpose. It is provided "as is" without express or implied warranty.
* See the GNU General Public License for more details.
*
@@ -26,392 +23,47 @@
#include <qwaitcondition.h>
#include <qregexp.h>
+#include "config.h"
#include "dot.h"
-#include "doxygen.h"
-#include "message.h"
+#include "dotrunner.h"
+#include "dotfilepatcher.h"
#include "util.h"
-#include "config.h"
-#include "language.h"
-#include "defargs.h"
-#include "docparser.h"
-#include "debug.h"
-#include "pagedef.h"
#include "portable.h"
-#include "dirdef.h"
-#include "vhdldocgen.h"
+#include "message.h"
#include "ftextstream.h"
-#include "md5.h"
-#include "memberlist.h"
-#include "groupdef.h"
-#include "classlist.h"
-#include "filename.h"
-#include "namespacedef.h"
-#include "memberdef.h"
-#include "membergroup.h"
+#include "doxygen.h"
+#include "language.h"
+#include "index.h"
#define MAP_CMD "cmapx"
-//#define FONTNAME "Helvetica"
-#define FONTNAME getDotFontName()
-#define FONTSIZE getDotFontSize()
-
-//--------------------------------------------------------------------
-
-static const char svgZoomHeader[] =
-"<svg id=\"main\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:space=\"preserve\" onload=\"init(evt)\">\n"
-"<style type=\"text/css\"><![CDATA[\n"
-".edge:hover path { stroke: red; }\n"
-".edge:hover polygon { stroke: red; fill: red; }\n"
-"]]></style>\n"
-"<script type=\"text/javascript\"><![CDATA[\n"
-"var edges = document.getElementsByTagName('g');\n"
-"if (edges && edges.length) {\n"
-" for (var i=0;i<edges.length;i++) {\n"
-" if (edges[i].id.substr(0,4)=='edge') {\n"
-" edges[i].setAttribute('class','edge');\n"
-" }\n"
-" }\n"
-"}\n"
-"]]></script>\n"
-" <defs>\n"
-" <circle id=\"rim\" cx=\"0\" cy=\"0\" r=\"7\"/>\n"
-" <circle id=\"rim2\" cx=\"0\" cy=\"0\" r=\"3.5\"/>\n"
-" <g id=\"zoomPlus\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"zoomplus.mouseover\" end=\"zoomplus.mouseout\"/>\n"
-" </use>\n"
-" <path d=\"M-4,0h8M0,-4v8\" fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" pointer-events=\"none\"/>\n"
-" </g>\n"
-" <g id=\"zoomMin\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"zoomminus.mouseover\" end=\"zoomminus.mouseout\"/>\n"
-" </use>\n"
-" <path d=\"M-4,0h8\" fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" pointer-events=\"none\"/>\n"
-" </g>\n"
-" <g id=\"dirArrow\">\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-" <g id=\"resetDef\">\n"
-" <use xlink:href=\"#rim2\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"reset.mouseover\" end=\"reset.mouseout\"/>\n"
-" </use>\n"
-" </g>\n"
-" </defs>\n"
-"\n"
-"<script type=\"text/javascript\">\n"
-;
-
-static const char svgZoomFooter[] =
-// navigation panel
-" <g id=\"navigator\" transform=\"translate(0 0)\" fill=\"#404254\">\n"
-" <rect fill=\"#f2f5e9\" fill-opacity=\"0.5\" stroke=\"#606060\" stroke-width=\".5\" x=\"0\" y=\"0\" width=\"60\" height=\"60\"/>\n"
-// zoom in
-" <use id=\"zoomplus\" xlink:href=\"#zoomPlus\" x=\"17\" y=\"9\" onmousedown=\"handleZoom(evt,'in')\"/>\n"
-// zoom out
-" <use id=\"zoomminus\" xlink:href=\"#zoomMin\" x=\"42\" y=\"9\" onmousedown=\"handleZoom(evt,'out')\"/>\n"
-// reset zoom
-" <use id=\"reset\" xlink:href=\"#resetDef\" x=\"30\" y=\"36\" onmousedown=\"handleReset()\"/>\n"
-// arrow up
-" <g id=\"arrowUp\" xlink:href=\"#dirArrow\" transform=\"translate(30 24)\" onmousedown=\"handlePan(0,-1)\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowUp.mouseover\" end=\"arrowUp.mouseout\"/>\n"
-" </use>\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-// arrow right
-" <g id=\"arrowRight\" xlink:href=\"#dirArrow\" transform=\"rotate(90) translate(36 -43)\" onmousedown=\"handlePan(1,0)\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowRight.mouseover\" end=\"arrowRight.mouseout\"/>\n"
-" </use>\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-// arrow down
-" <g id=\"arrowDown\" xlink:href=\"#dirArrow\" transform=\"rotate(180) translate(-30 -48)\" onmousedown=\"handlePan(0,1)\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowDown.mouseover\" end=\"arrowDown.mouseout\"/>\n"
-" </use>\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-// arrow left
-" <g id=\"arrowLeft\" xlink:href=\"#dirArrow\" transform=\"rotate(270) translate(-36 17)\" onmousedown=\"handlePan(-1,0)\">\n"
-" <use xlink:href=\"#rim\" fill=\"#404040\">\n"
-" <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowLeft.mouseover\" end=\"arrowLeft.mouseout\"/>\n"
-" </use>\n"
-" <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
-" </g>\n"
-" </g>\n"
-// link to original SVG
-" <svg viewBox=\"0 0 15 15\" width=\"100%\" height=\"30px\" preserveAspectRatio=\"xMaxYMin meet\">\n"
-" <g id=\"arrow_out\" transform=\"scale(0.3 0.3)\">\n"
-" <a xlink:href=\"$orgname\" target=\"_base\">\n"
-" <rect id=\"button\" ry=\"5\" rx=\"5\" y=\"6\" x=\"6\" height=\"38\" width=\"38\"\n"
-" fill=\"#f2f5e9\" fill-opacity=\"0.5\" stroke=\"#606060\" stroke-width=\"1.0\"/>\n"
-" <path id=\"arrow\"\n"
-" d=\"M 11.500037,31.436501 C 11.940474,20.09759 22.043105,11.32322 32.158766,21.979434 L 37.068811,17.246167 C 37.068811,17.246167 37.088388,32 37.088388,32 L 22.160133,31.978069 C 22.160133,31.978069 26.997745,27.140456 26.997745,27.140456 C 18.528582,18.264221 13.291696,25.230495 11.500037,31.436501 z\"\n"
-" style=\"fill:#404040;\"/>\n"
-" </a>\n"
-" </g>\n"
-" </svg>\n"
-"</svg>\n"
-;
+static int DOT_NUM_THREADS; // will be initialized in initDot
//--------------------------------------------------------------------
-/*! mapping from protection levels to color names */
-static const char *normalEdgeColorMap[] =
-{
- "midnightblue", // Public
- "darkgreen", // Protected
- "firebrick4", // Private
- "darkorchid3", // "use" relation
- "grey75", // Undocumented
- "orange", // template relation
- "orange" // type constraint
-};
-
-static const char *normalArrowStyleMap[] =
-{
- "empty", // Public
- "empty", // Protected
- "empty", // Private
- "open", // "use" relation
- 0, // Undocumented
- 0 // template relation
-};
-
-static const char *normalEdgeStyleMap[] =
-{
- "solid", // inheritance
- "dashed" // usage
-};
-
-static const char *umlEdgeColorMap[] =
-{
- "midnightblue", // Public
- "darkgreen", // Protected
- "firebrick4", // Private
- "grey25", // "use" relation
- "grey75", // Undocumented
- "orange", // template relation
- "orange" // type constraint
-};
-
-static const char *umlArrowStyleMap[] =
-{
- "onormal", // Public
- "onormal", // Protected
- "onormal", // Private
- "odiamond", // "use" relation
- 0, // Undocumented
- 0 // template relation
-};
-
-static const char *umlEdgeStyleMap[] =
-{
- "solid", // inheritance
- "solid" // usage
-};
-
-/** Helper struct holding the properties of a edge in a dot graph. */
-struct EdgeProperties
-{
- const char * const *edgeColorMap;
- const char * const *arrowStyleMap;
- const char * const *edgeStyleMap;
-};
-
-static EdgeProperties normalEdgeProps =
-{
- normalEdgeColorMap, normalArrowStyleMap, normalEdgeStyleMap
-};
-
-static EdgeProperties umlEdgeProps =
+void initDot()
{
- umlEdgeColorMap, umlArrowStyleMap, umlEdgeStyleMap
-};
-
-
-static QCString convertLabel(const QCString &l);
-
-static QCString getDotFontName()
-{
- static QCString dotFontName = Config_getString(DOT_FONTNAME);
- if (dotFontName.isEmpty())
+ DotGraph::DOT_FONTNAME = Config_getString(DOT_FONTNAME);
+ if (DotGraph::DOT_FONTNAME.isEmpty())
{
- //dotFontName="FreeSans.ttf";
- dotFontName="Helvetica";
+ DotGraph::DOT_FONTNAME="Helvetica";
}
- return dotFontName;
-}
-static int getDotFontSize()
-{
- static int dotFontSize = Config_getInt(DOT_FONTSIZE);
- if (dotFontSize<4) dotFontSize=4;
- return dotFontSize;
-}
+ DotGraph::DOT_FONTSIZE = Config_getInt(DOT_FONTSIZE);
+ if (DotGraph::DOT_FONTSIZE<4) DotGraph::DOT_FONTSIZE=4;
-static void writeGraphHeader(FTextStream &t,const QCString &title=QCString())
-{
- static bool interactiveSVG = Config_getBool(INTERACTIVE_SVG);
- t << "digraph ";
- if (title.isEmpty())
- {
- t << "\"Dot Graph\"";
- }
- else
- {
- t << "\"" << convertLabel(title) << "\"";
- }
- t << endl << "{" << endl;
- if (interactiveSVG) // insert a comment to force regeneration when this
- // option is toggled
- {
- t << " // INTERACTIVE_SVG=YES\n";
- }
- t << " // LATEX_PDF_SIZE\n"; // write placeholder for LaTeX PDF bounding box size repacement
- if (Config_getBool(DOT_TRANSPARENT))
- {
- t << " bgcolor=\"transparent\";" << endl;
- }
- t << " edge [fontname=\"" << FONTNAME << "\","
- "fontsize=\"" << FONTSIZE << "\","
- "labelfontname=\"" << FONTNAME << "\","
- "labelfontsize=\"" << FONTSIZE << "\"];\n";
- t << " node [fontname=\"" << FONTNAME << "\","
- "fontsize=\"" << FONTSIZE << "\",shape=record];\n";
-}
+ DOT_NUM_THREADS = Config_getInt(DOT_NUM_THREADS);
+ if (DOT_NUM_THREADS > 32) DOT_NUM_THREADS = 32;
+ if (DOT_NUM_THREADS <= 0) DOT_NUM_THREADS = QMAX(2,QThread::idealThreadCount()+1);
-static void writeGraphFooter(FTextStream &t)
-{
- t << "}" << endl;
-}
+ // these are copied to be sure to be thread save
+ DotRunner::DOT_CLEANUP = Config_getBool(DOT_CLEANUP);
+ DotRunner::DOT_MULTI_TARGETS = Config_getBool(DOT_MULTI_TARGETS);
+ DotRunner::DOT_EXE.init(Config_getString(DOT_PATH) + "dot");
-static QCString replaceRef(const QCString &buf,const QCString relPath,
- bool urlOnly,const QCString &context,const QCString &target=QCString())
-{
- // search for href="...", store ... part in link
- QCString href = "href";
- //bool isXLink=FALSE;
- int len = 6;
- int indexS = buf.find("href=\""), indexE;
- bool setTarget = FALSE;
- if (indexS>5 && buf.find("xlink:href=\"")!=-1) // XLink href (for SVG)
- {
- indexS-=6;
- len+=6;
- href.prepend("xlink:");
- //isXLink=TRUE;
- }
- if (indexS>=0 && (indexE=buf.find('"',indexS+len))!=-1)
- {
- QCString link = buf.mid(indexS+len,indexE-indexS-len);
- QCString result;
- if (urlOnly) // for user defined dot graphs
- {
- if (link.left(5)=="\\ref " || link.left(5)=="@ref ") // \ref url
- {
- result=href+"=\"";
- // fake ref node to resolve the url
- DocRef *df = new DocRef( (DocNode*) 0, link.mid(5), context );
- result+=externalRef(relPath,df->ref(),TRUE);
- if (!df->file().isEmpty())
- result += df->file().data() + Doxygen::htmlFileExtension;
- if (!df->anchor().isEmpty())
- result += "#" + df->anchor();
- delete df;
- result += "\"";
- }
- else
- {
- result = href+"=\"" + link + "\"";
- }
- }
- else // ref$url (external ref via tag file), or $url (local ref)
- {
- int marker = link.find('$');
- if (marker!=-1)
- {
- QCString ref = link.left(marker);
- QCString url = link.mid(marker+1);
- if (!ref.isEmpty())
- {
- result = externalLinkTarget();
- if (result != "") setTarget = TRUE;
- }
- result+= href+"=\"";
- result+=externalRef(relPath,ref,TRUE);
- result+= url + "\"";
- }
- else // should not happen, but handle properly anyway
- {
- result = href+"=\"" + link + "\"";
- }
- }
- if (!target.isEmpty() && !setTarget)
- {
- result+=" target=\""+target+"\"";
- }
- QCString leftPart = buf.left(indexS);
- QCString rightPart = buf.mid(indexE+1);
- return leftPart + result + rightPart;
- }
- else
- {
- return buf;
- }
+ DotGraph::IMG_EXT = getDotImageExtension();
}
-/*! converts the rectangles in a client site image map into a stream
- * \param t the stream to which the result is written.
- * \param mapName the name of the map file.
- * \param relPath the relative path to the root of the output directory
- * (used in case CREATE_SUBDIRS is enabled).
- * \param urlOnly if FALSE the url field in the map contains an external
- * references followed by a $ and then the URL.
- * \param context the context (file, class, or namespace) in which the
- * map file was found
- * \returns TRUE if successful.
- */
-static bool convertMapFile(FTextStream &t,const char *mapName,
- const QCString relPath, bool urlOnly=FALSE,
- const QCString &context=QCString())
-{
- QFile f(mapName);
- if (!f.open(IO_ReadOnly))
- {
- err("problems opening map file %s for inclusion in the docs!\n"
- "If you installed Graphviz/dot after a previous failing run, \n"
- "try deleting the output directory and rerun doxygen.\n",mapName);
- return FALSE;
- }
- const int maxLineLen=10240;
- while (!f.atEnd()) // foreach line
- {
- QCString buf(maxLineLen);
- int numBytes = f.readLine(buf.rawData(),maxLineLen);
- if (numBytes>0)
- {
- buf.resize(numBytes+1);
-
- if (buf.left(5)=="<area")
- {
- QCString replBuf = replaceRef(buf,relPath,urlOnly,context);
- // strip id="..." from replBuf since the id's are not needed and not unique.
- int indexS = replBuf.find("id=\""), indexE;
- if (indexS>0 && (indexE=replBuf.find('"',indexS+4))!=-1)
- {
- t << replBuf.left(indexS-1) << replBuf.right(replBuf.length() - indexE - 1);
- }
- else
- {
- t << replBuf;
- }
- }
- }
- }
- return TRUE;
-}
static QCString g_dotFontPath;
@@ -450,147 +102,6 @@ static void unsetDotFontPath()
g_dotFontPath="";
}
-static bool resetPDFSize(const int width,const int height, const char *base)
-{
- QString tmpName = QString::fromUtf8(QCString(base)+".tmp");
- QString patchFile = QString::fromUtf8(QCString(base)+".dot");
- if (!QDir::current().rename(patchFile,tmpName))
- {
- err("Failed to rename file %s to %s!\n",patchFile.data(),tmpName.data());
- return FALSE;
- }
- QFile fi(tmpName);
- QFile fo(patchFile);
- if (!fi.open(IO_ReadOnly))
- {
- err("problem opening file %s for patching!\n",tmpName.data());
- QDir::current().rename(tmpName,patchFile);
- return FALSE;
- }
- if (!fo.open(IO_WriteOnly))
- {
- err("problem opening file %s for patching!\n",patchFile.data());
- QDir::current().rename(tmpName,patchFile);
- fi.close();
- return FALSE;
- }
- FTextStream t(&fo);
- const int maxLineLen=100*1024;
- while (!fi.atEnd()) // foreach line
- {
- QCString line(maxLineLen);
- int numBytes = fi.readLine(line.rawData(),maxLineLen);
- if (numBytes<=0)
- {
- break;
- }
- line.resize(numBytes+1);
- if (line.find("LATEX_PDF_SIZE") != -1)
- {
- double scale = (width > height ? width : height)/double(MAX_LATEX_GRAPH_INCH);
- t << " size=\""<<width/scale << "," <<height/scale <<"\";\n";
- }
- else
- t << line;
- }
- fi.close();
- fo.close();
- // remove temporary file
- QDir::current().remove(tmpName);
- return TRUE;
-}
-static bool readBoundingBox(const char *fileName,int *width,int *height,bool isEps)
-{
- QCString bb = isEps ? QCString("%%PageBoundingBox:") : QCString("/MediaBox [");
- QFile f(fileName);
- if (!f.open(IO_ReadOnly|IO_Raw))
- {
- //printf("readBoundingBox: could not open %s\n",fileName);
- return FALSE;
- }
- const int maxLineLen=1024;
- char buf[maxLineLen];
- while (!f.atEnd())
- {
- int numBytes = f.readLine(buf,maxLineLen-1); // read line
- if (numBytes>0)
- {
- buf[numBytes]='\0';
- const char *p = strstr(buf,bb);
- if (p) // found PageBoundingBox or /MediaBox string
- {
- int x,y;
- if (sscanf(p+bb.length(),"%d %d %d %d",&x,&y,width,height)!=4)
- {
- //printf("readBoundingBox sscanf fail\n");
- return FALSE;
- }
- return TRUE;
- }
- }
- else // read error!
- {
- //printf("Read error %d!\n",numBytes);
- return FALSE;
- }
- }
- err("Failed to extract bounding box from generated diagram file %s\n",fileName);
- return FALSE;
-}
-
-static bool writeVecGfxFigure(FTextStream &out,const QCString &baseName,
- const QCString &figureName)
-{
- int width=400,height=550;
- static bool usePdfLatex = Config_getBool(USE_PDFLATEX);
- if (usePdfLatex)
- {
- if (!readBoundingBox(figureName+".pdf",&width,&height,FALSE))
- {
- //printf("writeVecGfxFigure()=0\n");
- return FALSE;
- }
- }
- else
- {
- if (!readBoundingBox(figureName+".eps",&width,&height,TRUE))
- {
- //printf("writeVecGfxFigure()=0\n");
- return FALSE;
- }
- }
- //printf("Got PDF/EPS size %d,%d\n",width,height);
- int maxWidth = 350; /* approx. page width in points, excl. margins */
- int maxHeight = 550; /* approx. page height in points, excl. margins */
- out << "\\nopagebreak\n"
- "\\begin{figure}[H]\n"
- "\\begin{center}\n"
- "\\leavevmode\n";
- if (width>maxWidth || height>maxHeight) // figure too big for page
- {
- // c*width/maxWidth > c*height/maxHeight, where c=maxWidth*maxHeight>0
- if (width*maxHeight>height*maxWidth)
- {
- out << "\\includegraphics[width=" << maxWidth << "pt]";
- }
- else
- {
- out << "\\includegraphics[height=" << maxHeight << "pt]";
- }
- }
- else
- {
- out << "\\includegraphics[width=" << width << "pt]";
- }
-
- out << "{" << baseName << "}\n"
- "\\end{center}\n"
- "\\end{figure}\n";
-
- //printf("writeVecGfxFigure()=1\n");
- return TRUE;
-}
-
// extract size from a dot generated SVG file
static bool readSVGSize(const QCString &fileName,int *width,int *height)
{
@@ -638,8 +149,8 @@ static void writeSVGNotSupported(FTextStream &out)
// check if a reference to a SVG figure can be written and does so if possible.
// return FALSE if not possible (for instance because the SVG file is not yet generated).
-static bool writeSVGFigureLink(FTextStream &out,const QCString &relPath,
- const QCString &baseName,const QCString &absImgName)
+bool writeSVGFigureLink(FTextStream &out,const QCString &relPath,
+ const QCString &baseName,const QCString &absImgName)
{
int width=600,height=600;
if (!readSVGSize(absImgName,&width,&height))
@@ -680,593 +191,6 @@ static bool writeSVGFigureLink(FTextStream &out,const QCString &relPath,
return TRUE;
}
-// since dot silently reproduces the input file when it does not
-// support the PNG format, we need to check the result.
-static void checkDotResult(const char *imgExt, const char *imgName)
-{
- if (qstrcmp(imgExt,"png")==0)
- {
- FILE *f = portable_fopen(imgName,"rb");
- if (f)
- {
- char data[4];
- if (fread(data,1,4,f)==4)
- {
- if (!(data[1]=='P' && data[2]=='N' && data[3]=='G'))
- {
- err("Image `%s' produced by dot is not a valid PNG!\n"
- "You should either select a different format "
- "(DOT_IMAGE_FORMAT in the config file) or install a more "
- "recent version of graphviz (1.7+)\n",imgName
- );
- }
- }
- else
- {
- err("Could not read image `%s' generated by dot!\n",imgName);
- }
- fclose(f);
- }
- else
- {
- err("Could not open image `%s' generated by dot!\n",imgName);
- }
- }
-}
-
-static bool insertMapFile(FTextStream &out,const QCString &mapFile,
- const QCString &relPath,const QCString &mapLabel)
-{
- QFileInfo fi(mapFile);
- if (fi.exists() && fi.size()>0) // reuse existing map file
- {
- QGString tmpstr;
- FTextStream tmpout(&tmpstr);
- convertMapFile(tmpout,mapFile,relPath,FALSE);
- if (!tmpstr.isEmpty())
- {
- out << "<map name=\"" << mapLabel << "\" id=\"" << mapLabel << "\">" << endl;
- out << tmpstr;
- out << "</map>" << endl;
- }
- return TRUE;
- }
- return FALSE; // no map file yet, need to generate it
-}
-
-static void removeDotGraph(const QCString &dotName)
-{
- static bool dotCleanUp = Config_getBool(DOT_CLEANUP);
- if (dotCleanUp)
- {
- QDir d;
- d.remove(dotName);
- }
-}
-
-
-
-/*! Checks if a file "baseName".md5 exists. If so the contents
- * are compared with \a md5. If equal FALSE is returned. If the .md5
- * file does not exist or its contents are not equal to \a md5,
- * a new .md5 is generated with the \a md5 string as contents.
- */
-static bool checkAndUpdateMd5Signature(const QCString &baseName,
- const QCString &md5)
-{
- QFile f(baseName+".md5");
- if (f.open(IO_ReadOnly))
- {
- // read checksum
- QCString md5stored(33);
- int bytesRead=f.readBlock(md5stored.rawData(),32);
- md5stored[32]='\0';
- // compare checksum
- if (bytesRead==32 && md5==md5stored)
- {
- // bail out if equal
- return FALSE;
- }
- }
- f.close();
- // create checksum file
- if (f.open(IO_WriteOnly))
- {
- f.writeBlock(md5.data(),32);
- f.close();
- }
- return TRUE;
-}
-
-static bool checkDeliverables(const QCString &file1,
- const QCString &file2=QCString())
-{
- bool file1Ok = TRUE;
- bool file2Ok = TRUE;
- if (!file1.isEmpty())
- {
- QFileInfo fi(file1);
- file1Ok = (fi.exists() && fi.size()>0);
- }
- if (!file2.isEmpty())
- {
- QFileInfo fi(file2);
- file2Ok = (fi.exists() && fi.size()>0);
- }
- return file1Ok && file2Ok;
-}
-
-//--------------------------------------------------------------------
-
-inline int DotNode::findParent( DotNode *n )
-{
- if ( !m_parents ) return -1;
- return m_parents->find(n);
-}
-
-//--------------------------------------------------------------------
-
-int DotNodeList::compareValues(const DotNode *n1,const DotNode *n2) const
-{
- return qstricmp(n1->m_label,n2->m_label);
-}
-
-//--------------------------------------------------------------------
-
-DotRunner::DotRunner(const QCString &file,const QCString &path,
- bool checkResult,const QCString &imageName)
- : m_dotExe(Config_getString(DOT_PATH)+"dot"),
- m_file(file), m_path(path),
- m_checkResult(checkResult), m_imageName(imageName),
- m_imgExt(getDotImageExtension())
-{
- static bool dotCleanUp = Config_getBool(DOT_CLEANUP);
- static bool dotMultiTargets = Config_getBool(DOT_MULTI_TARGETS);
- m_cleanUp = dotCleanUp;
- m_multiTargets = dotMultiTargets;
- m_jobs.setAutoDelete(TRUE);
-}
-
-void DotRunner::addJob(const char *format,const char *output, const char *base)
-{
- QCString args = QCString("-T")+format+" -o \""+output+"\"";
- m_jobs.append(new DotConstString(args, base));
-}
-
-void DotRunner::addPostProcessing(const char *cmd,const char *args)
-{
- m_postCmd.set(cmd);
- m_postArgs.set(args);
-}
-
-bool DotRunner::run()
-{
- int exitCode=0;
- int width=0,height=0;
-
- QCString dotArgs;
- QListIterator<DotConstString> li(m_jobs);
- DotConstString *s;
- if (m_multiTargets)
- {
- dotArgs=QCString("\"")+m_file.data()+"\"";
- for (li.toFirst();(s=li.current());++li)
- {
- dotArgs+=' ';
- dotArgs+=s->data();
- }
- if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0) goto error;
- dotArgs=QCString("\"")+m_file.data()+"\"";
- bool redo = FALSE;
- for (li.toFirst();(s=li.current());++li)
- {
- if (s->pdfData())
- {
- if (!readBoundingBox(QCString(s->pdfData())+".pdf",&width,&height,FALSE)) goto error;
- if ((width > MAX_LATEX_GRAPH_SIZE) || (height > MAX_LATEX_GRAPH_SIZE))
- {
- if (!resetPDFSize(width,height,s->pdfData())) goto error;
- dotArgs+=' ';
- dotArgs+=s->data();
- redo = TRUE;
- }
- }
- }
- if (redo)
- {
- if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0) goto error;
- }
- }
- else
- {
- for (li.toFirst();(s=li.current());++li)
- {
- dotArgs=QCString("\"")+m_file.data()+"\" "+s->data();
- if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0) goto error;
- if (s->pdfData())
- {
- if (!readBoundingBox(QCString(s->pdfData())+".pdf",&width,&height,FALSE)) goto error;
- if ((width > MAX_LATEX_GRAPH_SIZE) || (height > MAX_LATEX_GRAPH_SIZE))
- {
- if (!resetPDFSize(width,height,s->pdfData())) goto error;
- if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0) goto error;
- }
- }
- }
- }
- if (!m_postCmd.isEmpty() && portable_system(m_postCmd.data(),m_postArgs.data())!=0)
- {
- err("Problems running '%s' as a post-processing step for dot output\n",m_postCmd.data());
- return FALSE;
- }
- if (m_checkResult)
- {
- checkDotResult(m_imgExt.data(),m_imageName.data());
- }
- if (m_cleanUp)
- {
- //printf("removing dot file %s\n",m_file.data());
- //QDir(path).remove(file);
- m_cleanupItem.file.set(m_file.data());
- m_cleanupItem.path.set(m_path.data());
- }
- return TRUE;
-error:
- err("Problems running dot: exit code=%d, command='%s', arguments='%s'\n",
- exitCode,m_dotExe.data(),dotArgs.data());
- return FALSE;
-}
-
-//--------------------------------------------------------------------
-
-DotFilePatcher::DotFilePatcher(const char *patchFile)
- : m_patchFile(patchFile)
-{
- m_maps.setAutoDelete(TRUE);
-}
-
-QCString DotFilePatcher::file() const
-{
- return m_patchFile;
-}
-
-int DotFilePatcher::addMap(const QCString &mapFile,const QCString &relPath,
- bool urlOnly,const QCString &context,const QCString &label)
-{
- int id = m_maps.count();
- Map *map = new Map;
- map->mapFile = mapFile;
- map->relPath = relPath;
- map->urlOnly = urlOnly;
- map->context = context;
- map->label = label;
- map->zoomable = FALSE;
- map->graphId = -1;
- m_maps.append(map);
- return id;
-}
-
-int DotFilePatcher::addFigure(const QCString &baseName,
- const QCString &figureName,bool heightCheck)
-{
- int id = m_maps.count();
- Map *map = new Map;
- map->mapFile = figureName;
- map->urlOnly = heightCheck;
- map->label = baseName;
- map->zoomable = FALSE;
- map->graphId = -1;
- m_maps.append(map);
- return id;
-}
-
-int DotFilePatcher::addSVGConversion(const QCString &relPath,bool urlOnly,
- const QCString &context,bool zoomable,
- int graphId)
-{
- int id = m_maps.count();
- Map *map = new Map;
- map->relPath = relPath;
- map->urlOnly = urlOnly;
- map->context = context;
- map->zoomable = zoomable;
- map->graphId = graphId;
- m_maps.append(map);
- return id;
-}
-
-int DotFilePatcher::addSVGObject(const QCString &baseName,
- const QCString &absImgName,
- const QCString &relPath)
-{
- int id = m_maps.count();
- Map *map = new Map;
- map->mapFile = absImgName;
- map->relPath = relPath;
- map->label = baseName;
- map->zoomable = FALSE;
- map->graphId = -1;
- m_maps.append(map);
- return id;
-}
-
-bool DotFilePatcher::run()
-{
- //printf("DotFilePatcher::run(): %s\n",m_patchFile.data());
- static bool interactiveSVG = Config_getBool(INTERACTIVE_SVG);
- bool isSVGFile = m_patchFile.right(4)==".svg";
- int graphId = -1;
- QCString relPath;
- if (isSVGFile)
- {
- Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
- interactiveSVG = interactiveSVG && map->zoomable;
- graphId = map->graphId;
- relPath = map->relPath;
- //printf("DotFilePatcher::addSVGConversion: file=%s zoomable=%d\n",
- // m_patchFile.data(),map->zoomable);
- }
- QString tmpName = QString::fromUtf8(m_patchFile+".tmp");
- QString patchFile = QString::fromUtf8(m_patchFile);
- if (!QDir::current().rename(patchFile,tmpName))
- {
- err("Failed to rename file %s to %s!\n",m_patchFile.data(),tmpName.data());
- return FALSE;
- }
- QFile fi(tmpName);
- QFile fo(patchFile);
- if (!fi.open(IO_ReadOnly))
- {
- err("problem opening file %s for patching!\n",tmpName.data());
- QDir::current().rename(tmpName,patchFile);
- return FALSE;
- }
- if (!fo.open(IO_WriteOnly))
- {
- err("problem opening file %s for patching!\n",m_patchFile.data());
- QDir::current().rename(tmpName,patchFile);
- return FALSE;
- }
- FTextStream t(&fo);
- const int maxLineLen=100*1024;
- int lineNr=1;
- int width,height;
- bool insideHeader=FALSE;
- bool replacedHeader=FALSE;
- bool foundSize=FALSE;
- while (!fi.atEnd()) // foreach line
- {
- QCString line(maxLineLen);
- int numBytes = fi.readLine(line.rawData(),maxLineLen);
- if (numBytes<=0)
- {
- break;
- }
- line.resize(numBytes+1);
-
- //printf("line=[%s]\n",line.stripWhiteSpace().data());
- int i;
- ASSERT(numBytes<maxLineLen);
- if (isSVGFile)
- {
- if (interactiveSVG)
- {
- if (line.find("<svg")!=-1 && !replacedHeader)
- {
- int count;
- count = sscanf(line.data(),"<svg width=\"%dpt\" height=\"%dpt\"",&width,&height);
- //printf("width=%d height=%d\n",width,height);
- foundSize = count==2 && (width>500 || height>450);
- if (foundSize) insideHeader=TRUE;
- }
- else if (insideHeader && !replacedHeader && line.find("<title>")!=-1)
- {
- if (foundSize)
- {
- // insert special replacement header for interactive SVGs
- t << "<!--zoomable " << height << " -->\n";
- t << svgZoomHeader;
- t << "var viewWidth = " << width << ";\n";
- t << "var viewHeight = " << height << ";\n";
- if (graphId>=0)
- {
- t << "var sectionId = 'dynsection-" << graphId << "';\n";
- }
- t << "</script>\n";
- t << "<script xlink:href=\"" << relPath << "svgpan.js\"/>\n";
- t << "<svg id=\"graph\" class=\"graph\">\n";
- t << "<g id=\"viewport\">\n";
- }
- insideHeader=FALSE;
- replacedHeader=TRUE;
- }
- }
- if (!insideHeader || !foundSize) // copy SVG and replace refs,
- // unless we are inside the header of the SVG.
- // Then we replace it with another header.
- {
- Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
- t << replaceRef(line,map->relPath,map->urlOnly,map->context,"_top");
- }
- }
- else if ((i=line.find("<!-- SVG"))!=-1 || (i=line.find("[!-- SVG"))!=-1)
- {
- //printf("Found marker at %d\n",i);
- int mapId=-1;
- t << line.left(i);
- int n = sscanf(line.data()+i+1,"!-- SVG %d",&mapId);
- if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
- {
- int e = QMAX(line.find("--]"),line.find("-->"));
- Map *map = m_maps.at(mapId);
- //printf("DotFilePatcher::writeSVGFigure: file=%s zoomable=%d\n",
- // m_patchFile.data(),map->zoomable);
- if (!writeSVGFigureLink(t,map->relPath,map->label,map->mapFile))
- {
- err("Problem extracting size from SVG file %s\n",map->mapFile.data());
- }
- if (e!=-1) t << line.mid(e+3);
- }
- else // error invalid map id!
- {
- err("Found invalid SVG id in file %s!\n",m_patchFile.data());
- t << line.mid(i);
- }
- }
- else if ((i=line.find("<!-- MAP"))!=-1)
- {
- int mapId=-1;
- t << line.left(i);
- int n = sscanf(line.data()+i,"<!-- MAP %d",&mapId);
- if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
- {
- QGString result;
- FTextStream tt(&result);
- Map *map = m_maps.at(mapId);
- //printf("patching MAP %d in file %s with contents of %s\n",
- // mapId,m_patchFile.data(),map->mapFile.data());
- convertMapFile(tt,map->mapFile,map->relPath,map->urlOnly,map->context);
- if (!result.isEmpty())
- {
- t << "<map name=\"" << map->label << "\" id=\"" << map->label << "\">" << endl;
- t << result;
- t << "</map>" << endl;
- }
- }
- else // error invalid map id!
- {
- err("Found invalid MAP id in file %s!\n",m_patchFile.data());
- t << line.mid(i);
- }
- }
- else if ((i=line.find("% FIG"))!=-1)
- {
- int mapId=-1;
- int n = sscanf(line.data()+i+2,"FIG %d",&mapId);
- //printf("line='%s' n=%d\n",line.data()+i,n);
- if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
- {
- Map *map = m_maps.at(mapId);
- //printf("patching FIG %d in file %s with contents of %s\n",
- // mapId,m_patchFile.data(),map->mapFile.data());
- if (!writeVecGfxFigure(t,map->label,map->mapFile))
- {
- err("problem writing FIG %d figure!\n",mapId);
- return FALSE;
- }
- }
- else // error invalid map id!
- {
- err("Found invalid bounding FIG %d in file %s!\n",mapId,m_patchFile.data());
- t << line;
- }
- }
- else
- {
- t << line;
- }
- lineNr++;
- }
- fi.close();
- if (isSVGFile && interactiveSVG && replacedHeader)
- {
- QCString orgName=m_patchFile.left(m_patchFile.length()-4)+"_org.svg";
- t << substitute(svgZoomFooter,"$orgname",stripPath(orgName));
- fo.close();
- // keep original SVG file so we can refer to it, we do need to replace
- // dummy link by real ones
- QFile fi(tmpName);
- QFile fo(orgName);
- if (!fi.open(IO_ReadOnly))
- {
- err("problem opening file %s for reading!\n",tmpName.data());
- return FALSE;
- }
- if (!fo.open(IO_WriteOnly))
- {
- err("problem opening file %s for writing!\n",orgName.data());
- return FALSE;
- }
- FTextStream t(&fo);
- while (!fi.atEnd()) // foreach line
- {
- QCString line(maxLineLen);
- int numBytes = fi.readLine(line.rawData(),maxLineLen);
- if (numBytes<=0)
- {
- break;
- }
- line.resize(numBytes+1);
- Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
- t << replaceRef(line,map->relPath,map->urlOnly,map->context,"_top");
- }
- fi.close();
- fo.close();
- }
- // remove temporary file
- QDir::current().remove(tmpName);
- return TRUE;
-}
-
-//--------------------------------------------------------------------
-
-void DotRunnerQueue::enqueue(DotRunner *runner)
-{
- QMutexLocker locker(&m_mutex);
- m_queue.enqueue(runner);
- m_bufferNotEmpty.wakeAll();
-}
-
-DotRunner *DotRunnerQueue::dequeue()
-{
- QMutexLocker locker(&m_mutex);
- while (m_queue.isEmpty())
- {
- // wait until something is added to the queue
- m_bufferNotEmpty.wait(&m_mutex);
- }
- DotRunner *result = m_queue.dequeue();
- return result;
-}
-
-uint DotRunnerQueue::count() const
-{
- QMutexLocker locker(&m_mutex);
- return m_queue.count();
-}
-
-//--------------------------------------------------------------------
-
-DotWorkerThread::DotWorkerThread(DotRunnerQueue *queue)
- : m_queue(queue)
-{
- m_cleanupItems.setAutoDelete(TRUE);
-}
-
-void DotWorkerThread::run()
-{
- DotRunner *runner;
- while ((runner=m_queue->dequeue()))
- {
- runner->run();
- const DotRunner::CleanupItem &cleanup = runner->cleanup();
- if (!cleanup.file.isEmpty())
- {
- m_cleanupItems.append(new DotRunner::CleanupItem(cleanup));
- }
- }
-}
-
-void DotWorkerThread::cleanup()
-{
- QListIterator<DotRunner::CleanupItem> it(m_cleanupItems);
- DotRunner::CleanupItem *ci;
- for (;(ci=it.current());++it)
- {
- QDir(ci->path.data()).remove(ci->file.data());
- }
-}
-
//--------------------------------------------------------------------
DotManager *DotManager::m_theInstance = 0;
@@ -1282,15 +206,13 @@ DotManager *DotManager::instance()
DotManager::DotManager() : m_dotMaps(1009)
{
- m_dotRuns.setAutoDelete(TRUE);
+ m_runners.setAutoDelete(TRUE);
m_dotMaps.setAutoDelete(TRUE);
m_queue = new DotRunnerQueue;
int i;
- int numThreads = QMIN(32,Config_getInt(DOT_NUM_THREADS));
- if (numThreads!=1)
+ if (DOT_NUM_THREADS!=1)
{
- if (numThreads==0) numThreads = QMAX(2,QThread::idealThreadCount()+1);
- for (i=0;i<numThreads;i++)
+ for (i=0;i<DOT_NUM_THREADS;i++)
{
DotWorkerThread *thread = new DotWorkerThread(m_queue);
thread->start();
@@ -1312,11 +234,26 @@ DotManager::~DotManager()
delete m_queue;
}
-void DotManager::addRun(DotRunner *run)
+DotRunner* DotManager::createRunner(const QCString& absDotName, const QCString& md5Hash)
{
- m_dotRuns.append(run);
+ DotRunner * run = m_runners.find(absDotName);
+ if (run == 0)
+ {
+ run = new DotRunner(absDotName, md5Hash);
+ m_runners.insert(absDotName, run);
+ }
+ else
+ {
+ // we have a match
+ if (md5Hash != QCString(run->getMd5Hash().data()))
+ {
+ err("md5 hash does not match for two different runs of %s !\n", absDotName.data());
+ }
+ }
+ return run;
}
+
int DotManager::addMap(const QCString &file,const QCString &mapFile,
const QCString &relPath,bool urlOnly,const QCString &context,
const QCString &label)
@@ -1369,7 +306,7 @@ int DotManager::addSVGObject(const QCString &file,const QCString &baseName,
bool DotManager::run()
{
- uint numDotRuns = m_dotRuns.count();
+ uint numDotRuns = m_runners.count();
uint numDotMaps = m_dotMaps.count();
if (numDotRuns+numDotMaps>1)
{
@@ -1383,7 +320,7 @@ bool DotManager::run()
}
}
int i=1;
- QListIterator<DotRunner> li(m_dotRuns);
+ QDictIterator<DotRunner> li(m_runners);
bool setPath=FALSE;
if (Config_getBool(GENERATE_HTML))
@@ -1451,11 +388,6 @@ bool DotManager::run()
{
m_workers.at(i)->wait();
}
- // clean up dot files from main thread
- for (i=0;i<(int)m_workers.count();i++)
- {
- m_workers.at(i)->cleanup();
- }
}
portable_sysTimerStop();
if (setPath)
@@ -1494,2736 +426,59 @@ bool DotManager::run()
//--------------------------------------------------------------------
-
-/*! helper function that deletes all nodes in a connected graph, given
- * one of the graph's nodes
- */
-static void deleteNodes(DotNode *node,SDict<DotNode> *skipNodes=0)
-{
- //printf("deleteNodes skipNodes=%p\n",skipNodes);
- static DotNodeList deletedNodes;
- deletedNodes.setAutoDelete(TRUE);
- node->deleteNode(deletedNodes,skipNodes); // collect nodes to be deleted.
- deletedNodes.clear(); // actually remove the nodes.
-}
-
-DotNode::DotNode(int n,const char *lab,const char *tip, const char *url,
- bool isRoot,const ClassDef *cd)
- : m_subgraphId(-1)
- , m_number(n)
- , m_label(lab)
- , m_tooltip(tip)
- , m_url(url)
- , m_parents(0)
- , m_children(0)
- , m_edgeInfo(0)
- , m_deleted(FALSE)
- , m_written(FALSE)
- , m_hasDoc(FALSE)
- , m_isRoot(isRoot)
- , m_classDef(cd)
- , m_visible(FALSE)
- , m_truncated(Unknown)
- , m_distance(1000)
- , m_renumbered(false)
-{
-}
-
-DotNode::~DotNode()
-{
- delete m_children;
- delete m_parents;
- delete m_edgeInfo;
-}
-
-void DotNode::addChild(DotNode *n,
- int edgeColor,
- int edgeStyle,
- const char *edgeLab,
- const char *edgeURL,
- int edgeLabCol
- )
-{
- if (m_children==0)
- {
- m_children = new QList<DotNode>;
- m_edgeInfo = new QList<EdgeInfo>;
- m_edgeInfo->setAutoDelete(TRUE);
- }
- m_children->append(n);
- EdgeInfo *ei = new EdgeInfo;
- ei->m_color = edgeColor;
- ei->m_style = edgeStyle;
- ei->m_label = edgeLab;
- ei->m_url = edgeURL;
- if (edgeLabCol==-1)
- ei->m_labColor=edgeColor;
- else
- ei->m_labColor=edgeLabCol;
- m_edgeInfo->append(ei);
-}
-
-void DotNode::addParent(DotNode *n)
-{
- if (m_parents==0)
- {
- m_parents = new QList<DotNode>;
- }
- m_parents->append(n);
-}
-
-void DotNode::removeChild(DotNode *n)
-{
- if (m_children) m_children->remove(n);
-}
-
-void DotNode::removeParent(DotNode *n)
-{
- if (m_parents) m_parents->remove(n);
-}
-
-void DotNode::deleteNode(DotNodeList &deletedList,SDict<DotNode> *skipNodes)
-{
- if (m_deleted) return; // avoid recursive loops in case the graph has cycles
- m_deleted=TRUE;
- if (m_parents!=0) // delete all parent nodes of this node
- {
- QListIterator<DotNode> dnlip(*m_parents);
- DotNode *pn;
- for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
- {
- //pn->removeChild(this);
- pn->deleteNode(deletedList,skipNodes);
- }
- }
- if (m_children!=0) // delete all child nodes of this node
- {
- QListIterator<DotNode> dnlic(*m_children);
- DotNode *cn;
- for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
- {
- //cn->removeParent(this);
- cn->deleteNode(deletedList,skipNodes);
- }
- }
- // add this node to the list of deleted nodes.
- //printf("skipNodes=%p find(%p)=%p\n",skipNodes,this,skipNodes ? skipNodes->find((int)this) : 0);
- if (skipNodes==0 || skipNodes->find((char*)this)==0)
- {
- //printf("deleting\n");
- deletedList.append(this);
- }
-}
-
-void DotNode::setDistance(int distance)
-{
- if (distance<m_distance) m_distance = distance;
-}
-
-static QCString convertLabel(const QCString &l)
-{
- QString bBefore("\\_/<({[: =-+@%#~?$"); // break before character set
- QString bAfter(">]),:;|"); // break after character set
- QString p(l);
- if (p.isEmpty()) return QCString();
- QString result;
- QChar c,pc=0;
- uint idx = 0;
- int len=p.length();
- int charsLeft=len;
- int sinceLast=0;
- int foldLen=17; // ideal text length
- while (idx < p.length())
- {
- c = p[idx++];
- QString replacement;
- switch(c)
- {
- case '\\': replacement="\\\\"; break;
- case '\n': replacement="\\n"; break;
- case '<': replacement="\\<"; break;
- case '>': replacement="\\>"; break;
- case '|': replacement="\\|"; break;
- case '{': replacement="\\{"; break;
- case '}': replacement="\\}"; break;
- case '"': replacement="\\\""; break;
- default: replacement=c; break;
- }
- // Some heuristics to insert newlines to prevent too long
- // boxes and at the same time prevent ugly breaks
- if (c=='\n')
- {
- result+=replacement;
- foldLen = (3*foldLen+sinceLast+2)/4;
- sinceLast=1;
- }
- else if ((pc!=':' || c!=':') && charsLeft>foldLen/3 && sinceLast>foldLen && bBefore.contains(c))
- {
- result+="\\l";
- result+=replacement;
- foldLen = (foldLen+sinceLast+1)/2;
- sinceLast=1;
- }
- else if (charsLeft>1+foldLen/4 && sinceLast>foldLen+foldLen/3 &&
- !isupper(c) && p[idx].category()==QChar::Letter_Uppercase)
- {
- result+=replacement;
- result+="\\l";
- foldLen = (foldLen+sinceLast+1)/2;
- sinceLast=0;
- }
- else if (charsLeft>foldLen/3 && sinceLast>foldLen && bAfter.contains(c) && (c!=':' || p[idx]!=':'))
- {
- result+=replacement;
- result+="\\l";
- foldLen = (foldLen+sinceLast+1)/2;
- sinceLast=0;
- }
- else
- {
- result+=replacement;
- sinceLast++;
- }
- charsLeft--;
- pc=c;
- }
- return result.utf8();
-}
-
-static QCString escapeTooltip(const QCString &tooltip)
-{
- QCString result;
- const char *p=tooltip.data();
- if (p==0) return result;
- char c;
- while ((c=*p++))
- {
- switch(c)
- {
- case '"': result+="\\\""; break;
- case '\\': result+="\\\\"; break;
- default: result+=c; break;
- }
- }
- return result;
-}
-
-static void writeBoxMemberList(FTextStream &t,
- char prot,MemberList *ml,const ClassDef *scope,
- bool isStatic=FALSE,const QDict<void> *skipNames=0)
-{
- (void)isStatic;
- if (ml)
- {
- MemberListIterator mlia(*ml);
- MemberDef *mma;
- int totalCount=0;
- for (mlia.toFirst();(mma = mlia.current());++mlia)
- {
- if (mma->getClassDef()==scope &&
- (skipNames==0 || skipNames->find(mma->name())==0))
- {
- totalCount++;
- }
- }
-
- int count=0;
- for (mlia.toFirst();(mma = mlia.current());++mlia)
- {
- if (mma->getClassDef() == scope &&
- (skipNames==0 || skipNames->find(mma->name())==0))
- {
- static int limit = Config_getInt(UML_LIMIT_NUM_FIELDS);
- if (limit>0 && (totalCount>limit*3/2 && count>=limit))
- {
- t << theTranslator->trAndMore(QCString().sprintf("%d",totalCount-count)) << "\\l";
- break;
- }
- else
- {
- t << prot << " ";
- t << convertLabel(mma->name());
- if (!mma->isObjCMethod() &&
- (mma->isFunction() || mma->isSlot() || mma->isSignal())) t << "()";
- t << "\\l";
- count++;
- }
- }
- }
- // write member groups within the memberlist
- MemberGroupList *mgl = ml->getMemberGroupList();
- if (mgl)
- {
- MemberGroupListIterator mgli(*mgl);
- MemberGroup *mg;
- for (mgli.toFirst();(mg=mgli.current());++mgli)
- {
- if (mg->members())
- {
- writeBoxMemberList(t,prot,mg->members(),scope,isStatic,skipNames);
- }
- }
- }
- }
-}
-
-static QCString stripProtectionPrefix(const QCString &s)
-{
- if (!s.isEmpty() && (s[0]=='-' || s[0]=='+' || s[0]=='~' || s[0]=='#'))
- {
- return s.mid(1);
- }
- else
- {
- return s;
- }
-}
-
-void DotNode::writeBox(FTextStream &t,
- GraphType gt,
- GraphOutputFormat /*format*/,
- bool hasNonReachableChildren
- )
-{
- const char *labCol =
- m_url.isEmpty() ? "grey75" : // non link
- (
- (hasNonReachableChildren) ? "red" : "black"
- );
- t << " Node" << m_number << " [label=\"";
- static bool umlLook = Config_getBool(UML_LOOK);
-
- if (m_classDef && umlLook && (gt==Inheritance || gt==Collaboration))
- {
- // add names shown as relations to a dictionary, so we don't show
- // them as attributes as well
- QDict<void> arrowNames(17);
- if (m_edgeInfo)
- {
- // for each edge
- QListIterator<EdgeInfo> li(*m_edgeInfo);
- EdgeInfo *ei;
- for (li.toFirst();(ei=li.current());++li)
- {
- if (!ei->m_label.isEmpty()) // labels joined by \n
- {
- int li=ei->m_label.find('\n');
- int p=0;
- QCString lab;
- while ((li=ei->m_label.find('\n',p))!=-1)
- {
- lab = stripProtectionPrefix(ei->m_label.mid(p,li-p));
- arrowNames.insert(lab,(void*)0x8);
- p=li+1;
- }
- lab = stripProtectionPrefix(ei->m_label.right(ei->m_label.length()-p));
- arrowNames.insert(lab,(void*)0x8);
- }
- }
- }
-
- //printf("DotNode::writeBox for %s\n",m_classDef->name().data());
- static bool extractPrivate = Config_getBool(EXTRACT_PRIVATE);
- t << "{" << convertLabel(m_label);
- t << "\\n|";
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubAttribs),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubStaticAttribs),m_classDef,TRUE,&arrowNames);
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_properties),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacAttribs),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacStaticAttribs),m_classDef,TRUE,&arrowNames);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proAttribs),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proStaticAttribs),m_classDef,TRUE,&arrowNames);
- if (extractPrivate)
- {
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priAttribs),m_classDef,FALSE,&arrowNames);
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priStaticAttribs),m_classDef,TRUE,&arrowNames);
- }
- t << "|";
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubMethods),m_classDef);
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubStaticMethods),m_classDef,TRUE);
- writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType_pubSlots),m_classDef);
- writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacMethods),m_classDef);
- writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType_pacStaticMethods),m_classDef,TRUE);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proMethods),m_classDef);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proStaticMethods),m_classDef,TRUE);
- writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType_proSlots),m_classDef);
- if (extractPrivate)
- {
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priMethods),m_classDef);
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priStaticMethods),m_classDef,TRUE);
- writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType_priSlots),m_classDef);
- }
- if (m_classDef->getLanguage()!=SrcLangExt_Fortran &&
- m_classDef->getMemberGroupSDict())
- {
- MemberGroupSDict::Iterator mgdi(*m_classDef->getMemberGroupSDict());
- MemberGroup *mg;
- for (mgdi.toFirst();(mg=mgdi.current());++mgdi)
- {
- if (mg->members())
- {
- writeBoxMemberList(t,'*',mg->members(),m_classDef,FALSE,&arrowNames);
- }
- }
- }
- t << "}";
- }
- else // standard look
- {
- t << convertLabel(m_label);
- }
- t << "\",height=0.2,width=0.4";
- if (m_isRoot)
- {
- t << ",color=\"black\", fillcolor=\"grey75\", style=\"filled\", fontcolor=\"black\"";
- }
- else
- {
- static bool dotTransparent = Config_getBool(DOT_TRANSPARENT);
- if (!dotTransparent)
- {
- t << ",color=\"" << labCol << "\", fillcolor=\"";
- t << "white";
- t << "\", style=\"filled\"";
- }
- else
- {
- t << ",color=\"" << labCol << "\"";
- }
- if (!m_url.isEmpty())
- {
- int anchorPos = m_url.findRev('#');
- if (anchorPos==-1)
- {
- t << ",URL=\"" << m_url << Doxygen::htmlFileExtension << "\"";
- }
- else
- {
- t << ",URL=\"" << m_url.left(anchorPos) << Doxygen::htmlFileExtension
- << m_url.right(m_url.length()-anchorPos) << "\"";
- }
- }
- }
- if (!m_tooltip.isEmpty())
- {
- t << ",tooltip=\"" << escapeTooltip(m_tooltip) << "\"";
- }
- else
- {
- t << ",tooltip=\" \""; // space in tooltip is required otherwise still something like 'Node0' is used
- }
- t << "];" << endl;
-}
-
-void DotNode::writeArrow(FTextStream &t,
- GraphType gt,
- GraphOutputFormat format,
- DotNode *cn,
- EdgeInfo *ei,
- bool topDown,
- bool pointBack
- )
-{
- t << " Node";
- if (topDown)
- t << cn->number();
- else
- t << m_number;
- t << " -> Node";
- if (topDown)
- t << m_number;
- else
- t << cn->number();
- t << " [";
-
- static bool umlLook = Config_getBool(UML_LOOK);
- const EdgeProperties *eProps = umlLook ? &umlEdgeProps : &normalEdgeProps;
- QCString aStyle = eProps->arrowStyleMap[ei->m_color];
- bool umlUseArrow = aStyle=="odiamond";
-
- if (pointBack && !umlUseArrow) t << "dir=\"back\",";
- t << "color=\"" << eProps->edgeColorMap[ei->m_color]
- << "\",fontsize=\"" << FONTSIZE << "\",";
- t << "style=\"" << eProps->edgeStyleMap[ei->m_style] << "\"";
- if (!ei->m_label.isEmpty())
- {
- t << ",label=\" " << convertLabel(ei->m_label) << "\" ";
- }
- if (umlLook &&
- eProps->arrowStyleMap[ei->m_color] &&
- (gt==Inheritance || gt==Collaboration)
- )
- {
- bool rev = pointBack;
- if (umlUseArrow) rev=!rev; // UML use relates has arrow on the start side
- if (rev)
- t << ",arrowtail=\"" << eProps->arrowStyleMap[ei->m_color] << "\"";
- else
- t << ",arrowhead=\"" << eProps->arrowStyleMap[ei->m_color] << "\"";
- }
-
- if (format==GOF_BITMAP) t << ",fontname=\"" << FONTNAME << "\"";
- t << "];" << endl;
-}
-
-void DotNode::write(FTextStream &t,
- GraphType gt,
- GraphOutputFormat format,
- bool topDown,
- bool toChildren,
- bool backArrows
- )
-{
- //printf("DotNode::write(%d) name=%s this=%p written=%d visible=%d\n",m_distance,m_label.data(),this,m_written,m_visible);
- if (m_written) return; // node already written to the output
- if (!m_visible) return; // node is not visible
- writeBox(t,gt,format,m_truncated==Truncated);
- m_written=TRUE;
- QList<DotNode> *nl = toChildren ? m_children : m_parents;
- if (nl)
- {
- if (toChildren)
- {
- QListIterator<DotNode> dnli1(*nl);
- QListIterator<EdgeInfo> dnli2(*m_edgeInfo);
- DotNode *cn;
- for (dnli1.toFirst();(cn=dnli1.current());++dnli1,++dnli2)
- {
- if (cn->isVisible())
- {
- //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",cn->label().data());
- writeArrow(t,gt,format,cn,dnli2.current(),topDown,backArrows);
- }
- cn->write(t,gt,format,topDown,toChildren,backArrows);
- }
- }
- else // render parents
- {
- QListIterator<DotNode> dnli(*nl);
- DotNode *pn;
- for (dnli.toFirst();(pn=dnli.current());++dnli)
- {
- if (pn->isVisible())
- {
- //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",pn->label().data());
- writeArrow(t,
- gt,
- format,
- pn,
- pn->m_edgeInfo->at(pn->m_children->findRef(this)),
- FALSE,
- backArrows
- );
- }
- pn->write(t,gt,format,TRUE,FALSE,backArrows);
- }
- }
- }
- //printf("end DotNode::write(%d) name=%s\n",distance,m_label.data());
-}
-
-void DotNode::writeXML(FTextStream &t,bool isClassGraph)
-{
- t << " <node id=\"" << m_number << "\">" << endl;
- t << " <label>" << convertToXML(m_label) << "</label>" << endl;
- if (!m_url.isEmpty())
- {
- QCString url(m_url);
- const char *refPtr = url.data();
- char *urlPtr = strchr(url.rawData(),'$');
- if (urlPtr)
- {
- *urlPtr++='\0';
- t << " <link refid=\"" << convertToXML(urlPtr) << "\"";
- if (*refPtr!='\0')
- {
- t << " external=\"" << convertToXML(refPtr) << "\"";
- }
- t << "/>" << endl;
- }
- }
- if (m_children)
- {
- QListIterator<DotNode> nli(*m_children);
- QListIterator<EdgeInfo> eli(*m_edgeInfo);
- DotNode *childNode;
- EdgeInfo *edgeInfo;
- for (;(childNode=nli.current());++nli,++eli)
- {
- edgeInfo=eli.current();
- t << " <childnode refid=\"" << childNode->m_number << "\" relation=\"";
- if (isClassGraph)
- {
- switch(edgeInfo->m_color)
- {
- case EdgeInfo::Blue: t << "public-inheritance"; break;
- case EdgeInfo::Green: t << "protected-inheritance"; break;
- case EdgeInfo::Red: t << "private-inheritance"; break;
- case EdgeInfo::Purple: t << "usage"; break;
- case EdgeInfo::Orange: t << "template-instance"; break;
- case EdgeInfo::Orange2: t << "type-constraint"; break;
- case EdgeInfo::Grey: ASSERT(0); break;
- }
- }
- else // include graph
- {
- t << "include";
- }
- t << "\">" << endl;
- if (!edgeInfo->m_label.isEmpty())
- {
- int p=0;
- int ni;
- while ((ni=edgeInfo->m_label.find('\n',p))!=-1)
- {
- t << " <edgelabel>"
- << convertToXML(edgeInfo->m_label.mid(p,ni-p))
- << "</edgelabel>" << endl;
- p=ni+1;
- }
- t << " <edgelabel>"
- << convertToXML(edgeInfo->m_label.right(edgeInfo->m_label.length()-p))
- << "</edgelabel>" << endl;
- }
- t << " </childnode>" << endl;
- }
- }
- t << " </node>" << endl;
-}
-
-void DotNode::writeDocbook(FTextStream &t,bool isClassGraph)
-{
- t << " <node id=\"" << m_number << "\">" << endl;
- t << " <label>" << convertToXML(m_label) << "</label>" << endl;
- if (!m_url.isEmpty())
- {
- QCString url(m_url);
- const char *refPtr = url.data();
- char *urlPtr = strchr(url.rawData(),'$');
- if (urlPtr)
- {
- *urlPtr++='\0';
- t << " <link refid=\"" << convertToXML(urlPtr) << "\"";
- if (*refPtr!='\0')
- {
- t << " external=\"" << convertToXML(refPtr) << "\"";
- }
- t << "/>" << endl;
- }
- }
- if (m_children)
- {
- QListIterator<DotNode> nli(*m_children);
- QListIterator<EdgeInfo> eli(*m_edgeInfo);
- DotNode *childNode;
- EdgeInfo *edgeInfo;
- for (;(childNode=nli.current());++nli,++eli)
- {
- edgeInfo=eli.current();
- t << " <childnode refid=\"" << childNode->m_number << "\" relation=\"";
- if (isClassGraph)
- {
- switch(edgeInfo->m_color)
- {
- case EdgeInfo::Blue: t << "public-inheritance"; break;
- case EdgeInfo::Green: t << "protected-inheritance"; break;
- case EdgeInfo::Red: t << "private-inheritance"; break;
- case EdgeInfo::Purple: t << "usage"; break;
- case EdgeInfo::Orange: t << "template-instance"; break;
- case EdgeInfo::Orange2: t << "type-constraint"; break;
- case EdgeInfo::Grey: ASSERT(0); break;
- }
- }
- else // include graph
- {
- t << "include";
- }
- t << "\">" << endl;
- if (!edgeInfo->m_label.isEmpty())
- {
- int p=0;
- int ni;
- while ((ni=edgeInfo->m_label.find('\n',p))!=-1)
- {
- t << " <edgelabel>"
- << convertToXML(edgeInfo->m_label.mid(p,ni-p))
- << "</edgelabel>" << endl;
- p=ni+1;
- }
- t << " <edgelabel>"
- << convertToXML(edgeInfo->m_label.right(edgeInfo->m_label.length()-p))
- << "</edgelabel>" << endl;
- }
- t << " </childnode>" << endl;
- }
- }
- t << " </node>" << endl;
-}
-
-
-void DotNode::writeDEF(FTextStream &t)
-{
- const char* nodePrefix = " node-";
-
- t << " node = {" << endl;
- t << nodePrefix << "id = " << m_number << ';' << endl;
- t << nodePrefix << "label = '" << m_label << "';" << endl;
-
- if (!m_url.isEmpty())
- {
- QCString url(m_url);
- const char *refPtr = url.data();
- char *urlPtr = strchr(url.rawData(),'$');
- if (urlPtr)
- {
- *urlPtr++='\0';
- t << nodePrefix << "link = {" << endl << " "
- << nodePrefix << "link-id = '" << urlPtr << "';" << endl;
-
- if (*refPtr!='\0')
- {
- t << " " << nodePrefix << "link-external = '"
- << refPtr << "';" << endl;
- }
- t << " };" << endl;
- }
- }
- if (m_children)
- {
- QListIterator<DotNode> nli(*m_children);
- QListIterator<EdgeInfo> eli(*m_edgeInfo);
- DotNode *childNode;
- EdgeInfo *edgeInfo;
- for (;(childNode=nli.current());++nli,++eli)
- {
- edgeInfo=eli.current();
- t << " node-child = {" << endl;
- t << " child-id = '" << childNode->m_number << "';" << endl;
- t << " relation = ";
-
- switch(edgeInfo->m_color)
- {
- case EdgeInfo::Blue: t << "public-inheritance"; break;
- case EdgeInfo::Green: t << "protected-inheritance"; break;
- case EdgeInfo::Red: t << "private-inheritance"; break;
- case EdgeInfo::Purple: t << "usage"; break;
- case EdgeInfo::Orange: t << "template-instance"; break;
- case EdgeInfo::Orange2: t << "type-constraint"; break;
- case EdgeInfo::Grey: ASSERT(0); break;
- }
- t << ';' << endl;
-
- if (!edgeInfo->m_label.isEmpty())
- {
- t << " edgelabel = <<_EnD_oF_dEf_TeXt_" << endl
- << edgeInfo->m_label << endl
- << "_EnD_oF_dEf_TeXt_;" << endl;
- }
- t << " }; /* node-child */" << endl;
- } /* for (;childNode...) */
- }
- t << " }; /* node */" << endl;
-}
-
-
-void DotNode::clearWriteFlag()
-{
- m_written=FALSE;
- if (m_parents!=0)
- {
- QListIterator<DotNode> dnlip(*m_parents);
- DotNode *pn;
- for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
- {
- if (pn->m_written)
- {
- pn->clearWriteFlag();
- }
- }
- }
- if (m_children!=0)
- {
- QListIterator<DotNode> dnlic(*m_children);
- DotNode *cn;
- for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
- {
- if (cn->m_written)
- {
- cn->clearWriteFlag();
- }
- }
- }
-}
-
-void DotNode::colorConnectedNodes(int curColor)
-{
- if (m_children)
- {
- QListIterator<DotNode> dnlic(*m_children);
- DotNode *cn;
- for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
- {
- if (cn->m_subgraphId==-1) // uncolored child node
- {
- cn->m_subgraphId=curColor;
- cn->markAsVisible();
- cn->colorConnectedNodes(curColor);
- //printf("coloring node %s (%p): %d\n",cn->m_label.data(),cn,cn->m_subgraphId);
- }
- }
- }
-
- if (m_parents)
- {
- QListIterator<DotNode> dnlip(*m_parents);
- DotNode *pn;
- for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
- {
- if (pn->m_subgraphId==-1) // uncolored parent node
- {
- pn->m_subgraphId=curColor;
- pn->markAsVisible();
- pn->colorConnectedNodes(curColor);
- //printf("coloring node %s (%p): %d\n",pn->m_label.data(),pn,pn->m_subgraphId);
- }
- }
- }
-}
-
-void DotNode::renumberNodes(int &number)
-{
- m_number = number++;
- if (m_children)
- {
- QListIterator<DotNode> dnlic(*m_children);
- DotNode *cn;
- for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
- {
- if (!cn->m_renumbered)
- {
- cn->m_renumbered = true;
- cn->renumberNodes(number);
- }
- }
- }
-}
-
-const DotNode *DotNode::findDocNode() const
-{
- if (!m_url.isEmpty()) return this;
- //printf("findDocNode(): `%s'\n",m_label.data());
- if (m_parents)
- {
- QListIterator<DotNode> dnli(*m_parents);
- DotNode *pn;
- for (dnli.toFirst();(pn=dnli.current());++dnli)
- {
- if (!pn->m_hasDoc)
- {
- pn->m_hasDoc=TRUE;
- const DotNode *dn = pn->findDocNode();
- if (dn) return dn;
- }
- }
- }
- if (m_children)
- {
- QListIterator<DotNode> dnli(*m_children);
- DotNode *cn;
- for (dnli.toFirst();(cn=dnli.current());++dnli)
- {
- if (!cn->m_hasDoc)
- {
- cn->m_hasDoc=TRUE;
- const DotNode *dn = cn->findDocNode();
- if (dn) return dn;
- }
- }
- }
- return 0;
-}
-
-//--------------------------------------------------------------------
-
-void DotGfxHierarchyTable::createGraph(DotNode *n,FTextStream &out,
- const char *path,const char *fileName,int id) const
+class GraphLegendDotGraph : public DotGraph
{
- QDir d(path);
- QCString baseName;
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- if (m_prefix.isEmpty())
- baseName.sprintf("inherit_graph_%d",id);
- else
- baseName.sprintf("%sinherit_graph_%d",m_prefix.data(),id);
- QCString imgName = baseName+"."+ imgExt;
- QCString mapName = baseName+".map";
- QCString absImgName = QCString(d.absPath().data())+"/"+imgName;
- QCString absMapName = QCString(d.absPath().data())+"/"+mapName;
- QCString absBaseName = QCString(d.absPath().data())+"/"+baseName;
- QListIterator<DotNode> dnli2(*m_rootNodes);
- DotNode *node;
-
- // compute md5 checksum of the graph were are about to generate
- QGString theGraph;
- FTextStream md5stream(&theGraph);
- writeGraphHeader(md5stream,theTranslator->trGraphicalHierarchy());
- md5stream << " rankdir=\"LR\";" << endl;
- for (dnli2.toFirst();(node=dnli2.current());++dnli2)
- {
- if (node->m_subgraphId==n->m_subgraphId)
+ private:
+ virtual QCString getBaseName() const
{
- node->clearWriteFlag();
+ return "graph_legend";
}
- }
- for (dnli2.toFirst();(node=dnli2.current());++dnli2)
- {
- if (node->m_subgraphId==n->m_subgraphId)
- {
- node->write(md5stream,DotNode::Hierarchy,GOF_BITMAP,FALSE,TRUE,TRUE);
- }
- }
- writeGraphFooter(md5stream);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),33);
- bool regenerate=FALSE;
- if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
- !checkDeliverables(absImgName,absMapName))
- {
- regenerate=TRUE;
- // image was new or has changed
- QCString dotName=absBaseName+".dot";
- QFile f(dotName);
- if (!f.open(IO_WriteOnly)) return;
- FTextStream t(&f);
- t << theGraph;
- f.close();
- DotRunner *dotRun = new DotRunner(dotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
- }
- else
- {
- removeDotGraph(absBaseName+".dot");
- }
- Doxygen::indexList->addImageFile(imgName);
- // write image and map in a table row
- QCString mapLabel = escapeCharsInString(n->m_label,FALSE);
- if (imgExt=="svg") // vector graphics
- {
- if (regenerate || !writeSVGFigureLink(out,QCString(),baseName,absImgName))
+ virtual void computeTheGraph()
{
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,QCString(),
- FALSE,QCString(),FALSE,0);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,
- absImgName,QCString());
- out << "<!-- SVG " << mapId << " -->" << endl;
+ FTextStream md5stream(&m_theGraph);
+ writeGraphHeader(md5stream,theTranslator->trLegendTitle());
+ md5stream << " Node9 [shape=\"box\",label=\"Inherited\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",fillcolor=\"grey75\",style=\"filled\" fontcolor=\"black\"];\n";
+ md5stream << " Node10 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node10 [shape=\"box\",label=\"PublicBase\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classPublicBase" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node11 -> Node10 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node11 [shape=\"box\",label=\"Truncated\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"red\",URL=\"$classTruncated" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node13 -> Node9 [dir=\"back\",color=\"darkgreen\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node13 [shape=\"box\",label=\"ProtectedBase\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classProtectedBase" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node14 -> Node9 [dir=\"back\",color=\"firebrick4\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node14 [shape=\"box\",label=\"PrivateBase\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classPrivateBase" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node15 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node15 [shape=\"box\",label=\"Undocumented\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"grey75\"];\n";
+ md5stream << " Node16 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"solid\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node16 [shape=\"box\",label=\"Templ< int >\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node17 -> Node16 [dir=\"back\",color=\"orange\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"dashed\",label=\"< int >\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node17 [shape=\"box\",label=\"Templ< T >\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
+ md5stream << " Node18 -> Node9 [dir=\"back\",color=\"darkorchid3\",fontsize=\"" << DOT_FONTSIZE << "\",style=\"dashed\",label=\"m_usedClass\",fontname=\"" << DOT_FONTNAME << "\"];\n";
+ md5stream << " Node18 [shape=\"box\",label=\"Used\",fontsize=\"" << DOT_FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << DOT_FONTNAME << "\",color=\"black\",URL=\"$classUsed" << Doxygen::htmlFileExtension << "\"];\n";
+ writeGraphFooter(md5stream);
}
- }
- else // normal bitmap
- {
- out << "<img src=\"" << imgName << "\" border=\"0\" alt=\"\" usemap=\"#"
- << mapLabel << "\"/>" << endl;
- if (regenerate || !insertMapFile(out,absMapName,QCString(),mapLabel))
+ virtual QCString getMapLabel() const
{
- int mapId = DotManager::instance()->addMap(fileName,absMapName,QCString(),
- FALSE,QCString(),mapLabel);
- out << "<!-- MAP " << mapId << " -->" << endl;
+ return "";
}
- }
-}
-
-void DotGfxHierarchyTable::writeGraph(FTextStream &out,
- const char *path,const char *fileName) const
-{
- //printf("DotGfxHierarchyTable::writeGraph(%s)\n",name);
- //printf("m_rootNodes=%p count=%d\n",m_rootNodes,m_rootNodes->count());
-
- if (m_rootSubgraphs->count()==0) return;
-
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- // put each connected subgraph of the hierarchy in a row of the HTML output
- out << "<table border=\"0\" cellspacing=\"10\" cellpadding=\"0\">" << endl;
-
- QListIterator<DotNode> dnli(*m_rootSubgraphs);
- DotNode *n;
- int count=0;
- for (dnli.toFirst();(n=dnli.current());++dnli)
- {
- out << "<tr><td>";
- createGraph(n,out,path,fileName,count++);
- out << "</td></tr>" << endl;
- }
- out << "</table>" << endl;
-}
-
-void DotGfxHierarchyTable::addHierarchy(DotNode *n,const ClassDef *cd,bool hideSuper)
-{
- //printf("addHierarchy `%s' baseClasses=%d\n",cd->name().data(),cd->baseClasses()->count());
- if (cd->subClasses())
- {
- BaseClassListIterator bcli(*cd->subClasses());
- BaseClassDef *bcd;
- for ( ; (bcd=bcli.current()) ; ++bcli )
- {
- ClassDef *bClass=bcd->classDef;
- //printf(" Trying sub class=`%s' usedNodes=%d\n",bClass->name().data(),m_usedNodes->count());
- if (bClass->isVisibleInHierarchy() && hasVisibleRoot(bClass->baseClasses()))
- {
- DotNode *bn;
- //printf(" Node `%s' Found visible class=`%s'\n",n->m_label.data(),
- // bClass->name().data());
- if ((bn=m_usedNodes->find(bClass->name()))) // node already present
- {
- if (n->m_children==0 || n->m_children->findRef(bn)==-1) // no arrow yet
- {
- n->addChild(bn,bcd->prot);
- bn->addParent(n);
- //printf(" Adding node %s to existing base node %s (c=%d,p=%d)\n",
- // n->m_label.data(),
- // bn->m_label.data(),
- // bn->m_children ? bn->m_children->count() : 0,
- // bn->m_parents ? bn->m_parents->count() : 0
- // );
- }
- //else
- //{
- // printf(" Class already has an arrow!\n");
- //}
- }
- else
- {
- QCString tmp_url="";
- if (bClass->isLinkable() && !bClass->isHidden())
- {
- tmp_url=bClass->getReference()+"$"+bClass->getOutputFileBase();
- if (!bClass->anchor().isEmpty())
- {
- tmp_url+="#"+bClass->anchor();
- }
- }
- QCString tooltip = bClass->briefDescriptionAsTooltip();
- bn = new DotNode(getNextNodeNumber(),
- bClass->displayName(),
- tooltip,
- tmp_url.data()
- );
- n->addChild(bn,bcd->prot);
- bn->addParent(n);
- //printf(" Adding node %s to new base node %s (c=%d,p=%d)\n",
- // n->m_label.data(),
- // bn->m_label.data(),
- // bn->m_children ? bn->m_children->count() : 0,
- // bn->m_parents ? bn->m_parents->count() : 0
- // );
- //printf(" inserting %s (%p)\n",bClass->name().data(),bn);
- m_usedNodes->insert(bClass->name(),bn); // add node to the used list
- }
- if (!bClass->isVisited() && !hideSuper && bClass->subClasses())
- {
- bool wasVisited=bClass->isVisited();
- bClass->setVisited(TRUE);
- addHierarchy(bn,bClass,wasVisited);
- }
- }
- }
- }
- //printf("end addHierarchy\n");
-}
-
-void DotGfxHierarchyTable::addClassList(const ClassSDict *cl)
-{
- static bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE);
- ClassSDict::Iterator cli(*cl);
- ClassDef *cd;
- for (cli.toLast();(cd=cli.current());--cli)
- {
- //printf("Trying %s subClasses=%d\n",cd->name().data(),cd->subClasses()->count());
- if (cd->getLanguage()==SrcLangExt_VHDL &&
- (VhdlDocGen::VhdlClasses)cd->protection()!=VhdlDocGen::ENTITYCLASS
- )
- {
- continue;
- }
- if (sliceOpt && cd->compoundType() != m_classType)
- {
- continue;
- }
- if (!hasVisibleRoot(cd->baseClasses()) &&
- cd->isVisibleInHierarchy()
- ) // root node in the forest
- {
- QCString tmp_url="";
- if (cd->isLinkable() && !cd->isHidden())
- {
- tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
- if (!cd->anchor().isEmpty())
- {
- tmp_url+="#"+cd->anchor();
- }
- }
- //printf("Inserting root class %s\n",cd->name().data());
- QCString tooltip = cd->briefDescriptionAsTooltip();
- DotNode *n = new DotNode(getNextNodeNumber(),
- cd->displayName(),
- tooltip,
- tmp_url.data());
-
- //m_usedNodes->clear();
- m_usedNodes->insert(cd->name(),n);
- m_rootNodes->insert(0,n);
- if (!cd->isVisited() && cd->subClasses())
- {
- addHierarchy(n,cd,cd->isVisited());
- cd->setVisited(TRUE);
- }
- }
- }
-}
-
-DotGfxHierarchyTable::DotGfxHierarchyTable(const char *prefix,ClassDef::CompoundType ct)
- : m_prefix(prefix)
- , m_classType(ct)
-{
- m_rootNodes = new QList<DotNode>;
- m_usedNodes = new QDict<DotNode>(1009);
- m_usedNodes->setAutoDelete(TRUE);
- m_rootSubgraphs = new DotNodeList;
-
- // build a graph with each class as a node and the inheritance relations
- // as edges
- initClassHierarchy(Doxygen::classSDict);
- initClassHierarchy(Doxygen::hiddenClasses);
- addClassList(Doxygen::classSDict);
- addClassList(Doxygen::hiddenClasses);
- // m_usedNodes now contains all nodes in the graph
-
- // color the graph into a set of independent subgraphs
- bool done=FALSE;
- int curColor=0;
- QListIterator<DotNode> dnli(*m_rootNodes);
- while (!done) // there are still nodes to color
- {
- DotNode *n;
- done=TRUE; // we are done unless there are still uncolored nodes
- for (dnli.toLast();(n=dnli.current());--dnli)
- {
- if (n->m_subgraphId==-1) // not yet colored
- {
- //printf("Starting at node %s (%p): %d\n",n->m_label.data(),n,curColor);
- done=FALSE; // still uncolored nodes
- n->m_subgraphId=curColor;
- n->markAsVisible();
- n->colorConnectedNodes(curColor);
- curColor++;
- const DotNode *dn=n->findDocNode();
- if (dn!=0)
- m_rootSubgraphs->inSort(dn);
- else
- m_rootSubgraphs->inSort(n);
- }
- }
- }
-
- //printf("Number of independent subgraphs: %d\n",curColor);
- QListIterator<DotNode> dnli2(*m_rootSubgraphs);
- DotNode *n;
- for (dnli2.toFirst();(n=dnli2.current());++dnli2)
- {
- //printf("Node %s color=%d (c=%d,p=%d)\n",
- // n->m_label.data(),n->m_subgraphId,
- // n->m_children?n->m_children->count():0,
- // n->m_parents?n->m_parents->count():0);
- int number=0;
- n->renumberNodes(number);
- }
-}
-
-DotGfxHierarchyTable::~DotGfxHierarchyTable()
-{
- //printf("DotGfxHierarchyTable::~DotGfxHierarchyTable\n");
-
- //QDictIterator<DotNode> di(*m_usedNodes);
- //DotNode *n;
- //for (;(n=di.current());++di)
- //{
- // printf("Node %p: %s\n",n,n->label().data());
- //}
-
- delete m_rootNodes;
- delete m_usedNodes;
- delete m_rootSubgraphs;
-}
-
-//--------------------------------------------------------------------
-
-void DotClassGraph::addClass(const ClassDef *cd,DotNode *n,int prot,
- const char *label,const char *usedName,const char *templSpec,bool base,int distance)
-{
- if (Config_getBool(HIDE_UNDOC_CLASSES) && !cd->isLinkable()) return;
-
- int edgeStyle = (label || prot==EdgeInfo::Orange || prot==EdgeInfo::Orange2) ? EdgeInfo::Dashed : EdgeInfo::Solid;
- QCString className;
- if (cd->isAnonymous())
- {
- className="anonymous:";
- className+=label;
- }
- else if (usedName) // name is a typedef
- {
- className=usedName;
- }
- else if (templSpec) // name has a template part
- {
- className=insertTemplateSpecifierInScope(cd->name(),templSpec);
- }
- else // just a normal name
- {
- className=cd->displayName();
- }
- //printf("DotClassGraph::addClass(class=`%s',parent=%s,prot=%d,label=%s,dist=%d,usedName=%s,templSpec=%s,base=%d)\n",
- // className.data(),n->m_label.data(),prot,label,distance,usedName,templSpec,base);
- DotNode *bn = m_usedNodes->find(className);
- if (bn) // class already inserted
- {
- if (base)
- {
- n->addChild(bn,prot,edgeStyle,label);
- bn->addParent(n);
- }
- else
- {
- bn->addChild(n,prot,edgeStyle,label);
- n->addParent(bn);
- }
- bn->setDistance(distance);
- //printf(" add exiting node %s of %s\n",bn->m_label.data(),n->m_label.data());
- }
- else // new class
- {
- QCString displayName=className;
- if (Config_getBool(HIDE_SCOPE_NAMES)) displayName=stripScope(displayName);
- QCString tmp_url;
- if (cd->isLinkable() && !cd->isHidden())
- {
- tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
- if (!cd->anchor().isEmpty())
- {
- tmp_url+="#"+cd->anchor();
- }
- }
- QCString tooltip = cd->briefDescriptionAsTooltip();
- bn = new DotNode(getNextNodeNumber(),
- displayName,
- tooltip,
- tmp_url.data(),
- FALSE, // rootNode
- cd
- );
- if (base)
- {
- n->addChild(bn,prot,edgeStyle,label);
- bn->addParent(n);
- }
- else
- {
- bn->addChild(n,prot,edgeStyle,label);
- n->addParent(bn);
- }
- bn->setDistance(distance);
- m_usedNodes->insert(className,bn);
- //printf(" add new child node `%s' to %s hidden=%d url=%s\n",
- // className.data(),n->m_label.data(),cd->isHidden(),tmp_url.data());
-
- buildGraph(cd,bn,base,distance+1);
- }
-}
-
-void DotClassGraph::determineTruncatedNodes(QList<DotNode> &queue,bool includeParents)
-{
- while (queue.count()>0)
- {
- DotNode *n = queue.take(0);
- if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
- {
- bool truncated = FALSE;
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- if (!dn->isVisible())
- truncated = TRUE;
- else
- queue.append(dn);
- }
- }
- if (n->m_parents && includeParents)
- {
- QListIterator<DotNode> li(*n->m_parents);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- if (!dn->isVisible())
- truncated = TRUE;
- else
- queue.append(dn);
- }
- }
- n->markAsTruncated(truncated);
- }
- }
-}
-
-bool DotClassGraph::determineVisibleNodes(DotNode *rootNode,
- int maxNodes,bool includeParents)
-{
- QList<DotNode> childQueue;
- QList<DotNode> parentQueue;
- QArray<int> childTreeWidth;
- QArray<int> parentTreeWidth;
- childQueue.append(rootNode);
- if (includeParents) parentQueue.append(rootNode);
- bool firstNode=TRUE; // flag to force reprocessing rootNode in the parent loop
- // despite being marked visible in the child loop
- while ((childQueue.count()>0 || parentQueue.count()>0) && maxNodes>0)
- {
- static int maxDistance = Config_getInt(MAX_DOT_GRAPH_DEPTH);
- if (childQueue.count()>0)
- {
- DotNode *n = childQueue.take(0);
- int distance = n->distance();
- if (!n->isVisible() && distance<=maxDistance) // not yet processed
- {
- if (distance>0)
- {
- int oldSize=(int)childTreeWidth.size();
- if (distance>oldSize)
- {
- childTreeWidth.resize(QMAX(childTreeWidth.size(),(uint)distance));
- int i; for (i=oldSize;i<distance;i++) childTreeWidth[i]=0;
- }
- childTreeWidth[distance-1]+=n->label().length();
- }
- n->markAsVisible();
- maxNodes--;
- // add direct children
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- childQueue.append(dn);
- }
- }
- }
- }
- if (includeParents && parentQueue.count()>0)
- {
- DotNode *n = parentQueue.take(0);
- if ((!n->isVisible() || firstNode) && n->distance()<=maxDistance) // not yet processed
- {
- firstNode=FALSE;
- int distance = n->distance();
- if (distance>0)
- {
- int oldSize = (int)parentTreeWidth.size();
- if (distance>oldSize)
- {
- parentTreeWidth.resize(QMAX(parentTreeWidth.size(),(uint)distance));
- int i; for (i=oldSize;i<distance;i++) parentTreeWidth[i]=0;
- }
- parentTreeWidth[distance-1]+=n->label().length();
- }
- n->markAsVisible();
- maxNodes--;
- // add direct parents
- if (n->m_parents)
- {
- QListIterator<DotNode> li(*n->m_parents);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- parentQueue.append(dn);
- }
- }
- }
- }
- }
- if (Config_getBool(UML_LOOK)) return FALSE; // UML graph are always top to bottom
- int maxWidth=0;
- int maxHeight=(int)QMAX(childTreeWidth.size(),parentTreeWidth.size());
- uint i;
- for (i=0;i<childTreeWidth.size();i++)
- {
- if (childTreeWidth.at(i)>maxWidth) maxWidth=childTreeWidth.at(i);
- }
- for (i=0;i<parentTreeWidth.size();i++)
- {
- if (parentTreeWidth.at(i)>maxWidth) maxWidth=parentTreeWidth.at(i);
- }
- //printf("max tree width=%d, max tree height=%d\n",maxWidth,maxHeight);
- return maxWidth>80 && maxHeight<12; // used metric to decide to render the tree
- // from left to right instead of top to bottom,
- // with the idea to render very wide trees in
- // left to right order.
-}
-
-void DotClassGraph::buildGraph(const ClassDef *cd,DotNode *n,bool base,int distance)
-{
- static bool templateRelations = Config_getBool(TEMPLATE_RELATIONS);
- //printf("DocClassGraph::buildGraph(%s,distance=%d,base=%d)\n",
- // cd->name().data(),distance,base);
- // ---- Add inheritance relations
-
- if (m_graphType == DotNode::Inheritance || m_graphType==DotNode::Collaboration)
- {
- BaseClassList *bcl = base ? cd->baseClasses() : cd->subClasses();
- if (bcl)
- {
- BaseClassListIterator bcli(*bcl);
- BaseClassDef *bcd;
- for ( ; (bcd=bcli.current()) ; ++bcli )
- {
- //printf("-------- inheritance relation %s->%s templ=`%s'\n",
- // cd->name().data(),bcd->classDef->name().data(),bcd->templSpecifiers.data());
- addClass(bcd->classDef,n,bcd->prot,0,bcd->usedName,
- bcd->templSpecifiers,base,distance);
- }
- }
- }
- if (m_graphType == DotNode::Collaboration)
- {
- // ---- Add usage relations
-
- UsesClassDict *dict =
- base ? cd->usedImplementationClasses() :
- cd->usedByImplementationClasses()
- ;
- if (dict)
- {
- UsesClassDictIterator ucdi(*dict);
- UsesClassDef *ucd;
- for (;(ucd=ucdi.current());++ucdi)
- {
- QCString label;
- QDictIterator<void> dvi(*ucd->accessors);
- const char *s;
- bool first=TRUE;
- int count=0;
- int maxLabels=10;
- for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count)
- {
- if (first)
- {
- label=s;
- first=FALSE;
- }
- else
- {
- label+=QCString("\n")+s;
- }
- }
- if (count==maxLabels) label+="\n...";
- //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data());
- addClass(ucd->classDef,n,EdgeInfo::Purple,label,0,
- ucd->templSpecifiers,base,distance);
- }
- }
- }
- if (templateRelations && base)
- {
- ConstraintClassDict *dict = cd->templateTypeConstraints();
- if (dict)
- {
- ConstraintClassDictIterator ccdi(*dict);
- ConstraintClassDef *ccd;
- for (;(ccd=ccdi.current());++ccdi)
- {
- QCString label;
- QDictIterator<void> dvi(*ccd->accessors);
- const char *s;
- bool first=TRUE;
- int count=0;
- int maxLabels=10;
- for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count)
- {
- if (first)
- {
- label=s;
- first=FALSE;
- }
- else
- {
- label+=QCString("\n")+s;
- }
- }
- if (count==maxLabels) label+="\n...";
- //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data());
- addClass(ccd->classDef,n,EdgeInfo::Orange2,label,0,
- 0,TRUE,distance);
- }
- }
- }
-
- // ---- Add template instantiation relations
-
- if (templateRelations)
- {
- if (base) // template relations for base classes
- {
- const ClassDef *templMaster=cd->templateMaster();
- if (templMaster)
- {
- QDictIterator<ClassDef> cli(*templMaster->getTemplateInstances());
- const ClassDef *templInstance;
- for (;(templInstance=cli.current());++cli)
- {
- if (templInstance==cd)
- {
- addClass(templMaster,n,EdgeInfo::Orange,cli.currentKey(),0,
- 0,TRUE,distance);
- }
- }
- }
- }
- else // template relations for super classes
- {
- const QDict<ClassDef> *templInstances = cd->getTemplateInstances();
- if (templInstances)
- {
- QDictIterator<ClassDef> cli(*templInstances);
- const ClassDef *templInstance;
- for (;(templInstance=cli.current());++cli)
- {
- addClass(templInstance,n,EdgeInfo::Orange,cli.currentKey(),0,
- 0,FALSE,distance);
- }
- }
- }
- }
-}
-
-DotClassGraph::DotClassGraph(const ClassDef *cd,DotNode::GraphType t)
-{
- //printf("--------------- DotClassGraph::DotClassGraph `%s'\n",cd->displayName().data());
- m_graphType = t;
- QCString tmp_url="";
- if (cd->isLinkable() && !cd->isHidden())
- {
- tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
- if (!cd->anchor().isEmpty())
- {
- tmp_url+="#"+cd->anchor();
- }
- }
- QCString className = cd->displayName();
- QCString tooltip = cd->briefDescriptionAsTooltip();
- m_startNode = new DotNode(getNextNodeNumber(),
- className,
- tooltip,
- tmp_url.data(),
- TRUE, // is a root node
- cd
- );
- m_startNode->setDistance(0);
- m_usedNodes = new QDict<DotNode>(1009);
- m_usedNodes->insert(className,m_startNode);
-
- //printf("Root node %s\n",cd->name().data());
- //if (m_recDepth>0)
- //{
- buildGraph(cd,m_startNode,TRUE,1);
- if (t==DotNode::Inheritance) buildGraph(cd,m_startNode,FALSE,1);
- //}
-
- static int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- //int directChildNodes = 1;
- //if (m_startNode->m_children!=0)
- // directChildNodes+=m_startNode->m_children->count();
- //if (t==DotNode::Inheritance && m_startNode->m_parents!=0)
- // directChildNodes+=m_startNode->m_parents->count();
- //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
- //openNodeQueue.append(m_startNode);
- m_lrRank = determineVisibleNodes(m_startNode,maxNodes,t==DotNode::Inheritance);
- QList<DotNode> openNodeQueue;
- openNodeQueue.append(m_startNode);
- determineTruncatedNodes(openNodeQueue,t==DotNode::Inheritance);
-
- m_collabFileName = cd->collaborationGraphFileName();
- m_inheritFileName = cd->inheritanceGraphFileName();
-}
-
-bool DotClassGraph::isTrivial() const
-{
- static bool umlLook = Config_getBool(UML_LOOK);
- if (m_graphType==DotNode::Inheritance)
- return m_startNode->m_children==0 && m_startNode->m_parents==0;
- else
- return !umlLook && m_startNode->m_children==0;
-}
-
-bool DotClassGraph::isTooBig() const
-{
- static int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int numNodes = 0;
- numNodes+= m_startNode->m_children ? m_startNode->m_children->count() : 0;
- if (m_graphType==DotNode::Inheritance)
- {
- numNodes+= m_startNode->m_parents ? m_startNode->m_parents->count() : 0;
- }
- return numNodes>=maxNodes;
-}
-
-DotClassGraph::~DotClassGraph()
-{
- deleteNodes(m_startNode);
- delete m_usedNodes;
-}
-
-/*! Computes a 16 byte md5 checksum for a given dot graph.
- * The md5 checksum is returned as a 32 character ASCII string.
- */
-QCString computeMd5Signature(DotNode *root,
- DotNode::GraphType gt,
- GraphOutputFormat format,
- const QCString &rank, // either "LR", "RL", or ""
- bool renderParents,
- bool backArrows,
- const QCString &title,
- QCString &graphStr
- )
-{
- //printf("computeMd5Signature\n");
- QGString buf;
- FTextStream md5stream(&buf);
- writeGraphHeader(md5stream,title);
- if (!rank.isEmpty())
- {
- md5stream << " rankdir=\"" << rank << "\";" << endl;
- }
- root->clearWriteFlag();
- root->write(md5stream,
- gt,
- format,
- gt!=DotNode::CallGraph && gt!=DotNode::Dependency,
- TRUE,
- backArrows);
- if (renderParents && root->m_parents)
- {
- QListIterator<DotNode> dnli(*root->m_parents);
- DotNode *pn;
- for (dnli.toFirst();(pn=dnli.current());++dnli)
- {
- if (pn->isVisible())
- {
- root->writeArrow(md5stream, // stream
- gt, // graph type
- format, // output format
- pn, // child node
- pn->m_edgeInfo->at(pn->m_children->findRef(root)), // edge info
- FALSE, // topDown?
- backArrows // point back?
- );
- }
- pn->write(md5stream, // stream
- gt, // graph type
- format, // output format
- TRUE, // topDown?
- FALSE, // toChildren?
- backArrows // backward pointing arrows?
- );
- }
- }
- writeGraphFooter(md5stream);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)buf.data(),buf.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),33);
- graphStr=buf.data();
- //printf("md5: %s | file: %s\n",sigStr,baseName.data());
- return sigStr;
-}
-
-static bool updateDotGraph(DotNode *root,
- DotNode::GraphType gt,
- const QCString &baseName,
- GraphOutputFormat format,
- const QCString &rank,
- bool renderParents,
- bool backArrows,
- const QCString &title=QCString()
- )
-{
- QCString theGraph;
- // TODO: write graph to theGraph, then compute md5 checksum
- QCString md5 = computeMd5Signature(
- root,gt,format,rank,renderParents,
- backArrows,title,theGraph);
- QFile f(baseName+".dot");
- if (f.open(IO_WriteOnly))
- {
- FTextStream t(&f);
- t << theGraph;
- }
- return checkAndUpdateMd5Signature(baseName,md5); // graph needs to be regenerated
-}
-
-QCString DotClassGraph::writeGraph(FTextStream &out,
- GraphOutputFormat graphFormat,
- EmbeddedOutputFormat textFormat,
- const char *path,
- const char *fileName,
- const char *relPath,
- bool /*isTBRank*/,
- bool generateImageMap,
- int graphId) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QCString baseName;
- QCString mapName;
- switch (m_graphType)
- {
- case DotNode::Collaboration:
- mapName="coll_map";
- baseName=m_collabFileName;
- break;
- case DotNode::Inheritance:
- mapName="inherit_map";
- baseName=m_inheritFileName;
- break;
- default:
- ASSERT(0);
- break;
- }
-
- // derive target file names from baseName
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString absBaseName = d.absPath().utf8()+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- QCString absImgName = absBaseName+"."+imgExt;
-
- bool regenerate = FALSE;
- if (updateDotGraph(m_startNode,
- m_graphType,
- absBaseName,
- graphFormat,
- m_lrRank ? "LR" : "",
- m_graphType==DotNode::Inheritance,
- TRUE,
- m_startNode->label()
- ) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
- if (graphFormat==GOF_BITMAP) // run dot to create a bitmap image
- {
- DotRunner *dotRun = new DotRunner(absDotName,
- d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
-
- }
- else if (graphFormat==GOF_EPS) // run dot to create a .eps image
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
- }
- }
- Doxygen::indexList->addImageFile(baseName+"."+imgExt);
-
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- out << "<para>" << endl;
- out << " <informalfigure>" << endl;
- out << " <mediaobject>" << endl;
- out << " <imageobject>" << endl;
- out << " <imagedata";
- out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- out << "</imagedata>" << endl;
- out << " </imageobject>" << endl;
- out << " </mediaobject>" << endl;
- out << " </informalfigure>" << endl;
- out << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && generateImageMap) // produce HTML to include the image
- {
- QCString mapLabel = escapeCharsInString(m_startNode->m_label,FALSE)+"_"+
- escapeCharsInString(mapName,FALSE);
- if (imgExt=="svg") // add link to SVG file without map file
- {
- out << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- out << "<!-- SVG " << mapId << " -->" << endl;
- }
- out << "</div>" << endl;
- }
- else // add link to bitmap file with image map
- {
- out << "<div class=\"center\">";
- out << "<img src=\"" << relPath << baseName << "."
- << imgExt << "\" border=\"0\" usemap=\"#"
- << mapLabel << "\" alt=\"";
- switch (m_graphType)
- {
- case DotNode::Collaboration:
- out << "Collaboration graph";
- break;
- case DotNode::Inheritance:
- out << "Inheritance graph";
- break;
- default:
- ASSERT(0);
- break;
- }
- out << "\"/>";
- out << "</div>" << endl;
- if (regenerate || !insertMapFile(out,absMapName,relPath,mapLabel))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- FALSE,QCString(),mapLabel);
- out << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- }
- else if (graphFormat==GOF_EPS) // produce tex to include the .eps image
- {
- if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE /*TRUE*/);
- out << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-//--------------------------------------------------------------------
-
-void DotClassGraph::writeXML(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeXML(t,TRUE);
- }
-}
-
-void DotClassGraph::writeDocbook(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeDocbook(t,TRUE);
- }
-}
-
-void DotClassGraph::writeDEF(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeDEF(t);
- }
-}
-
-//--------------------------------------------------------------------
-
-void DotInclDepGraph::buildGraph(DotNode *n,const FileDef *fd,int distance)
-{
- QList<IncludeInfo> *includeFiles =
- m_inverse ? fd->includedByFileList() : fd->includeFileList();
- if (includeFiles)
- {
- QListIterator<IncludeInfo> ili(*includeFiles);
- IncludeInfo *ii;
- for (;(ii=ili.current());++ili)
- {
- const FileDef *bfd = ii->fileDef;
- QCString in = ii->includeName;
- //printf(">>>> in=`%s' bfd=%p\n",ii->includeName.data(),bfd);
- bool doc=TRUE,src=FALSE;
- if (bfd)
- {
- in = bfd->absFilePath();
- doc = bfd->isLinkable() && !bfd->isHidden();
- src = bfd->generateSourceFile();
- }
- if (doc || src || !Config_getBool(HIDE_UNDOC_RELATIONS))
- {
- QCString url="";
- if (bfd) url=bfd->getOutputFileBase().copy();
- if (!doc && src)
- {
- url=bfd->getSourceFileBase();
- }
- DotNode *bn = m_usedNodes->find(in);
- if (bn) // file is already a node in the graph
- {
- n->addChild(bn,0,0,0);
- bn->addParent(n);
- bn->setDistance(distance);
- }
- else
- {
- QCString tmp_url;
- QCString tooltip;
- if (bfd)
- {
- tmp_url=doc || src ? bfd->getReference()+"$"+url : QCString();
- tooltip = bfd->briefDescriptionAsTooltip();
- }
- bn = new DotNode(
- getNextNodeNumber(),// n
- ii->includeName, // label
- tooltip, // tip
- tmp_url, // url
- FALSE, // rootNode
- 0 // cd
- );
- n->addChild(bn,0,0,0);
- bn->addParent(n);
- m_usedNodes->insert(in,bn);
- bn->setDistance(distance);
-
- if (bfd) buildGraph(bn,bfd,distance+1);
- }
- }
- }
- }
-}
-
-void DotInclDepGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes)
-{
- while (queue.count()>0 && maxNodes>0)
- {
- static int maxDistance = Config_getInt(MAX_DOT_GRAPH_DEPTH);
- DotNode *n = queue.take(0);
- if (!n->isVisible() && n->distance()<=maxDistance) // not yet processed
- {
- n->markAsVisible();
- maxNodes--;
- // add direct children
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- queue.append(dn);
- }
- }
- }
- }
-}
-
-void DotInclDepGraph::determineTruncatedNodes(QList<DotNode> &queue)
-{
- while (queue.count()>0)
- {
- DotNode *n = queue.take(0);
- if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
- {
- bool truncated = FALSE;
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- if (!dn->isVisible())
- truncated = TRUE;
- else
- queue.append(dn);
- }
- }
- n->markAsTruncated(truncated);
- }
- }
-}
-
-DotInclDepGraph::DotInclDepGraph(const FileDef *fd,bool inverse)
-{
- m_inverse = inverse;
- ASSERT(fd!=0);
- m_inclDepFileName = fd->includeDependencyGraphFileName();
- m_inclByDepFileName = fd->includedByDependencyGraphFileName();
- QCString tmp_url=fd->getReference()+"$"+fd->getOutputFileBase();
- QCString tooltip = fd->briefDescriptionAsTooltip();
- m_startNode = new DotNode(getNextNodeNumber(),
- fd->docName(),
- tooltip,
- tmp_url.data(),
- TRUE // root node
- );
- m_startNode->setDistance(0);
- m_usedNodes = new QDict<DotNode>(1009);
- m_usedNodes->insert(fd->absFilePath(),m_startNode);
- buildGraph(m_startNode,fd,1);
-
- static int nodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int maxNodes = nodes;
- //int directChildNodes = 1;
- //if (m_startNode->m_children!=0)
- // directChildNodes+=m_startNode->m_children->count();
- //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
- QList<DotNode> openNodeQueue;
- openNodeQueue.append(m_startNode);
- determineVisibleNodes(openNodeQueue,maxNodes);
- openNodeQueue.clear();
- openNodeQueue.append(m_startNode);
- determineTruncatedNodes(openNodeQueue);
-}
-
-DotInclDepGraph::~DotInclDepGraph()
-{
- deleteNodes(m_startNode);
- delete m_usedNodes;
-}
-
-QCString DotInclDepGraph::writeGraph(FTextStream &out,
- GraphOutputFormat graphFormat,
- EmbeddedOutputFormat textFormat,
- const char *path,
- const char *fileName,
- const char *relPath,
- bool generateImageMap,
- int graphId
- ) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QCString baseName;
- if (m_inverse)
- {
- baseName=m_inclByDepFileName;
- }
- else
- {
- baseName=m_inclDepFileName;
- }
- QCString mapName=escapeCharsInString(m_startNode->m_label,FALSE);
- if (m_inverse) mapName+="dep";
-
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString absBaseName = d.absPath().utf8()+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- QCString absImgName = absBaseName+"."+imgExt;
-
- bool regenerate = FALSE;
- if (updateDotGraph(m_startNode,
- DotNode::Dependency,
- absBaseName,
- graphFormat,
- "", // lrRank
- FALSE, // renderParents
- m_inverse, // backArrows
- m_startNode->label()
- ) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
- if (graphFormat==GOF_BITMAP)
- {
- // run dot to create a bitmap image
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
- }
- else if (graphFormat==GOF_EPS)
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
- }
- }
- Doxygen::indexList->addImageFile(baseName+"."+imgExt);
-
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- out << "<para>" << endl;
- out << " <informalfigure>" << endl;
- out << " <mediaobject>" << endl;
- out << " <imageobject>" << endl;
- out << " <imagedata";
- out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- out << "</imagedata>" << endl;
- out << " </imageobject>" << endl;
- out << " </mediaobject>" << endl;
- out << " </informalfigure>" << endl;
- out << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && generateImageMap)
- {
- if (imgExt=="svg") // Scalable vector graphics
- {
- out << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- out << "<!-- SVG " << mapId << " -->" << endl;
- }
- out << "</div>" << endl;
- }
- else // bitmap graphics
- {
- out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." << imgExt << "\" border=\"0\" usemap=\"#" << mapName << "\" alt=\"\"/>";
- out << "</div>" << endl;
-
- QCString absMapName = absBaseName+".map";
- if (regenerate || !insertMapFile(out,absMapName,relPath,mapName))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- FALSE,QCString(),mapName);
- out << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- }
- else if (graphFormat==GOF_EPS) // encapsulated postscript
- {
- if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
- out << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-bool DotInclDepGraph::isTrivial() const
-{
- return m_startNode->m_children==0;
-}
-
-bool DotInclDepGraph::isTooBig() const
-{
- static int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0;
- return numNodes>=maxNodes;
-}
-
-void DotInclDepGraph::writeXML(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeXML(t,FALSE);
- }
-}
-
-void DotInclDepGraph::writeDocbook(FTextStream &t)
-{
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *node;
- for (;(node=dni.current());++dni)
- {
- node->writeDocbook(t,FALSE);
- }
-}
-
-//-------------------------------------------------------------
-
-void DotCallGraph::buildGraph(DotNode *n,const MemberDef *md,int distance)
-{
- MemberSDict *refs = m_inverse ? md->getReferencedByMembers() : md->getReferencesMembers();
- if (refs)
- {
- refs->sort();
- MemberSDict::Iterator mri(*refs);
- MemberDef *rmd;
- for (;(rmd=mri.current());++mri)
- {
- if (rmd->showInCallGraph())
- {
- QCString uniqueId;
- uniqueId=rmd->getReference()+"$"+
- rmd->getOutputFileBase()+"#"+rmd->anchor();
- DotNode *bn = m_usedNodes->find(uniqueId);
- if (bn) // file is already a node in the graph
- {
- n->addChild(bn,0,0,0);
- bn->addParent(n);
- bn->setDistance(distance);
- }
- else
- {
- QCString name;
- if (Config_getBool(HIDE_SCOPE_NAMES))
- {
- name = rmd->getOuterScope()==m_scope ?
- rmd->name() : rmd->qualifiedName();
- }
- else
- {
- name = rmd->qualifiedName();
- }
- QCString tooltip = rmd->briefDescriptionAsTooltip();
- bn = new DotNode(
- getNextNodeNumber(),
- linkToText(rmd->getLanguage(),name,FALSE),
- tooltip,
- uniqueId,
- 0 //distance
- );
- n->addChild(bn,0,0,0);
- bn->addParent(n);
- bn->setDistance(distance);
- m_usedNodes->insert(uniqueId,bn);
-
- buildGraph(bn,rmd,distance+1);
- }
- }
- }
- }
-}
-
-void DotCallGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes)
-{
- while (queue.count()>0 && maxNodes>0)
- {
- static int maxDistance = Config_getInt(MAX_DOT_GRAPH_DEPTH);
- DotNode *n = queue.take(0);
- if (!n->isVisible() && n->distance()<=maxDistance) // not yet processed
- {
- n->markAsVisible();
- maxNodes--;
- // add direct children
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- queue.append(dn);
- }
- }
- }
- }
-}
-
-void DotCallGraph::determineTruncatedNodes(QList<DotNode> &queue)
-{
- while (queue.count()>0)
- {
- DotNode *n = queue.take(0);
- if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
- {
- bool truncated = FALSE;
- if (n->m_children)
- {
- QListIterator<DotNode> li(*n->m_children);
- DotNode *dn;
- for (li.toFirst();(dn=li.current());++li)
- {
- if (!dn->isVisible())
- truncated = TRUE;
- else
- queue.append(dn);
- }
- }
- n->markAsTruncated(truncated);
- }
- }
-}
-
-DotCallGraph::DotCallGraph(const MemberDef *md,bool inverse)
-{
- m_inverse = inverse;
- m_diskName = md->getOutputFileBase()+"_"+md->anchor();
- m_scope = md->getOuterScope();
- QCString uniqueId;
- uniqueId = md->getReference()+"$"+
- md->getOutputFileBase()+"#"+md->anchor();
- QCString name;
- if (Config_getBool(HIDE_SCOPE_NAMES))
- {
- name = md->name();
- }
- else
- {
- name = md->qualifiedName();
- }
- QCString tooltip = md->briefDescriptionAsTooltip();
- m_startNode = new DotNode(getNextNodeNumber(),
- linkToText(md->getLanguage(),name,FALSE),
- tooltip,
- uniqueId.data(),
- TRUE // root node
- );
- m_startNode->setDistance(0);
- m_usedNodes = new QDict<DotNode>(1009);
- m_usedNodes->insert(uniqueId,m_startNode);
- buildGraph(m_startNode,md,1);
-
- static int nodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int maxNodes = nodes;
- //int directChildNodes = 1;
- //if (m_startNode->m_children!=0)
- // directChildNodes+=m_startNode->m_children->count();
- //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
- QList<DotNode> openNodeQueue;
- openNodeQueue.append(m_startNode);
- determineVisibleNodes(openNodeQueue,maxNodes);
- openNodeQueue.clear();
- openNodeQueue.append(m_startNode);
- determineTruncatedNodes(openNodeQueue);
-}
-
-DotCallGraph::~DotCallGraph()
-{
- deleteNodes(m_startNode);
- delete m_usedNodes;
-}
-
-QCString DotCallGraph::writeGraph(FTextStream &out, GraphOutputFormat graphFormat,
- EmbeddedOutputFormat textFormat,
- const char *path,const char *fileName,
- const char *relPath,bool generateImageMap,int
- graphId) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QCString baseName = m_diskName + (m_inverse ? "_icgraph" : "_cgraph");
- QCString mapName = baseName;
-
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString absBaseName = d.absPath().utf8()+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- QCString absImgName = absBaseName+"."+imgExt;
-
- bool regenerate = FALSE;
-
- if (updateDotGraph(m_startNode,
- DotNode::CallGraph,
- absBaseName,
- graphFormat,
- m_inverse ? "RL" : "LR", // lrRank
- FALSE, // renderParents
- m_inverse, // backArrows
- m_startNode->label()
- ) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
- if (graphFormat==GOF_BITMAP)
- {
- // run dot to create a bitmap image
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
-
- }
- else if (graphFormat==GOF_EPS)
- {
- // run dot to create a .eps image
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
-
- }
- }
- Doxygen::indexList->addImageFile(baseName+"."+imgExt);
-
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- out << "<para>" << endl;
- out << " <informalfigure>" << endl;
- out << " <mediaobject>" << endl;
- out << " <imageobject>" << endl;
- out << " <imagedata";
- out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- out << "</imagedata>" << endl;
- out << " </imageobject>" << endl;
- out << " </mediaobject>" << endl;
- out << " </informalfigure>" << endl;
- out << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && generateImageMap)
- {
- if (imgExt=="svg") // Scalable vector graphics
- {
- out << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- out << "<!-- SVG " << mapId << " -->" << endl;
- }
- out << "</div>" << endl;
- }
- else // bitmap graphics
- {
- out << "<div class=\"center\"><img src=\"" << relPath << baseName << "."
- << imgExt << "\" border=\"0\" usemap=\"#"
- << mapName << "\" alt=\"";
- out << "\"/>";
- out << "</div>" << endl;
-
- if (regenerate || !insertMapFile(out,absMapName,relPath,mapName))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- FALSE,QCString(),mapName);
- out << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- }
- else if (graphFormat==GOF_EPS) // encapsulated postscript
- {
- if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
- out << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-bool DotCallGraph::isTrivial() const
-{
- return m_startNode->m_children==0;
-}
-
-bool DotCallGraph::isTooBig() const
-{
- static int maxNodes = Config_getInt(DOT_GRAPH_MAX_NODES);
- int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0;
- return numNodes>=maxNodes;
-}
-
-//-------------------------------------------------------------
-static void writeDotDirDepGraph(FTextStream &t,const DirDef *dd,bool linkRelations);
-
-DotDirDeps::DotDirDeps(const DirDef *dir) : m_dir(dir)
-{
-}
-
-DotDirDeps::~DotDirDeps()
-{
-}
-
-QCString DotDirDeps::writeGraph(FTextStream &out,
- GraphOutputFormat graphFormat,
- EmbeddedOutputFormat textFormat,
- const char *path,
- const char *fileName,
- const char *relPath,
- bool generateImageMap,
- int graphId,
- bool linkRelations) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QCString baseName=m_dir->getOutputFileBase()+"_dep";
- QCString mapName=escapeCharsInString(baseName,FALSE);
-
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString absBaseName = d.absPath().utf8()+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- QCString absImgName = absBaseName+"."+imgExt;
-
- // compute md5 checksum of the graph were are about to generate
- QGString theGraph;
- FTextStream md5stream(&theGraph);
- //m_dir->writeDepGraph(md5stream);
- writeDotDirDepGraph(md5stream,m_dir,linkRelations);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),33);
- bool regenerate=FALSE;
- if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
-
- QFile f(absDotName);
- if (!f.open(IO_WriteOnly))
- {
- err("Cannot create file %s.dot for writing!\n",baseName.data());
- }
- FTextStream t(&f);
- t << theGraph.data();
- f.close();
-
- if (graphFormat==GOF_BITMAP)
- {
- // run dot to create a bitmap image
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
- }
- else if (graphFormat==GOF_EPS)
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
- }
- }
- Doxygen::indexList->addImageFile(baseName+"."+imgExt);
-
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- out << "<para>" << endl;
- out << " <informalfigure>" << endl;
- out << " <mediaobject>" << endl;
- out << " <imageobject>" << endl;
- out << " <imagedata";
- out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- out << "</imagedata>" << endl;
- out << " </imageobject>" << endl;
- out << " </mediaobject>" << endl;
- out << " </informalfigure>" << endl;
- out << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && generateImageMap)
- {
- if (imgExt=="svg") // Scalable vector graphics
- {
- out << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- out << "<!-- SVG " << mapId << " -->" << endl;
- }
- out << "</div>" << endl;
- }
- else // bitmap graphics
- {
- out << "<div class=\"center\"><img src=\"" << relPath << baseName << "."
- << imgExt << "\" border=\"0\" usemap=\"#"
- << mapName << "\" alt=\"";
- out << convertToXML(m_dir->displayName());
- out << "\"/>";
- out << "</div>" << endl;
-
- if (regenerate || !insertMapFile(out,absMapName,relPath,mapName))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- TRUE,QCString(),mapName);
- out << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- }
- else if (graphFormat==GOF_EPS)
- {
- if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
- out << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-bool DotDirDeps::isTrivial() const
-{
- return m_dir->depGraphIsTrivial();
-}
-
-//-------------------------------------------------------------
+ friend void generateGraphLegend(const char* path);
+};
void generateGraphLegend(const char *path)
{
QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
+ GraphLegendDotGraph dg;
+ FTextStream ts;
+ dg.writeGraph(ts, GOF_BITMAP, EOF_Html, path, "", "", FALSE, 0);
- QGString theGraph;
- FTextStream md5stream(&theGraph);
- writeGraphHeader(md5stream,theTranslator->trLegendTitle());
- md5stream << " Node9 [shape=\"box\",label=\"Inherited\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",fillcolor=\"grey75\",style=\"filled\" fontcolor=\"black\"];\n";
- md5stream << " Node10 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node10 [shape=\"box\",label=\"PublicBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPublicBase" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node11 -> Node10 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node11 [shape=\"box\",label=\"Truncated\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"red\",URL=\"$classTruncated" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node13 -> Node9 [dir=\"back\",color=\"darkgreen\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node13 [shape=\"box\",label=\"ProtectedBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classProtectedBase" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node14 -> Node9 [dir=\"back\",color=\"firebrick4\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node14 [shape=\"box\",label=\"PrivateBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPrivateBase" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node15 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node15 [shape=\"box\",label=\"Undocumented\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"grey75\"];\n";
- md5stream << " Node16 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node16 [shape=\"box\",label=\"Templ< int >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node17 -> Node16 [dir=\"back\",color=\"orange\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"< int >\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node17 [shape=\"box\",label=\"Templ< T >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
- md5stream << " Node18 -> Node9 [dir=\"back\",color=\"darkorchid3\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"m_usedClass\",fontname=\"" << FONTNAME << "\"];\n";
- md5stream << " Node18 [shape=\"box\",label=\"Used\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classUsed" << Doxygen::htmlFileExtension << "\"];\n";
- writeGraphFooter(md5stream);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),33);
- QCString absBaseName = (QCString)path+"/graph_legend";
- QCString absDotName = absBaseName+".dot";
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString imgName = "graph_legend."+imgExt;
- QCString absImgName = absBaseName+"."+imgExt;
- if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
- !checkDeliverables(absImgName))
- {
- QFile dotFile(absDotName);
- if (!dotFile.open(IO_WriteOnly))
- {
- err("Could not open file %s for writing\n",dotFile.name().data());
- return;
- }
-
- FTextStream dotText(&dotFile);
- dotText << theGraph;
- dotFile.close();
-
- // run dot to generate the a bitmap image from the graph
-
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
- dotRun->addJob(imgFmt,absImgName);
- DotManager::instance()->addRun(dotRun);
- }
- else
- {
- removeDotGraph(absDotName);
- }
- Doxygen::indexList->addImageFile(imgName);
-
- if (imgExt=="svg")
+ if (getDotImageExtension()=="svg")
{
DotManager::instance()->addSVGObject(
- absBaseName+Config_getString(HTML_FILE_EXTENSION),
+ dg.absBaseName()+Config_getString(HTML_FILE_EXTENSION),
"graph_legend",
- absImgName,QCString());
+ dg.absImgName(),QCString());
}
}
@@ -4238,19 +493,20 @@ void writeDotGraphFromFile(const char *inFile,const char *outDir,
}
QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
QCString imgName = (QCString)outFile+"."+imgExt;
QCString absImgName = d.absPath().utf8()+"/"+imgName;
QCString absOutFile = d.absPath().utf8()+"/"+outFile;
- DotRunner dotRun(inFile,d.absPath().data(),FALSE,absImgName);
+ DotRunner dotRun(inFile, QCString());
if (format==GOF_BITMAP)
- dotRun.addJob(imgFmt,absImgName);
+ {
+ dotRun.addJob(Config_getEnum(DOT_IMAGE_FORMAT),absImgName);
+ }
else // format==GOF_EPS
{
if (Config_getBool(USE_PDFLATEX))
{
- dotRun.addJob("pdf",absOutFile+".pdf",absOutFile);
+ dotRun.addJob("pdf",absOutFile+".pdf");
}
else
{
@@ -4264,13 +520,10 @@ void writeDotGraphFromFile(const char *inFile,const char *outDir,
return;
}
- if (format==GOF_BITMAP) checkDotResult(getDotImageExtension(),absImgName);
-
Doxygen::indexList->addImageFile(imgName);
}
-
/*! Writes user defined image map to the output.
* \param t text stream to write to
* \param inFile just the basename part of the filename
@@ -4294,11 +547,10 @@ void writeDotImageMapFromFile(FTextStream &t,
QCString mapName = baseName+".map";
QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
QCString imgName = baseName+"."+imgExt;
QCString absOutFile = d.absPath().utf8()+"/"+mapName;
- DotRunner dotRun(inFile,d.absPath().data(),FALSE);
+ DotRunner dotRun(inFile, QCString());
dotRun.addJob(MAP_CMD,absOutFile);
dotRun.preventCleanUp();
if (!dotRun.run())
@@ -4333,612 +585,3 @@ void writeDotImageMapFromFile(FTextStream &t,
}
d.remove(absOutFile);
}
-
-//-------------------------------------------------------------
-
-DotGroupCollaboration::DotGroupCollaboration(const GroupDef* gd)
-{
- QCString tmp_url = gd->getReference()+"$"+gd->getOutputFileBase();
- m_usedNodes = new QDict<DotNode>(1009);
- QCString tooltip = gd->briefDescriptionAsTooltip();
- m_rootNode = new DotNode(getNextNodeNumber(), gd->groupTitle(), tooltip, tmp_url, TRUE );
- m_rootNode->markAsVisible();
- m_usedNodes->insert(gd->name(), m_rootNode );
- m_edges.setAutoDelete(TRUE);
-
- m_diskName = gd->getOutputFileBase();
-
- buildGraph( gd );
-}
-
-DotGroupCollaboration::~DotGroupCollaboration()
-{
- delete m_usedNodes;
-}
-
-void DotGroupCollaboration::buildGraph(const GroupDef* gd)
-{
- QCString tmp_url;
- //===========================
- // hierarchy.
-
- // Write parents
- const GroupList *groups = gd->partOfGroups();
- if ( groups )
- {
- GroupListIterator gli(*groups);
- const GroupDef *d;
- for (gli.toFirst();(d=gli.current());++gli)
- {
- DotNode* nnode = m_usedNodes->find(d->name());
- if ( !nnode )
- { // add node
- tmp_url = d->getReference()+"$"+d->getOutputFileBase();
- QCString tooltip = d->briefDescriptionAsTooltip();
- nnode = new DotNode(getNextNodeNumber(), d->groupTitle(), tooltip, tmp_url );
- nnode->markAsVisible();
- m_usedNodes->insert(d->name(), nnode );
- }
- tmp_url = "";
- addEdge( nnode, m_rootNode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
- }
- }
-
- // Add subgroups
- if ( gd->getSubGroups() && gd->getSubGroups()->count() )
- {
- QListIterator<GroupDef> defli(*gd->getSubGroups());
- const GroupDef *def;
- for (;(def=defli.current());++defli)
- {
- DotNode* nnode = m_usedNodes->find(def->name());
- if ( !nnode )
- { // add node
- tmp_url = def->getReference()+"$"+def->getOutputFileBase();
- QCString tooltip = def->briefDescriptionAsTooltip();
- nnode = new DotNode(getNextNodeNumber(), def->groupTitle(), tooltip, tmp_url );
- nnode->markAsVisible();
- m_usedNodes->insert(def->name(), nnode );
- }
- tmp_url = "";
- addEdge( m_rootNode, nnode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
- }
- }
-
- //=======================
- // Write collaboration
-
- // Add members
- addMemberList( gd->getMemberList(MemberListType_allMembersList) );
-
- // Add classes
- if ( gd->getClasses() && gd->getClasses()->count() )
- {
- ClassSDict::Iterator defli(*gd->getClasses());
- ClassDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- if (!def->anchor().isEmpty())
- {
- tmp_url+="#"+def->anchor();
- }
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tclass );
- }
- }
-
- // Add namespaces
- if ( gd->getNamespaces() && gd->getNamespaces()->count() )
- {
- NamespaceSDict::Iterator defli(*gd->getNamespaces());
- NamespaceDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tnamespace );
- }
- }
-
- // Add files
- if ( gd->getFiles() && gd->getFiles()->count() )
- {
- QListIterator<FileDef> defli(*gd->getFiles());
- const FileDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tfile );
- }
- }
-
- // Add pages
- if ( gd->getPages() && gd->getPages()->count() )
- {
- PageSDict::Iterator defli(*gd->getPages());
- PageDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tpages );
- }
- }
-
- // Add directories
- if ( gd->getDirs() && gd->getDirs()->count() )
- {
- QListIterator<DirDef> defli(*gd->getDirs());
- const DirDef *def;
- for (;(def=defli.current());++defli)
- {
- tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tdir );
- }
- }
-}
-
-void DotGroupCollaboration::addMemberList( MemberList* ml )
-{
- if ( !( ml && ml->count()) ) return;
- MemberListIterator defli(*ml);
- MemberDef *def;
- for (;(def=defli.current());++defli)
- {
- QCString tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension
- +"#"+def->anchor();
- addCollaborationMember( def, tmp_url, DotGroupCollaboration::tmember );
- }
-}
-
-DotGroupCollaboration::Edge* DotGroupCollaboration::addEdge(
- DotNode* _pNStart, DotNode* _pNEnd, EdgeType _eType,
- const QCString& _label, const QCString& _url )
-{
- // search a existing link.
- QListIterator<Edge> lli(m_edges);
- Edge* newEdge = 0;
- for ( lli.toFirst(); (newEdge=lli.current()); ++lli)
- {
- if ( newEdge->pNStart==_pNStart &&
- newEdge->pNEnd==_pNEnd &&
- newEdge->eType==_eType
- )
- { // edge already found
- break;
- }
- }
- if ( newEdge==0 ) // new link
- {
- newEdge = new Edge(_pNStart,_pNEnd,_eType);
- m_edges.append( newEdge );
- }
-
- if (!_label.isEmpty())
- {
- newEdge->links.append(new Link(_label,_url));
- }
-
- return newEdge;
-}
-
-void DotGroupCollaboration::addCollaborationMember(
- const Definition* def, QCString& url, EdgeType eType )
-{
- // Create group nodes
- if ( !def->partOfGroups() )
- return;
- GroupListIterator gli(*def->partOfGroups());
- GroupDef *d;
- QCString tmp_str;
- for (;(d=gli.current());++gli)
- {
- DotNode* nnode = m_usedNodes->find(d->name());
- if ( nnode != m_rootNode )
- {
- if ( nnode==0 )
- { // add node
- tmp_str = d->getReference()+"$"+d->getOutputFileBase();
- QCString tooltip = d->briefDescriptionAsTooltip();
- nnode = new DotNode(getNextNodeNumber(), d->groupTitle(), tooltip, tmp_str );
- nnode->markAsVisible();
- m_usedNodes->insert(d->name(), nnode );
- }
- tmp_str = def->qualifiedName();
- addEdge( m_rootNode, nnode, eType, tmp_str, url );
- }
- }
-}
-
-
-QCString DotGroupCollaboration::writeGraph( FTextStream &t,
- GraphOutputFormat graphFormat, EmbeddedOutputFormat textFormat,
- const char *path, const char *fileName, const char *relPath,
- bool writeImageMap,int graphId) const
-{
- QDir d(path);
- // store the original directory
- if (!d.exists())
- {
- err("Output dir %s does not exist!\n",path); exit(1);
- }
- static bool usePDFLatex = Config_getBool(USE_PDFLATEX);
-
- QGString theGraph;
- FTextStream md5stream(&theGraph);
- writeGraphHeader(md5stream,m_rootNode->label());
-
- // clean write flags
- QDictIterator<DotNode> dni(*m_usedNodes);
- DotNode *pn;
- for (dni.toFirst();(pn=dni.current());++dni)
- {
- pn->clearWriteFlag();
- }
-
- // write other nodes.
- for (dni.toFirst();(pn=dni.current());++dni)
- {
- pn->write(md5stream,DotNode::Inheritance,graphFormat,TRUE,FALSE,FALSE);
- }
-
- // write edges
- QListIterator<Edge> eli(m_edges);
- Edge* edge;
- for (eli.toFirst();(edge=eli.current());++eli)
- {
- edge->write( md5stream );
- }
-
- writeGraphFooter(md5stream);
- uchar md5_sig[16];
- QCString sigStr(33);
- MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
- MD5SigToString(md5_sig,sigStr.rawData(),33);
- QCString imgExt = getDotImageExtension();
- QCString imgFmt = Config_getEnum(DOT_IMAGE_FORMAT);
- QCString baseName = m_diskName;
- QCString imgName = baseName+"."+imgExt;
- QCString absPath = d.absPath().data();
- QCString absBaseName = absPath+"/"+baseName;
- QCString absDotName = absBaseName+".dot";
- QCString absImgName = absBaseName+"."+imgExt;
- QCString absMapName = absBaseName+".map";
- QCString absPdfName = absBaseName+".pdf";
- QCString absEpsName = absBaseName+".eps";
- bool regenerate=FALSE;
- if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
- !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
- usePDFLatex ? absPdfName : absEpsName,
- graphFormat==GOF_BITMAP /*&& generateImageMap*/ ? absMapName : QCString())
- )
- {
- regenerate=TRUE;
-
- QFile dotfile(absDotName);
- if (dotfile.open(IO_WriteOnly))
- {
- FTextStream tdot(&dotfile);
- tdot << theGraph;
- dotfile.close();
- }
-
- if (graphFormat==GOF_BITMAP) // run dot to create a bitmap image
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- dotRun->addJob(imgFmt,absImgName);
- if (writeImageMap) dotRun->addJob(MAP_CMD,absMapName);
- DotManager::instance()->addRun(dotRun);
-
- }
- else if (graphFormat==GOF_EPS)
- {
- DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
- if (usePDFLatex)
- {
- dotRun->addJob("pdf",absPdfName,absBaseName);
- }
- else
- {
- dotRun->addJob("ps",absEpsName);
- }
- DotManager::instance()->addRun(dotRun);
- }
-
- }
- if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
- {
- t << "<para>" << endl;
- t << " <informalfigure>" << endl;
- t << " <mediaobject>" << endl;
- t << " <imageobject>" << endl;
- t << " <imagedata";
- t << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"0\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
- t << "</imagedata>" << endl;
- t << " </imageobject>" << endl;
- t << " </mediaobject>" << endl;
- t << " </informalfigure>" << endl;
- t << "</para>" << endl;
- }
- else if (graphFormat==GOF_BITMAP && writeImageMap)
- {
- QCString mapLabel = escapeCharsInString(baseName,FALSE);
- t << "<center><table><tr><td>";
-
- if (imgExt=="svg")
- {
- t << "<div class=\"center\">";
- if (regenerate || !writeSVGFigureLink(t,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
- {
- if (regenerate)
- {
- DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
- }
- int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
- t << "<!-- SVG " << mapId << " -->" << endl;
- }
- t << "</div>" << endl;
- }
- else
- {
- t << "<img src=\"" << relPath << imgName
- << "\" border=\"0\" alt=\"\" usemap=\"#"
- << mapLabel << "\"/>" << endl;
- if (regenerate || !insertMapFile(t,absMapName,relPath,mapLabel))
- {
- int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
- FALSE,QCString(),mapLabel);
- t << "<!-- MAP " << mapId << " -->" << endl;
- }
- }
- t << "</td></tr></table></center>" << endl;
- }
- else if (graphFormat==GOF_EPS)
- {
- if (regenerate || !writeVecGfxFigure(t,baseName,absBaseName))
- {
- int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
- t << endl << "% FIG " << figId << endl;
- }
- }
- if (!regenerate) removeDotGraph(absDotName);
-
- return baseName;
-}
-
-void DotGroupCollaboration::Edge::write( FTextStream &t ) const
-{
- const char* linkTypeColor[] = {
- "darkorchid3"
- ,"orange"
- ,"blueviolet"
- ,"darkgreen"
- ,"firebrick4"
- ,"grey75"
- ,"midnightblue"
- };
- QCString arrowStyle = "dir=\"none\", style=\"dashed\"";
- t << " Node" << pNStart->number();
- t << "->";
- t << "Node" << pNEnd->number();
-
- t << " [shape=plaintext";
- if (links.count()>0) // there are links
- {
- t << ", ";
- // HTML-like edge labels crash on my Mac with Graphviz 2.0! and
- // are not supported by older version of dot.
- //
- //t << label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">";
- //QListIterator<Link> lli(links);
- //Link *link;
- //for( lli.toFirst(); (link=lli.current()); ++lli)
- //{
- // t << "<TR><TD";
- // if ( !link->url.isEmpty() )
- // t << " HREF=\"" << link->url << "\"";
- // t << ">" << link->label << "</TD></TR>";
- //}
- //t << "</TABLE>>";
-
- t << "label=\"";
- QListIterator<Link> lli(links);
- Link *link;
- bool first=TRUE;
- int count=0;
- const int maxLabels = 10;
- for( lli.toFirst(); (link=lli.current()) && count<maxLabels; ++lli,++count)
- {
- if (first) first=FALSE; else t << "\\n";
- t << convertLabel(link->label);
- }
- if (count==maxLabels) t << "\\n...";
- t << "\"";
-
- }
- switch( eType )
- {
- case thierarchy:
- arrowStyle = "dir=\"back\", style=\"solid\"";
- break;
- default:
- t << ", color=\"" << linkTypeColor[(int)eType] << "\"";
- break;
- }
- t << ", " << arrowStyle;
- t << "];" << endl;
-}
-
-bool DotGroupCollaboration::isTrivial() const
-{
- return m_usedNodes->count() <= 1;
-}
-
-void DotGroupCollaboration::writeGraphHeader(FTextStream &t,
- const QCString &title) const
-{
- t << "digraph ";
- if (title.isEmpty())
- {
- t << "\"Dot Graph\"";
- }
- else
- {
- t << "\"" << convertLabel(title) << "\"";
- }
- t << endl;
- t << "{" << endl;
- if (Config_getBool(DOT_TRANSPARENT))
- {
- t << " bgcolor=\"transparent\";" << endl;
- }
- t << " edge [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\","
- "labelfontname=\"" << FONTNAME << "\",labelfontsize=\"" << FONTSIZE << "\"];\n";
- t << " node [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\",shape=box];\n";
- t << " rankdir=LR;\n";
-}
-
-void writeDotDirDepGraph(FTextStream &t,const DirDef *dd,bool linkRelations)
-{
- t << "digraph \"" << dd->displayName() << "\" {\n";
- if (Config_getBool(DOT_TRANSPARENT))
- {
- t << " bgcolor=transparent;\n";
- }
- t << " compound=true\n";
- t << " node [ fontsize=\"" << FONTSIZE << "\", fontname=\"" << FONTNAME << "\"];\n";
- t << " edge [ labelfontsize=\"" << FONTSIZE << "\", labelfontname=\"" << FONTNAME << "\"];\n";
-
- QDict<DirDef> dirsInGraph(257);
-
- dirsInGraph.insert(dd->getOutputFileBase(),dd);
- if (dd->parent())
- {
- t << " subgraph cluster" << dd->parent()->getOutputFileBase() << " {\n";
- t << " graph [ bgcolor=\"#ddddee\", pencolor=\"black\", label=\""
- << dd->parent()->shortName()
- << "\" fontname=\"" << FONTNAME << "\", fontsize=\"" << FONTSIZE << "\", URL=\"";
- t << dd->parent()->getOutputFileBase() << Doxygen::htmlFileExtension;
- t << "\"]\n";
- }
- if (dd->isCluster())
- {
- t << " subgraph cluster" << dd->getOutputFileBase() << " {\n";
- t << " graph [ bgcolor=\"#eeeeff\", pencolor=\"black\", label=\"\""
- << " URL=\"" << dd->getOutputFileBase() << Doxygen::htmlFileExtension
- << "\"];\n";
- t << " " << dd->getOutputFileBase() << " [shape=plaintext label=\""
- << dd->shortName() << "\"];\n";
-
- // add nodes for sub directories
- QListIterator<DirDef> sdi(dd->subDirs());
- const DirDef *sdir;
- for (sdi.toFirst();(sdir=sdi.current());++sdi)
- {
- t << " " << sdir->getOutputFileBase() << " [shape=box label=\""
- << sdir->shortName() << "\"";
- if (sdir->isCluster())
- {
- t << " color=\"red\"";
- }
- else
- {
- t << " color=\"black\"";
- }
- t << " fillcolor=\"white\" style=\"filled\"";
- t << " URL=\"" << sdir->getOutputFileBase()
- << Doxygen::htmlFileExtension << "\"";
- t << "];\n";
- dirsInGraph.insert(sdir->getOutputFileBase(),sdir);
- }
- t << " }\n";
- }
- else
- {
- t << " " << dd->getOutputFileBase() << " [shape=box, label=\""
- << dd->shortName() << "\", style=\"filled\", fillcolor=\"#eeeeff\","
- << " pencolor=\"black\", URL=\"" << dd->getOutputFileBase()
- << Doxygen::htmlFileExtension << "\"];\n";
- }
- if (dd->parent())
- {
- t << " }\n";
- }
-
- // add nodes for other used directories
- QDictIterator<UsedDir> udi(*dd->usedDirs());
- UsedDir *udir;
- //printf("*** For dir %s\n",shortName().data());
- for (udi.toFirst();(udir=udi.current());++udi)
- // for each used dir (=directly used or a parent of a directly used dir)
- {
- const DirDef *usedDir=udir->dir();
- const DirDef *dir=dd;
- while (dir)
- {
- //printf("*** check relation %s->%s same_parent=%d !%s->isParentOf(%s)=%d\n",
- // dir->shortName().data(),usedDir->shortName().data(),
- // dir->parent()==usedDir->parent(),
- // usedDir->shortName().data(),
- // shortName().data(),
- // !usedDir->isParentOf(this)
- // );
- if (dir!=usedDir && dir->parent()==usedDir->parent() &&
- !usedDir->isParentOf(dd))
- // include if both have the same parent (or no parent)
- {
- t << " " << usedDir->getOutputFileBase() << " [shape=box label=\""
- << usedDir->shortName() << "\"";
- if (usedDir->isCluster())
- {
- if (!Config_getBool(DOT_TRANSPARENT))
- {
- t << " fillcolor=\"white\" style=\"filled\"";
- }
- t << " color=\"red\"";
- }
- t << " URL=\"" << usedDir->getOutputFileBase()
- << Doxygen::htmlFileExtension << "\"];\n";
- dirsInGraph.insert(usedDir->getOutputFileBase(),usedDir);
- break;
- }
- dir=dir->parent();
- }
- }
-
- // add relations between all selected directories
- const DirDef *dir;
- QDictIterator<DirDef> di(dirsInGraph);
- for (di.toFirst();(dir=di.current());++di) // foreach dir in the graph
- {
- QDictIterator<UsedDir> udi(*dir->usedDirs());
- UsedDir *udir;
- for (udi.toFirst();(udir=udi.current());++udi) // foreach used dir
- {
- const DirDef *usedDir=udir->dir();
- if ((dir!=dd || !udir->inherited()) && // only show direct dependendies for this dir
- (usedDir!=dd || !udir->inherited()) && // only show direct dependendies for this dir
- !usedDir->isParentOf(dir) && // don't point to own parent
- dirsInGraph.find(usedDir->getOutputFileBase())) // only point to nodes that are in the graph
- {
- QCString relationName;
- relationName.sprintf("dir_%06d_%06d",dir->dirCount(),usedDir->dirCount());
- if (Doxygen::dirRelations.find(relationName)==0)
- {
- // new relation
- Doxygen::dirRelations.append(relationName,
- new DirRelation(relationName,dir,udir));
- }
- int nrefs = udir->filePairs().count();
- t << " " << dir->getOutputFileBase() << "->"
- << usedDir->getOutputFileBase();
- t << " [headlabel=\"" << nrefs << "\", labeldistance=1.5";
- if (linkRelations)
- {
- t << " headhref=\"" << relationName << Doxygen::htmlFileExtension << "\"";
- }
- t << "];\n";
- }
- }
- }
-
- t << "}\n";
-}