dot.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  *
3  *
4  *
5  *
6  * Copyright (C) 1997-2015 by Dimitri van Heesch.
7  *
8  * Permission to use, copy, modify, and distribute this software and its
9  * documentation under the terms of the GNU General Public License is hereby
10  * granted. No representations are made about the suitability of this software
11  * for any purpose. It is provided "as is" without express or implied warranty.
12  * See the GNU General Public License for more details.
13  *
14  * Documents produced by Doxygen are derivative works derived from the
15  * input used in their production; they are not affected by this license.
16  *
17  */
18 
19 #include <stdlib.h>
20 
21 #include <qdir.h>
22 #include <qfile.h>
23 #include <qqueue.h>
24 #include <qthread.h>
25 #include <qmutex.h>
26 #include <qwaitcondition.h>
27 
28 #include "dot.h"
29 #include "doxygen.h"
30 #include "message.h"
31 #include "util.h"
32 #include "config.h"
33 #include "language.h"
34 #include "defargs.h"
35 #include "docparser.h"
36 #include "debug.h"
37 #include "pagedef.h"
38 #include "portable.h"
39 #include "dirdef.h"
40 #include "vhdldocgen.h"
41 #include "ftextstream.h"
42 #include "md5.h"
43 #include "memberlist.h"
44 #include "groupdef.h"
45 #include "classlist.h"
46 #include "filename.h"
47 #include "namespacedef.h"
48 #include "memberdef.h"
49 #include "membergroup.h"
50 
51 #define MAP_CMD "cmapx"
52 
53 //#define FONTNAME "Helvetica"
54 #define FONTNAME getDotFontName()
55 #define FONTSIZE getDotFontSize()
56 
57 //--------------------------------------------------------------------
58 
59 static const char svgZoomHeader[] =
60 "<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"
61 "<style type=\"text/css\"><![CDATA[\n"
62 ".edge:hover path { stroke: red; }\n"
63 ".edge:hover polygon { stroke: red; fill: red; }\n"
64 "]]></style>\n"
65 "<script type=\"text/javascript\"><![CDATA[\n"
66 "var edges = document.getElementsByTagName('g');\n"
67 "if (edges && edges.length) {\n"
68 " for (var i=0;i<edges.length;i++) {\n"
69 " if (edges[i].id.substr(0,4)=='edge') {\n"
70 " edges[i].setAttribute('class','edge');\n"
71 " }\n"
72 " }\n"
73 "}\n"
74 "]]></script>\n"
75 " <defs>\n"
76 " <circle id=\"rim\" cx=\"0\" cy=\"0\" r=\"7\"/>\n"
77 " <circle id=\"rim2\" cx=\"0\" cy=\"0\" r=\"3.5\"/>\n"
78 " <g id=\"zoomPlus\">\n"
79 " <use xlink:href=\"#rim\" fill=\"#404040\">\n"
80 " <set attributeName=\"fill\" to=\"#808080\" begin=\"zoomplus.mouseover\" end=\"zoomplus.mouseout\"/>\n"
81 " </use>\n"
82 " <path d=\"M-4,0h8M0,-4v8\" fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" pointer-events=\"none\"/>\n"
83 " </g>\n"
84 " <g id=\"zoomMin\">\n"
85 " <use xlink:href=\"#rim\" fill=\"#404040\">\n"
86 " <set attributeName=\"fill\" to=\"#808080\" begin=\"zoomminus.mouseover\" end=\"zoomminus.mouseout\"/>\n"
87 " </use>\n"
88 " <path d=\"M-4,0h8\" fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" pointer-events=\"none\"/>\n"
89 " </g>\n"
90 " <g id=\"dirArrow\">\n"
91 " <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
92 " </g>\n"
93 " <g id=\"resetDef\">\n"
94 " <use xlink:href=\"#rim2\" fill=\"#404040\">\n"
95 " <set attributeName=\"fill\" to=\"#808080\" begin=\"reset.mouseover\" end=\"reset.mouseout\"/>\n"
96 " </use>\n"
97 " </g>\n"
98 " </defs>\n"
99 "\n"
100 "<script type=\"text/javascript\">\n"
101 ;
102 
103 static const char svgZoomFooter[] =
104 // navigation panel
105 " <g id=\"navigator\" transform=\"translate(0 0)\" fill=\"#404254\">\n"
106 " <rect fill=\"#f2f5e9\" fill-opacity=\"0.5\" stroke=\"#606060\" stroke-width=\".5\" x=\"0\" y=\"0\" width=\"60\" height=\"60\"/>\n"
107 // zoom in
108 " <use id=\"zoomplus\" xlink:href=\"#zoomPlus\" x=\"17\" y=\"9\" onmousedown=\"handleZoom(evt,'in')\"/>\n"
109 // zoom out
110 " <use id=\"zoomminus\" xlink:href=\"#zoomMin\" x=\"42\" y=\"9\" onmousedown=\"handleZoom(evt,'out')\"/>\n"
111 // reset zoom
112 " <use id=\"reset\" xlink:href=\"#resetDef\" x=\"30\" y=\"36\" onmousedown=\"handleReset()\"/>\n"
113 // arrow up
114 " <g id=\"arrowUp\" xlink:href=\"#dirArrow\" transform=\"translate(30 24)\" onmousedown=\"handlePan(0,-1)\">\n"
115 " <use xlink:href=\"#rim\" fill=\"#404040\">\n"
116 " <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowUp.mouseover\" end=\"arrowUp.mouseout\"/>\n"
117 " </use>\n"
118 " <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
119 " </g>\n"
120 // arrow right
121 " <g id=\"arrowRight\" xlink:href=\"#dirArrow\" transform=\"rotate(90) translate(36 -43)\" onmousedown=\"handlePan(1,0)\">\n"
122 " <use xlink:href=\"#rim\" fill=\"#404040\">\n"
123 " <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowRight.mouseover\" end=\"arrowRight.mouseout\"/>\n"
124 " </use>\n"
125 " <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
126 " </g>\n"
127 // arrow down
128 " <g id=\"arrowDown\" xlink:href=\"#dirArrow\" transform=\"rotate(180) translate(-30 -48)\" onmousedown=\"handlePan(0,1)\">\n"
129 " <use xlink:href=\"#rim\" fill=\"#404040\">\n"
130 " <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowDown.mouseover\" end=\"arrowDown.mouseout\"/>\n"
131 " </use>\n"
132 " <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
133 " </g>\n"
134 // arrow left
135 " <g id=\"arrowLeft\" xlink:href=\"#dirArrow\" transform=\"rotate(270) translate(-36 17)\" onmousedown=\"handlePan(-1,0)\">\n"
136 " <use xlink:href=\"#rim\" fill=\"#404040\">\n"
137 " <set attributeName=\"fill\" to=\"#808080\" begin=\"arrowLeft.mouseover\" end=\"arrowLeft.mouseout\"/>\n"
138 " </use>\n"
139 " <path fill=\"none\" stroke=\"white\" stroke-width=\"1.5\" d=\"M0,-3.0v7 M-2.5,-0.5L0,-3.0L2.5,-0.5\"/>\n"
140 " </g>\n"
141 " </g>\n"
142 // link to orginial SVG
143 " <svg viewBox=\"0 0 15 15\" width=\"100%\" height=\"30px\" preserveAspectRatio=\"xMaxYMin meet\">\n"
144 " <g id=\"arrow_out\" transform=\"scale(0.3 0.3)\">\n"
145 " <a xlink:href=\"$orgname\" target=\"_base\">\n"
146 " <rect id=\"button\" ry=\"5\" rx=\"5\" y=\"6\" x=\"6\" height=\"38\" width=\"38\"\n"
147 " fill=\"#f2f5e9\" fill-opacity=\"0.5\" stroke=\"#606060\" stroke-width=\"1.0\"/>\n"
148 " <path id=\"arrow\"\n"
149 " 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"
150 " style=\"fill:#404040;\"/>\n"
151 " </a>\n"
152 " </g>\n"
153 " </svg>\n"
154 "</svg>\n"
155 ;
156 
157 //--------------------------------------------------------------------
158 
159 /*! mapping from protection levels to color names */
160 static const char *normalEdgeColorMap[] =
161 {
162  "midnightblue", // Public
163  "darkgreen", // Protected
164  "firebrick4", // Private
165  "darkorchid3", // "use" relation
166  "grey75", // Undocumented
167  "orange", // template relation
168  "orange" // type constraint
169 };
170 
171 static const char *normalArrowStyleMap[] =
172 {
173  "empty", // Public
174  "empty", // Protected
175  "empty", // Private
176  "open", // "use" relation
177  0, // Undocumented
178  0 // template relation
179 };
180 
181 static const char *normalEdgeStyleMap[] =
182 {
183  "solid", // inheritance
184  "dashed" // usage
185 };
186 
187 static const char *umlEdgeColorMap[] =
188 {
189  "midnightblue", // Public
190  "darkgreen", // Protected
191  "firebrick4", // Private
192  "grey25", // "use" relation
193  "grey75", // Undocumented
194  "orange", // template relation
195  "orange" // type constraint
196 };
197 
198 static const char *umlArrowStyleMap[] =
199 {
200  "onormal", // Public
201  "onormal", // Protected
202  "onormal", // Private
203  "odiamond", // "use" relation
204  0, // Undocumented
205  0 // template relation
206 };
207 
208 static const char *umlEdgeStyleMap[] =
209 {
210  "solid", // inheritance
211  "solid" // usage
212 };
213 
214 /** Helper struct holding the properties of a edge in a dot graph. */
216 {
217  const char * const *edgeColorMap;
218  const char * const *arrowStyleMap;
219  const char * const *edgeStyleMap;
220 };
221 
223 {
225 };
226 
228 {
230 };
231 
232 
234 {
235  static QCString dotFontName = Config_getString("DOT_FONTNAME");
236  if (dotFontName.isEmpty())
237  {
238  //dotFontName="FreeSans.ttf";
239  dotFontName="Helvetica";
240  }
241  return dotFontName;
242 }
243 
244 static int getDotFontSize()
245 {
246  static int dotFontSize = Config_getInt("DOT_FONTSIZE");
247  if (dotFontSize<4) dotFontSize=4;
248  return dotFontSize;
249 }
250 
252 {
253  static bool interactiveSVG = Config_getBool("INTERACTIVE_SVG");
254  t << "digraph ";
255  if (title.isEmpty())
256  {
257  t << "\"Dot Graph\"";
258  }
259  else
260  {
261  t << "\"" << convertToXML(title) << "\"";
262  }
263  t << endl << "{" << endl;
264  if (interactiveSVG) // insert a comment to force regeneration when this
265  // option is toggled
266  {
267  t << " // INTERACTIVE_SVG=YES\n";
268  }
269  if (Config_getBool("DOT_TRANSPARENT"))
270  {
271  t << " bgcolor=\"transparent\";" << endl;
272  }
273  t << " edge [fontname=\"" << FONTNAME << "\","
274  "fontsize=\"" << FONTSIZE << "\","
275  "labelfontname=\"" << FONTNAME << "\","
276  "labelfontsize=\"" << FONTSIZE << "\"];\n";
277  t << " node [fontname=\"" << FONTNAME << "\","
278  "fontsize=\"" << FONTSIZE << "\",shape=record];\n";
279 }
280 
282 {
283  t << "}" << endl;
284 }
285 
286 static QCString replaceRef(const QCString &buf,const QCString relPath,
287  bool urlOnly,const QCString &context,const QCString &target=QCString())
288 {
289  // search for href="...", store ... part in link
290  QCString href = "href";
291  //bool isXLink=FALSE;
292  int len = 6;
293  int indexS = buf.find("href=\""), indexE;
294  if (indexS>5 && buf.find("xlink:href=\"")!=-1) // XLink href (for SVG)
295  {
296  indexS-=6;
297  len+=6;
298  href.prepend("xlink:");
299  //isXLink=TRUE;
300  }
301  if (indexS>=0 && (indexE=buf.find('"',indexS+len))!=-1)
302  {
303  QCString link = buf.mid(indexS+len,indexE-indexS-len);
305  if (urlOnly) // for user defined dot graphs
306  {
307  if (link.left(5)=="\\ref " || link.left(5)=="@ref ") // \ref url
308  {
309  result=href+"=\"";
310  // fake ref node to resolve the url
311  DocRef *df = new DocRef( (DocNode*) 0, link.mid(5), context );
312  result+=externalRef(relPath,df->ref(),TRUE);
313  if (!df->file().isEmpty())
314  result += df->file().data() + Doxygen::htmlFileExtension;
315  if (!df->anchor().isEmpty())
316  result += "#" + df->anchor();
317  delete df;
318  result += "\"";
319  }
320  else
321  {
322  result = href+"=\"" + link + "\"";
323  }
324  }
325  else // ref$url (external ref via tag file), or $url (local ref)
326  {
327  int marker = link.find('$');
328  if (marker!=-1)
329  {
330  QCString ref = link.left(marker);
331  QCString url = link.mid(marker+1);
332  if (!ref.isEmpty())
333  {
334  result = externalLinkTarget() + externalRef(relPath,ref,FALSE);
335  }
336  result+= href+"=\"";
337  result+=externalRef(relPath,ref,TRUE);
338  result+= url + "\"";
339  }
340  else // should not happen, but handle properly anyway
341  {
342  result = href+"=\"" + link + "\"";
343  }
344  }
345  if (!target.isEmpty())
346  {
347  result+=" target=\""+target+"\"";
348  }
349  QCString leftPart = buf.left(indexS);
350  QCString rightPart = buf.mid(indexE+1);
351  return leftPart + result + rightPart;
352  }
353  else
354  {
355  return buf;
356  }
357 }
358 
359 /*! converts the rectangles in a client site image map into a stream
360  * \param t the stream to which the result is written.
361  * \param mapName the name of the map file.
362  * \param relPath the relative path to the root of the output directory
363  * (used in case CREATE_SUBDIRS is enabled).
364  * \param urlOnly if FALSE the url field in the map contains an external
365  * references followed by a $ and then the URL.
366  * \param context the context (file, class, or namespace) in which the
367  * map file was found
368  * \returns TRUE if successful.
369  */
370 static bool convertMapFile(FTextStream &t,const char *mapName,
371  const QCString relPath, bool urlOnly=FALSE,
372  const QCString &context=QCString())
373 {
374  QFile f(mapName);
375  if (!f.open(IO_ReadOnly))
376  {
377  err("problems opening map file %s for inclusion in the docs!\n"
378  "If you installed Graphviz/dot after a previous failing run, \n"
379  "try deleting the output directory and rerun doxygen.\n",mapName);
380  return FALSE;
381  }
382  const int maxLineLen=10240;
383  while (!f.atEnd()) // foreach line
384  {
385  QCString buf(maxLineLen);
386  int numBytes = f.readLine(buf.rawData(),maxLineLen);
387  if (numBytes>0)
388  {
389  buf.resize(numBytes+1);
390 
391  if (buf.left(5)=="<area")
392  {
393  t << replaceRef(buf,relPath,urlOnly,context);
394  }
395  }
396  }
397  return TRUE;
398 }
399 
401 static int s_max_newNumber=0;
402 
403 inline int reNumberNode(int number, bool doReNumbering)
404 {
405  if (!doReNumbering)
406  {
407  return number;
408  }
409  else
410  {
411  int s = s_newNumber.size();
412  if (number>=s)
413  {
414  int ns=0;
415  ns = s * 3 / 2 + 5; // new size
416  if (number>=ns) // number still doesn't fit
417  {
418  ns = number * 3 / 2 + 5;
419  }
420  s_newNumber.resize(ns);
421  for (int i=s;i<ns;i++) // clear new part of the array
422  {
423  s_newNumber.at(i)=0;
424  }
425  }
426  int i = s_newNumber.at(number);
427  if (i == 0) // not yet mapped
428  {
429  i = ++s_max_newNumber; // start from 1
430  s_newNumber.at(number) = i;
431  }
432  return i;
433  }
434 }
435 
436 static void resetReNumbering()
437 {
438  s_max_newNumber=0;
439  s_newNumber.resize(s_max_newNumber);
440 }
441 
443 
444 static void setDotFontPath(const char *path)
445 {
446  ASSERT(g_dotFontPath.isEmpty());
447  g_dotFontPath = portable_getenv("DOTFONTPATH");
448  QCString newFontPath = Config_getString("DOT_FONTPATH");
449  QCString spath = path;
450  if (!newFontPath.isEmpty() && !spath.isEmpty())
451  {
452  newFontPath.prepend(spath+portable_pathListSeparator());
453  }
454  else if (newFontPath.isEmpty() && !spath.isEmpty())
455  {
456  newFontPath=path;
457  }
458  else
459  {
460  portable_unsetenv("DOTFONTPATH");
461  return;
462  }
463  portable_setenv("DOTFONTPATH",newFontPath);
464 }
465 
466 static void unsetDotFontPath()
467 {
468  if (g_dotFontPath.isEmpty())
469  {
470  portable_unsetenv("DOTFONTPATH");
471  }
472  else
473  {
474  portable_setenv("DOTFONTPATH",g_dotFontPath);
475  }
476  g_dotFontPath="";
477 }
478 
479 static bool readBoundingBox(const char *fileName,int *width,int *height,bool isEps)
480 {
481  QCString bb = isEps ? QCString("%%PageBoundingBox:") : QCString("/MediaBox [");
482  QFile f(fileName);
483  if (!f.open(IO_ReadOnly|IO_Raw))
484  {
485  //printf("readBoundingBox: could not open %s\n",fileName);
486  return FALSE;
487  }
488  const int maxLineLen=1024;
489  char buf[maxLineLen];
490  while (!f.atEnd())
491  {
492  int numBytes = f.readLine(buf,maxLineLen-1); // read line
493  if (numBytes>0)
494  {
495  buf[numBytes]='\0';
496  const char *p = strstr(buf,bb);
497  if (p) // found PageBoundingBox or /MediaBox string
498  {
499  int x,y;
500  if (sscanf(p+bb.length(),"%d %d %d %d",&x,&y,width,height)!=4)
501  {
502  //printf("readBoundingBox sscanf fail\n");
503  return FALSE;
504  }
505  return TRUE;
506  }
507  }
508  else // read error!
509  {
510  //printf("Read error %d!\n",numBytes);
511  return FALSE;
512  }
513  }
514  err("Failed to extract bounding box from generated diagram file %s\n",fileName);
515  return FALSE;
516 }
517 
519  const QCString &figureName)
520 {
521  int width=400,height=550;
522  static bool usePdfLatex = Config_getBool("USE_PDFLATEX");
523  if (usePdfLatex)
524  {
525  if (!readBoundingBox(figureName+".pdf",&width,&height,FALSE))
526  {
527  //printf("writeVecGfxFigure()=0\n");
528  return FALSE;
529  }
530  }
531  else
532  {
533  if (!readBoundingBox(figureName+".eps",&width,&height,TRUE))
534  {
535  //printf("writeVecGfxFigure()=0\n");
536  return FALSE;
537  }
538  }
539  //printf("Got PDF/EPS size %d,%d\n",width,height);
540  int maxWidth = 350; /* approx. page width in points, excl. margins */
541  int maxHeight = 550; /* approx. page height in points, excl. margins */
542  out << "\\nopagebreak\n"
543  "\\begin{figure}[H]\n"
544  "\\begin{center}\n"
545  "\\leavevmode\n";
546  if (width>maxWidth || height>maxHeight) // figure too big for page
547  {
548  // c*width/maxWidth > c*height/maxHeight, where c=maxWidth*maxHeight>0
549  if (width*maxHeight>height*maxWidth)
550  {
551  out << "\\includegraphics[width=" << maxWidth << "pt]";
552  }
553  else
554  {
555  out << "\\includegraphics[height=" << maxHeight << "pt]";
556  }
557  }
558  else
559  {
560  out << "\\includegraphics[width=" << width << "pt]";
561  }
562 
563  out << "{" << baseName << "}\n"
564  "\\end{center}\n"
565  "\\end{figure}\n";
566 
567  //printf("writeVecGfxFigure()=1\n");
568  return TRUE;
569 }
570 
571 // extract size from a dot generated SVG file
572 static bool readSVGSize(const QCString &fileName,int *width,int *height)
573 {
574  bool found=FALSE;
575  QFile f(fileName);
576  if (!f.open(IO_ReadOnly))
577  {
578  return FALSE;
579  }
580  const int maxLineLen=4096;
581  char buf[maxLineLen];
582  while (!f.atEnd() && !found)
583  {
584  int numBytes = f.readLine(buf,maxLineLen-1); // read line
585  if (numBytes>0)
586  {
587  buf[numBytes]='\0';
588  if (qstrncmp(buf,"<!--zoomable ",13)==0)
589  {
590  *width=-1;
591  *height=-1;
592  sscanf(buf,"<!--zoomable %d",height);
593  //printf("Found zoomable for %s!\n",fileName.data());
594  found=TRUE;
595  }
596  else if (sscanf(buf,"<svg width=\"%dpt\" height=\"%dpt\"",width,height)==2)
597  {
598  //printf("Found fixed size %dx%d for %s!\n",*width,*height,fileName.data());
599  found=TRUE;
600  }
601  }
602  else // read error!
603  {
604  //printf("Read error %d!\n",numBytes);
605  return FALSE;
606  }
607  }
608  return TRUE;
609 }
610 
612 {
613  out << "<p><b>This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.</b></p>";
614 }
615 
616 // check if a reference to a SVG figure can be written and does so if possible.
617 // return FALSE if not possible (for instance because the SVG file is not yet generated).
618 static bool writeSVGFigureLink(FTextStream &out,const QCString &relPath,
619  const QCString &baseName,const QCString &absImgName)
620 {
621  int width=600,height=600;
622  if (!readSVGSize(absImgName,&width,&height))
623  {
624  return FALSE;
625  }
626  if (width==-1)
627  {
628  if (height<=60)
629  height=300;
630  else
631  height+=300; // add some extra space for zooming
632  if (height>600) height=600; // clip to maximum height of 600 pixels
633  out << "<div class=\"zoom\">";
634  //out << "<object type=\"image/svg+xml\" data=\""
635  //out << "<embed type=\"image/svg+xml\" src=\""
636  out << "<iframe scrolling=\"no\" frameborder=\"0\" src=\""
637  << relPath << baseName << ".svg\" width=\"100%\" height=\"" << height << "\">";
638  }
639  else
640  {
641  //out << "<object type=\"image/svg+xml\" data=\""
642  //out << "<embed type=\"image/svg+xml\" src=\""
643  out << "<iframe scrolling=\"no\" frameborder=\"0\" src=\""
644  << relPath << baseName << ".svg\" width=\""
645  << ((width*96+48)/72) << "\" height=\""
646  << ((height*96+48)/72) << "\">";
647  }
649  //out << "</object>";
650  //out << "</embed>";
651  out << "</iframe>";
652  if (width==-1)
653  {
654  out << "</div>";
655  }
656 
657  return TRUE;
658 }
659 
660 // since dot silently reproduces the input file when it does not
661 // support the PNG format, we need to check the result.
662 static void checkDotResult(const char *imgExt, const char *imgName)
663 {
664  if (qstrcmp(imgExt,"png")==0)
665  {
666  FILE *f = portable_fopen(imgName,"rb");
667  if (f)
668  {
669  char data[4];
670  if (fread(data,1,4,f)==4)
671  {
672  if (!(data[1]=='P' && data[2]=='N' && data[3]=='G'))
673  {
674  err("Image `%s' produced by dot is not a valid PNG!\n"
675  "You should either select a different format "
676  "(DOT_IMAGE_FORMAT in the config file) or install a more "
677  "recent version of graphviz (1.7+)\n",imgName
678  );
679  }
680  }
681  else
682  {
683  err("Could not read image `%s' generated by dot!\n",imgName);
684  }
685  fclose(f);
686  }
687  else
688  {
689  err("Could not open image `%s' generated by dot!\n",imgName);
690  }
691  }
692 }
693 
694 static bool insertMapFile(FTextStream &out,const QCString &mapFile,
695  const QCString &relPath,const QCString &mapLabel)
696 {
697  QFileInfo fi(mapFile);
698  if (fi.exists() && fi.size()>0) // reuse existing map file
699  {
700  QGString tmpstr;
701  FTextStream tmpout(&tmpstr);
702  convertMapFile(tmpout,mapFile,relPath);
703  if (!tmpstr.isEmpty())
704  {
705  out << "<map name=\"" << mapLabel << "\" id=\"" << mapLabel << "\">" << endl;
706  out << tmpstr;
707  out << "</map>" << endl;
708  }
709  return TRUE;
710  }
711  return FALSE; // no map file yet, need to generate it
712 }
713 
714 static void removeDotGraph(const QCString &dotName)
715 {
716  static bool dotCleanUp = Config_getBool("DOT_CLEANUP");
717  if (dotCleanUp)
718  {
719  QDir d;
720  d.remove(dotName);
721  }
722 }
723 
724 
725 
726 /*! Checks if a file "baseName".md5 exists. If so the contents
727  * are compared with \a md5. If equal FALSE is returned. If the .md5
728  * file does not exist or its contents are not equal to \a md5,
729  * a new .md5 is generated with the \a md5 string as contents.
730  */
732  const QCString &md5)
733 {
734  QFile f(baseName+".md5");
735  if (f.open(IO_ReadOnly))
736  {
737  // read checksum
738  QCString md5stored(33);
739  int bytesRead=f.readBlock(md5stored.rawData(),32);
740  md5stored[32]='\0';
741  // compare checksum
742  if (bytesRead==32 && md5==md5stored)
743  {
744  // bail out if equal
745  return FALSE;
746  }
747  }
748  f.close();
749  // create checksum file
750  if (f.open(IO_WriteOnly))
751  {
752  f.writeBlock(md5.data(),32);
753  f.close();
754  }
755  return TRUE;
756 }
757 
758 static bool checkDeliverables(const QCString &file1,
759  const QCString &file2=QCString())
760 {
761  bool file1Ok = TRUE;
762  bool file2Ok = TRUE;
763  if (!file1.isEmpty())
764  {
765  QFileInfo fi(file1);
766  file1Ok = (fi.exists() && fi.size()>0);
767  }
768  if (!file2.isEmpty())
769  {
770  QFileInfo fi(file2);
771  file2Ok = (fi.exists() && fi.size()>0);
772  }
773  return file1Ok && file2Ok;
774 }
775 
776 //--------------------------------------------------------------------
777 
779 {
780  if ( !m_parents ) return -1;
781  return m_parents->find(n);
782 }
783 
784 //--------------------------------------------------------------------
785 
786 int DotNodeList::compareValues(const DotNode *n1,const DotNode *n2) const
787 {
788  return qstricmp(n1->m_label,n2->m_label);
789 }
790 
791 //--------------------------------------------------------------------
792 
794  bool checkResult,const QCString &imageName)
795  : m_dotExe(Config_getString("DOT_PATH")+"dot"),
796  m_file(file), m_path(path),
797  m_checkResult(checkResult), m_imageName(imageName),
798  m_imgExt(getDotImageExtension())
799 {
800  static bool dotCleanUp = Config_getBool("DOT_CLEANUP");
801  static bool dotMultiTargets = Config_getBool("DOT_MULTI_TARGETS");
802  m_cleanUp = dotCleanUp;
803  m_multiTargets = dotMultiTargets;
805 }
806 
807 void DotRunner::addJob(const char *format,const char *output)
808 {
809  QCString args = QCString("-T")+format+" -o \""+output+"\"";
810  m_jobs.append(new DotConstString(args));
811 }
812 
813 void DotRunner::addPostProcessing(const char *cmd,const char *args)
814 {
815  m_postCmd.set(cmd);
816  m_postArgs.set(args);
817 }
818 
820 {
821  int exitCode=0;
822 
823  QCString dotArgs;
825  DotConstString *s;
826  if (m_multiTargets)
827  {
828  dotArgs=QCString("\"")+m_file.data()+"\"";
829  for (li.toFirst();(s=li.current());++li)
830  {
831  dotArgs+=' ';
832  dotArgs+=s->data();
833  }
834  if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0)
835  {
836  goto error;
837  }
838  }
839  else
840  {
841  for (li.toFirst();(s=li.current());++li)
842  {
843  dotArgs=QCString("\"")+m_file.data()+"\" "+s->data();
844  if ((exitCode=portable_system(m_dotExe.data(),dotArgs,FALSE))!=0)
845  {
846  goto error;
847  }
848  }
849  }
851  {
852  err("Problems running '%s' as a post-processing step for dot output\n",m_postCmd.data());
853  return FALSE;
854  }
855  if (m_checkResult)
856  {
858  }
859  if (m_cleanUp)
860  {
861  //printf("removing dot file %s\n",m_file.data());
862  //QDir(path).remove(file);
865  }
866  return TRUE;
867 error:
868  err("Problems running dot: exit code=%d, command='%s', arguments='%s'\n",
869  exitCode,m_dotExe.data(),dotArgs.data());
870  return FALSE;
871 }
872 
873 //--------------------------------------------------------------------
874 
875 DotFilePatcher::DotFilePatcher(const char *patchFile)
876  : m_patchFile(patchFile)
877 {
878  m_maps.setAutoDelete(TRUE);
879 }
880 
882 {
883  return m_patchFile;
884 }
885 
886 int DotFilePatcher::addMap(const QCString &mapFile,const QCString &relPath,
887  bool urlOnly,const QCString &context,const QCString &label)
888 {
889  int id = m_maps.count();
890  Map *map = new Map;
891  map->mapFile = mapFile;
892  map->relPath = relPath;
893  map->urlOnly = urlOnly;
894  map->context = context;
895  map->label = label;
896  map->zoomable = FALSE;
897  map->graphId = -1;
898  m_maps.append(map);
899  return id;
900 }
901 
903  const QCString &figureName,bool heightCheck)
904 {
905  int id = m_maps.count();
906  Map *map = new Map;
907  map->mapFile = figureName;
908  map->urlOnly = heightCheck;
909  map->label = baseName;
910  map->zoomable = FALSE;
911  map->graphId = -1;
912  m_maps.append(map);
913  return id;
914 }
915 
916 int DotFilePatcher::addSVGConversion(const QCString &relPath,bool urlOnly,
917  const QCString &context,bool zoomable,
918  int graphId)
919 {
920  int id = m_maps.count();
921  Map *map = new Map;
922  map->relPath = relPath;
923  map->urlOnly = urlOnly;
924  map->context = context;
925  map->zoomable = zoomable;
926  map->graphId = graphId;
927  m_maps.append(map);
928  return id;
929 }
930 
932  const QCString &absImgName,
933  const QCString &relPath)
934 {
935  int id = m_maps.count();
936  Map *map = new Map;
937  map->mapFile = absImgName;
938  map->relPath = relPath;
939  map->label = baseName;
940  map->zoomable = FALSE;
941  map->graphId = -1;
942  m_maps.append(map);
943  return id;
944 }
945 
947 {
948  //printf("DotFilePatcher::run(): %s\n",m_patchFile.data());
949  static bool interactiveSVG = Config_getBool("INTERACTIVE_SVG");
950  bool isSVGFile = m_patchFile.right(4)==".svg";
951  int graphId = -1;
952  QCString relPath;
953  if (isSVGFile)
954  {
955  Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
956  interactiveSVG = interactiveSVG && map->zoomable;
957  graphId = map->graphId;
958  relPath = map->relPath;
959  //printf("DotFilePatcher::addSVGConversion: file=%s zoomable=%d\n",
960  // m_patchFile.data(),map->zoomable);
961  }
962  QString tmpName = QString::fromUtf8(m_patchFile+".tmp");
964  if (!QDir::current().rename(patchFile,tmpName))
965  {
966  err("Failed to rename file %s to %s!\n",m_patchFile.data(),tmpName.data());
967  return FALSE;
968  }
969  QFile fi(tmpName);
970  QFile fo(patchFile);
971  if (!fi.open(IO_ReadOnly))
972  {
973  err("problem opening file %s for patching!\n",tmpName.data());
974  QDir::current().rename(tmpName,patchFile);
975  return FALSE;
976  }
977  if (!fo.open(IO_WriteOnly))
978  {
979  err("problem opening file %s for patching!\n",m_patchFile.data());
980  QDir::current().rename(tmpName,patchFile);
981  return FALSE;
982  }
983  FTextStream t(&fo);
984  const int maxLineLen=100*1024;
985  int lineNr=1;
986  int width,height;
987  bool insideHeader=FALSE;
988  bool replacedHeader=FALSE;
989  bool foundSize=FALSE;
990  while (!fi.atEnd()) // foreach line
991  {
992  QCString line(maxLineLen);
993  int numBytes = fi.readLine(line.rawData(),maxLineLen);
994  if (numBytes<=0)
995  {
996  break;
997  }
998  line.resize(numBytes+1);
999 
1000  //printf("line=[%s]\n",line.stripWhiteSpace().data());
1001  int i;
1002  ASSERT(numBytes<maxLineLen);
1003  if (isSVGFile)
1004  {
1005  if (interactiveSVG)
1006  {
1007  if (line.find("<svg")!=-1 && !replacedHeader)
1008  {
1009  int count;
1010  count = sscanf(line.data(),"<svg width=\"%dpt\" height=\"%dpt\"",&width,&height);
1011  //printf("width=%d height=%d\n",width,height);
1012  foundSize = count==2 && (width>500 || height>450);
1013  if (foundSize) insideHeader=TRUE;
1014  }
1015  else if (insideHeader && !replacedHeader && line.find("<title>")!=-1)
1016  {
1017  if (foundSize)
1018  {
1019  // insert special replacement header for interactive SVGs
1020  t << "<!--zoomable " << height << " -->\n";
1021  t << svgZoomHeader;
1022  t << "var viewWidth = " << width << ";\n";
1023  t << "var viewHeight = " << height << ";\n";
1024  if (graphId>=0)
1025  {
1026  t << "var sectionId = 'dynsection-" << graphId << "';\n";
1027  }
1028  t << "</script>\n";
1029  t << "<script xlink:href=\"" << relPath << "svgpan.js\"/>\n";
1030  t << "<svg id=\"graph\" class=\"graph\">\n";
1031  t << "<g id=\"viewport\">\n";
1032  }
1033  insideHeader=FALSE;
1034  replacedHeader=TRUE;
1035  }
1036  }
1037  if (!insideHeader || !foundSize) // copy SVG and replace refs,
1038  // unless we are inside the header of the SVG.
1039  // Then we replace it with another header.
1040  {
1041  Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
1042  t << replaceRef(line,map->relPath,map->urlOnly,map->context,"_top");
1043  }
1044  }
1045  else if ((i=line.find("<!-- SVG"))!=-1 || (i=line.find("[!-- SVG"))!=-1)
1046  {
1047  //printf("Found marker at %d\n",i);
1048  int mapId=-1;
1049  t << line.left(i);
1050  int n = sscanf(line.data()+i+1,"!-- SVG %d",&mapId);
1051  if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
1052  {
1053  int e = QMAX(line.find("--]"),line.find("-->"));
1054  Map *map = m_maps.at(mapId);
1055  //printf("DotFilePatcher::writeSVGFigure: file=%s zoomable=%d\n",
1056  // m_patchFile.data(),map->zoomable);
1057  if (!writeSVGFigureLink(t,map->relPath,map->label,map->mapFile))
1058  {
1059  err("Problem extracting size from SVG file %s\n",map->mapFile.data());
1060  }
1061  if (e!=-1) t << line.mid(e+3);
1062  }
1063  else // error invalid map id!
1064  {
1065  err("Found invalid SVG id in file %s!\n",m_patchFile.data());
1066  t << line.mid(i);
1067  }
1068  }
1069  else if ((i=line.find("<!-- MAP"))!=-1)
1070  {
1071  int mapId=-1;
1072  t << line.left(i);
1073  int n = sscanf(line.data()+i,"<!-- MAP %d",&mapId);
1074  if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
1075  {
1076  Map *map = m_maps.at(mapId);
1077  //printf("patching MAP %d in file %s with contents of %s\n",
1078  // mapId,m_patchFile.data(),map->mapFile.data());
1079  t << "<map name=\"" << map->label << "\" id=\"" << map->label << "\">" << endl;
1080  convertMapFile(t,map->mapFile,map->relPath,map->urlOnly,map->context);
1081  t << "</map>" << endl;
1082  }
1083  else // error invalid map id!
1084  {
1085  err("Found invalid MAP id in file %s!\n",m_patchFile.data());
1086  t << line.mid(i);
1087  }
1088  }
1089  else if ((i=line.find("% FIG"))!=-1)
1090  {
1091  int mapId=-1;
1092  int n = sscanf(line.data()+i+2,"FIG %d",&mapId);
1093  //printf("line='%s' n=%d\n",line.data()+i,n);
1094  if (n==1 && mapId>=0 && mapId<(int)m_maps.count())
1095  {
1096  Map *map = m_maps.at(mapId);
1097  //printf("patching FIG %d in file %s with contents of %s\n",
1098  // mapId,m_patchFile.data(),map->mapFile.data());
1099  if (!writeVecGfxFigure(t,map->label,map->mapFile))
1100  {
1101  err("problem writing FIG %d figure!\n",mapId);
1102  return FALSE;
1103  }
1104  }
1105  else // error invalid map id!
1106  {
1107  err("Found invalid bounding FIG %d in file %s!\n",mapId,m_patchFile.data());
1108  t << line;
1109  }
1110  }
1111  else
1112  {
1113  t << line;
1114  }
1115  lineNr++;
1116  }
1117  fi.close();
1118  if (isSVGFile && interactiveSVG && replacedHeader)
1119  {
1120  QCString orgName=m_patchFile.left(m_patchFile.length()-4)+"_org.svg";
1121  t << substitute(svgZoomFooter,"$orgname",stripPath(orgName));
1122  fo.close();
1123  // keep original SVG file so we can refer to it, we do need to replace
1124  // dummy link by real ones
1125  QFile fi(tmpName);
1126  QFile fo(orgName);
1127  if (!fi.open(IO_ReadOnly))
1128  {
1129  err("problem opening file %s for reading!\n",tmpName.data());
1130  return FALSE;
1131  }
1132  if (!fo.open(IO_WriteOnly))
1133  {
1134  err("problem opening file %s for writing!\n",orgName.data());
1135  return FALSE;
1136  }
1137  FTextStream t(&fo);
1138  while (!fi.atEnd()) // foreach line
1139  {
1140  QCString line(maxLineLen);
1141  int numBytes = fi.readLine(line.rawData(),maxLineLen);
1142  if (numBytes<=0)
1143  {
1144  break;
1145  }
1146  line.resize(numBytes+1);
1147  Map *map = m_maps.at(0); // there is only one 'map' for a SVG file
1148  t << replaceRef(line,map->relPath,map->urlOnly,map->context,"_top");
1149  }
1150  fi.close();
1151  fo.close();
1152  }
1153  // remove temporary file
1154  QDir::current().remove(tmpName);
1155  return TRUE;
1156 }
1157 
1158 //--------------------------------------------------------------------
1159 
1161 {
1162  QMutexLocker locker(&m_mutex);
1163  m_queue.enqueue(runner);
1164  m_bufferNotEmpty.wakeAll();
1165 }
1166 
1168 {
1169  QMutexLocker locker(&m_mutex);
1170  while (m_queue.isEmpty())
1171  {
1172  // wait until something is added to the queue
1173  m_bufferNotEmpty.wait(&m_mutex);
1174  }
1175  DotRunner *result = m_queue.dequeue();
1176  return result;
1177 }
1178 
1180 {
1181  QMutexLocker locker(&m_mutex);
1182  return m_queue.count();
1183 }
1184 
1185 //--------------------------------------------------------------------
1186 
1188  : m_queue(queue)
1189 {
1191 }
1192 
1194 {
1195  DotRunner *runner;
1196  while ((runner=m_queue->dequeue()))
1197  {
1198  runner->run();
1199  const DotRunner::CleanupItem &cleanup = runner->cleanup();
1200  if (!cleanup.file.isEmpty())
1201  {
1203  }
1204  }
1205 }
1206 
1208 {
1211  for (;(ci=it.current());++it)
1212  {
1213  QDir(ci->path.data()).remove(ci->file.data());
1214  }
1215 }
1216 
1217 //--------------------------------------------------------------------
1218 
1220 
1222 {
1223  if (!m_theInstance)
1224  {
1225  m_theInstance = new DotManager;
1226  }
1227  return m_theInstance;
1228 }
1229 
1230 DotManager::DotManager() : m_dotMaps(1009)
1231 {
1234  m_queue = new DotRunnerQueue;
1235  int i;
1236  int numThreads = QMIN(32,Config_getInt("DOT_NUM_THREADS"));
1237  if (numThreads!=1)
1238  {
1239  if (numThreads==0) numThreads = QMAX(2,QThread::idealThreadCount()+1);
1240  for (i=0;i<numThreads;i++)
1241  {
1242  DotWorkerThread *thread = new DotWorkerThread(m_queue);
1243  thread->start();
1244  if (thread->isRunning())
1245  {
1246  m_workers.append(thread);
1247  }
1248  else // no more threads available!
1249  {
1250  delete thread;
1251  }
1252  }
1253  ASSERT(m_workers.count()>0);
1254  }
1255 }
1256 
1258 {
1259  delete m_queue;
1260 }
1261 
1263 {
1264  m_dotRuns.append(run);
1265 }
1266 
1267 int DotManager::addMap(const QCString &file,const QCString &mapFile,
1268  const QCString &relPath,bool urlOnly,const QCString &context,
1269  const QCString &label)
1270 {
1271  DotFilePatcher *map = m_dotMaps.find(file);
1272  if (map==0)
1273  {
1274  map = new DotFilePatcher(file);
1275  m_dotMaps.append(file,map);
1276  }
1277  return map->addMap(mapFile,relPath,urlOnly,context,label);
1278 }
1279 
1281  const QCString &figureName,bool heightCheck)
1282 {
1283  DotFilePatcher *map = m_dotMaps.find(file);
1284  if (map==0)
1285  {
1286  map = new DotFilePatcher(file);
1287  m_dotMaps.append(file,map);
1288  }
1289  return map->addFigure(baseName,figureName,heightCheck);
1290 }
1291 
1293  bool urlOnly,const QCString &context,bool zoomable,
1294  int graphId)
1295 {
1296  DotFilePatcher *map = m_dotMaps.find(file);
1297  if (map==0)
1298  {
1299  map = new DotFilePatcher(file);
1300  m_dotMaps.append(file,map);
1301  }
1302  return map->addSVGConversion(relPath,urlOnly,context,zoomable,graphId);
1303 }
1304 
1306  const QCString &absImgName,const QCString &relPath)
1307 {
1308  DotFilePatcher *map = m_dotMaps.find(file);
1309  if (map==0)
1310  {
1311  map = new DotFilePatcher(file);
1312  m_dotMaps.append(file,map);
1313  }
1314  return map->addSVGObject(baseName,absImgName,relPath);
1315 }
1316 
1318 {
1319  uint numDotRuns = m_dotRuns.count();
1320  uint numDotMaps = m_dotMaps.count();
1321  if (numDotRuns+numDotMaps>1)
1322  {
1323  if (m_workers.count()==0)
1324  {
1325  msg("Generating dot graphs in single threaded mode...\n");
1326  }
1327  else
1328  {
1329  msg("Generating dot graphs using %d parallel threads...\n",QMIN(numDotRuns+numDotMaps,m_workers.count()));
1330  }
1331  }
1332  int i=1;
1334 
1335  bool setPath=FALSE;
1336  if (Config_getBool("GENERATE_HTML"))
1337  {
1338  setDotFontPath(Config_getString("HTML_OUTPUT"));
1339  setPath=TRUE;
1340  }
1341  else if (Config_getBool("GENERATE_LATEX"))
1342  {
1343  setDotFontPath(Config_getString("LATEX_OUTPUT"));
1344  setPath=TRUE;
1345  }
1346  else if (Config_getBool("GENERATE_RTF"))
1347  {
1348  setDotFontPath(Config_getString("RTF_OUTPUT"));
1349  setPath=TRUE;
1350  }
1352  // fill work queue with dot operations
1353  DotRunner *dr;
1354  int prev=1;
1355  if (m_workers.count()==0) // no threads to work with
1356  {
1357  for (li.toFirst();(dr=li.current());++li)
1358  {
1359  msg("Running dot for graph %d/%d\n",prev,numDotRuns);
1360  dr->run();
1361  prev++;
1362  }
1363  }
1364  else // use multiple threads to run instances of dot in parallel
1365  {
1366  for (li.toFirst();(dr=li.current());++li)
1367  {
1368  m_queue->enqueue(dr);
1369  }
1370  // wait for the queue to become empty
1371  while ((i=m_queue->count())>0)
1372  {
1373  i = numDotRuns - i;
1374  while (i>=prev)
1375  {
1376  msg("Running dot for graph %d/%d\n",prev,numDotRuns);
1377  prev++;
1378  }
1379  portable_sleep(100);
1380  }
1381  while ((int)numDotRuns>=prev)
1382  {
1383  msg("Running dot for graph %d/%d\n",prev,numDotRuns);
1384  prev++;
1385  }
1386  // signal the workers we are done
1387  for (i=0;i<(int)m_workers.count();i++)
1388  {
1389  m_queue->enqueue(0); // add terminator for each worker
1390  }
1391  // wait for the workers to finish
1392  for (i=0;i<(int)m_workers.count();i++)
1393  {
1394  m_workers.at(i)->wait();
1395  }
1396  // clean up dot files from main thread
1397  for (i=0;i<(int)m_workers.count();i++)
1398  {
1399  m_workers.at(i)->cleanup();
1400  }
1401  }
1403  if (setPath)
1404  {
1405  unsetDotFontPath();
1406  }
1407 
1408  // patch the output file and insert the maps and figures
1409  i=1;
1411  DotFilePatcher *map;
1412  // since patching the svg files may involve patching the header of the SVG
1413  // (for zoomable SVGs), and patching the .html files requires reading that
1414  // header after the SVG is patched, we first process the .svg files and
1415  // then the other files.
1416  for (di.toFirst();(map=di.current());++di)
1417  {
1418  if (map->file().right(4)==".svg")
1419  {
1420  msg("Patching output file %d/%d\n",i,numDotMaps);
1421  if (!map->run()) return FALSE;
1422  i++;
1423  }
1424  }
1425  for (di.toFirst();(map=di.current());++di)
1426  {
1427  if (map->file().right(4)!=".svg")
1428  {
1429  msg("Patching output file %d/%d\n",i,numDotMaps);
1430  if (!map->run()) return FALSE;
1431  i++;
1432  }
1433  }
1434  return TRUE;
1435 }
1436 
1437 //--------------------------------------------------------------------
1438 
1439 
1440 /*! helper function that deletes all nodes in a connected graph, given
1441  * one of the graph's nodes
1442  */
1443 static void deleteNodes(DotNode *node,SDict<DotNode> *skipNodes=0)
1444 {
1445  //printf("deleteNodes skipNodes=%p\n",skipNodes);
1446  static DotNodeList deletedNodes;
1447  deletedNodes.setAutoDelete(TRUE);
1448  node->deleteNode(deletedNodes,skipNodes); // collect nodes to be deleted.
1449  deletedNodes.clear(); // actually remove the nodes.
1450 }
1451 
1452 DotNode::DotNode(int n,const char *lab,const char *tip, const char *url,
1453  bool isRoot,ClassDef *cd)
1454  : m_subgraphId(-1)
1455  , m_number(n)
1456  , m_label(lab)
1457  , m_tooltip(tip)
1458  , m_url(url)
1459  , m_parents(0)
1460  , m_children(0)
1461  , m_edgeInfo(0)
1462  , m_deleted(FALSE)
1463  , m_written(FALSE)
1464  , m_hasDoc(FALSE)
1465  , m_isRoot(isRoot)
1466  , m_classDef(cd)
1467  , m_visible(FALSE)
1468  , m_truncated(Unknown)
1469  , m_distance(1000)
1470 {
1471 }
1472 
1474 {
1475  delete m_children;
1476  delete m_parents;
1477  delete m_edgeInfo;
1478 }
1479 
1481  int edgeColor,
1482  int edgeStyle,
1483  const char *edgeLab,
1484  const char *edgeURL,
1485  int edgeLabCol
1486  )
1487 {
1488  if (m_children==0)
1489  {
1490  m_children = new QList<DotNode>;
1493  }
1494  m_children->append(n);
1495  EdgeInfo *ei = new EdgeInfo;
1496  ei->m_color = edgeColor;
1497  ei->m_style = edgeStyle;
1498  ei->m_label = edgeLab;
1499  ei->m_url = edgeURL;
1500  if (edgeLabCol==-1)
1501  ei->m_labColor=edgeColor;
1502  else
1503  ei->m_labColor=edgeLabCol;
1504  m_edgeInfo->append(ei);
1505 }
1506 
1508 {
1509  if (m_parents==0)
1510  {
1511  m_parents = new QList<DotNode>;
1512  }
1513  m_parents->append(n);
1514 }
1515 
1517 {
1518  if (m_children) m_children->remove(n);
1519 }
1520 
1522 {
1523  if (m_parents) m_parents->remove(n);
1524 }
1525 
1526 void DotNode::deleteNode(DotNodeList &deletedList,SDict<DotNode> *skipNodes)
1527 {
1528  if (m_deleted) return; // avoid recursive loops in case the graph has cycles
1529  m_deleted=TRUE;
1530  if (m_parents!=0) // delete all parent nodes of this node
1531  {
1533  DotNode *pn;
1534  for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
1535  {
1536  //pn->removeChild(this);
1537  pn->deleteNode(deletedList,skipNodes);
1538  }
1539  }
1540  if (m_children!=0) // delete all child nodes of this node
1541  {
1543  DotNode *cn;
1544  for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
1545  {
1546  //cn->removeParent(this);
1547  cn->deleteNode(deletedList,skipNodes);
1548  }
1549  }
1550  // add this node to the list of deleted nodes.
1551  //printf("skipNodes=%p find(%p)=%p\n",skipNodes,this,skipNodes ? skipNodes->find((int)this) : 0);
1552  if (skipNodes==0 || skipNodes->find((char*)this)==0)
1553  {
1554  //printf("deleting\n");
1555  deletedList.append(this);
1556  }
1557 }
1558 
1560 {
1561  if (distance<m_distance) m_distance = distance;
1562 }
1563 
1565 {
1566  QCString result;
1567  QCString bBefore("\\_/<({[: =-+@%#~?$"); // break before character set
1568  QCString bAfter(">]),:;|"); // break after character set
1569  const char *p=l.data();
1570  if (p==0) return result;
1571  char c,pc=0;
1572  char cs[2];
1573  cs[1]=0;
1574  int len=l.length();
1575  int charsLeft=len;
1576  int sinceLast=0;
1577  int foldLen=17; // ideal text length
1578  while ((c=*p++))
1579  {
1580  QCString replacement;
1581  switch(c)
1582  {
1583  case '\\': replacement="\\\\"; break;
1584  case '\n': replacement="\\n"; break;
1585  case '<': replacement="\\<"; break;
1586  case '>': replacement="\\>"; break;
1587  case '|': replacement="\\|"; break;
1588  case '{': replacement="\\{"; break;
1589  case '}': replacement="\\}"; break;
1590  case '"': replacement="\\\""; break;
1591  default: cs[0]=c; replacement=cs; break;
1592  }
1593  // Some heuristics to insert newlines to prevent too long
1594  // boxes and at the same time prevent ugly breaks
1595  if (c=='\n')
1596  {
1597  result+=replacement;
1598  foldLen = (3*foldLen+sinceLast+2)/4;
1599  sinceLast=1;
1600  }
1601  else if ((pc!=':' || c!=':') && charsLeft>foldLen/3 && sinceLast>foldLen && bBefore.contains(c))
1602  {
1603  result+="\\l";
1604  result+=replacement;
1605  foldLen = (foldLen+sinceLast+1)/2;
1606  sinceLast=1;
1607  }
1608  else if (charsLeft>1+foldLen/4 && sinceLast>foldLen+foldLen/3 &&
1609  !isupper(c) && isupper(*p))
1610  {
1611  result+=replacement;
1612  result+="\\l";
1613  foldLen = (foldLen+sinceLast+1)/2;
1614  sinceLast=0;
1615  }
1616  else if (charsLeft>foldLen/3 && sinceLast>foldLen && bAfter.contains(c) && (c!=':' || *p!=':'))
1617  {
1618  result+=replacement;
1619  result+="\\l";
1620  foldLen = (foldLen+sinceLast+1)/2;
1621  sinceLast=0;
1622  }
1623  else
1624  {
1625  result+=replacement;
1626  sinceLast++;
1627  }
1628  charsLeft--;
1629  pc=c;
1630  }
1631  return result;
1632 }
1633 
1634 static QCString escapeTooltip(const QCString &tooltip)
1635 {
1636  QCString result;
1637  const char *p=tooltip.data();
1638  if (p==0) return result;
1639  char c;
1640  while ((c=*p++))
1641  {
1642  switch(c)
1643  {
1644  case '"': result+="\\\""; break;
1645  default: result+=c; break;
1646  }
1647  }
1648  return result;
1649 }
1650 
1652  char prot,MemberList *ml,ClassDef *scope,
1653  bool isStatic=FALSE,const QDict<void> *skipNames=0)
1654 {
1655  (void)isStatic;
1656  if (ml)
1657  {
1658  MemberListIterator mlia(*ml);
1659  MemberDef *mma;
1660  int totalCount=0;
1661  for (mlia.toFirst();(mma = mlia.current());++mlia)
1662  {
1663  if (mma->getClassDef()==scope &&
1664  (skipNames==0 || skipNames->find(mma->name())==0))
1665  {
1666  totalCount++;
1667  }
1668  }
1669 
1670  int count=0;
1671  for (mlia.toFirst();(mma = mlia.current());++mlia)
1672  {
1673  if (mma->getClassDef() == scope &&
1674  (skipNames==0 || skipNames->find(mma->name())==0))
1675  {
1676  static int limit = Config_getInt("UML_LIMIT_NUM_FIELDS");
1677  if (limit>0 && (totalCount>limit*3/2 && count>=limit))
1678  {
1679  t << theTranslator->trAndMore(QCString().sprintf("%d",totalCount-count)) << "\\l";
1680  break;
1681  }
1682  else
1683  {
1684  t << prot << " ";
1685  t << convertLabel(mma->name());
1686  if (!mma->isObjCMethod() &&
1687  (mma->isFunction() || mma->isSlot() || mma->isSignal())) t << "()";
1688  t << "\\l";
1689  count++;
1690  }
1691  }
1692  }
1693  // write member groups within the memberlist
1694  MemberGroupList *mgl = ml->getMemberGroupList();
1695  if (mgl)
1696  {
1697  MemberGroupListIterator mgli(*mgl);
1698  MemberGroup *mg;
1699  for (mgli.toFirst();(mg=mgli.current());++mgli)
1700  {
1701  if (mg->members())
1702  {
1703  writeBoxMemberList(t,prot,mg->members(),scope,isStatic,skipNames);
1704  }
1705  }
1706  }
1707  }
1708 }
1709 
1711 {
1712  if (!s.isEmpty() && (s[0]=='-' || s[0]=='+' || s[0]=='~' || s[0]=='#'))
1713  {
1714  return s.mid(1);
1715  }
1716  else
1717  {
1718  return s;
1719  }
1720 }
1721 
1723  GraphType gt,
1724  GraphOutputFormat /*format*/,
1725  bool hasNonReachableChildren,
1726  bool reNumber)
1727 {
1728  const char *labCol =
1729  m_url.isEmpty() ? "grey75" : // non link
1730  (
1731  (hasNonReachableChildren) ? "red" : "black"
1732  );
1733  t << " Node" << reNumberNode(m_number,reNumber) << " [label=\"";
1734  static bool umlLook = Config_getBool("UML_LOOK");
1735 
1736  if (m_classDef && umlLook && (gt==Inheritance || gt==Collaboration))
1737  {
1738  // add names shown as relations to a dictionary, so we don't show
1739  // them as attributes as well
1740  QDict<void> arrowNames(17);
1741  if (m_edgeInfo)
1742  {
1743  // for each edge
1745  EdgeInfo *ei;
1746  for (li.toFirst();(ei=li.current());++li)
1747  {
1748  if (!ei->m_label.isEmpty()) // labels joined by \n
1749  {
1750  int li=ei->m_label.find('\n');
1751  int p=0;
1752  QCString lab;
1753  while ((li=ei->m_label.find('\n',p))!=-1)
1754  {
1755  lab = stripProtectionPrefix(ei->m_label.mid(p,li-p));
1756  arrowNames.insert(lab,(void*)0x8);
1757  p=li+1;
1758  }
1759  lab = stripProtectionPrefix(ei->m_label.right(ei->m_label.length()-p));
1760  arrowNames.insert(lab,(void*)0x8);
1761  }
1762  }
1763  }
1764 
1765  //printf("DotNode::writeBox for %s\n",m_classDef->name().data());
1766  static bool extractPrivate = Config_getBool("EXTRACT_PRIVATE");
1767  t << "{" << convertLabel(m_label);
1768  t << "\\n|";
1776  if (extractPrivate)
1777  {
1780  }
1781  t << "|";
1790  if (extractPrivate)
1791  {
1795  }
1798  {
1800  MemberGroup *mg;
1801  for (mgdi.toFirst();(mg=mgdi.current());++mgdi)
1802  {
1803  if (mg->members())
1804  {
1805  writeBoxMemberList(t,'*',mg->members(),m_classDef,FALSE,&arrowNames);
1806  }
1807  }
1808  }
1809  t << "}";
1810  }
1811  else // standard look
1812  {
1813  t << convertLabel(m_label);
1814  }
1815  t << "\",height=0.2,width=0.4";
1816  if (m_isRoot)
1817  {
1818  t << ",color=\"black\", fillcolor=\"grey75\", style=\"filled\", fontcolor=\"black\"";
1819  }
1820  else
1821  {
1822  static bool dotTransparent = Config_getBool("DOT_TRANSPARENT");
1823  if (!dotTransparent)
1824  {
1825  t << ",color=\"" << labCol << "\", fillcolor=\"";
1826  t << "white";
1827  t << "\", style=\"filled\"";
1828  }
1829  else
1830  {
1831  t << ",color=\"" << labCol << "\"";
1832  }
1833  if (!m_url.isEmpty())
1834  {
1835  int anchorPos = m_url.findRev('#');
1836  if (anchorPos==-1)
1837  {
1838  t << ",URL=\"" << m_url << Doxygen::htmlFileExtension << "\"";
1839  }
1840  else
1841  {
1842  t << ",URL=\"" << m_url.left(anchorPos) << Doxygen::htmlFileExtension
1843  << m_url.right(m_url.length()-anchorPos) << "\"";
1844  }
1845  }
1846  if (!m_tooltip.isEmpty())
1847  {
1848  t << ",tooltip=\"" << escapeTooltip(m_tooltip) << "\"";
1849  }
1850  }
1851  t << "];" << endl;
1852 }
1853 
1855  GraphType gt,
1857  DotNode *cn,
1858  EdgeInfo *ei,
1859  bool topDown,
1860  bool pointBack,
1861  bool reNumber
1862  )
1863 {
1864  t << " Node";
1865  if (topDown)
1866  t << reNumberNode(cn->number(),reNumber);
1867  else
1868  t << reNumberNode(m_number,reNumber);
1869  t << " -> Node";
1870  if (topDown)
1871  t << reNumberNode(m_number,reNumber);
1872  else
1873  t << reNumberNode(cn->number(),reNumber);
1874  t << " [";
1875 
1876  static bool umlLook = Config_getBool("UML_LOOK");
1877  const EdgeProperties *eProps = umlLook ? &umlEdgeProps : &normalEdgeProps;
1878  QCString aStyle = eProps->arrowStyleMap[ei->m_color];
1879  bool umlUseArrow = aStyle=="odiamond";
1880 
1881  if (pointBack && !umlUseArrow) t << "dir=\"back\",";
1882  t << "color=\"" << eProps->edgeColorMap[ei->m_color]
1883  << "\",fontsize=\"" << FONTSIZE << "\",";
1884  t << "style=\"" << eProps->edgeStyleMap[ei->m_style] << "\"";
1885  if (!ei->m_label.isEmpty())
1886  {
1887  t << ",label=\" " << convertLabel(ei->m_label) << "\" ";
1888  }
1889  if (umlLook &&
1890  eProps->arrowStyleMap[ei->m_color] &&
1891  (gt==Inheritance || gt==Collaboration)
1892  )
1893  {
1894  bool rev = pointBack;
1895  if (umlUseArrow) rev=!rev; // UML use relates has arrow on the start side
1896  if (rev)
1897  t << ",arrowtail=\"" << eProps->arrowStyleMap[ei->m_color] << "\"";
1898  else
1899  t << ",arrowhead=\"" << eProps->arrowStyleMap[ei->m_color] << "\"";
1900  }
1901 
1902  if (format==GOF_BITMAP) t << ",fontname=\"" << FONTNAME << "\"";
1903  t << "];" << endl;
1904 }
1905 
1907  GraphType gt,
1909  bool topDown,
1910  bool toChildren,
1911  bool backArrows,
1912  bool reNumber
1913  )
1914 {
1915  //printf("DotNode::write(%d) name=%s this=%p written=%d visible=%d\n",m_distance,m_label.data(),this,m_written,m_visible);
1916  if (m_written) return; // node already written to the output
1917  if (!m_visible) return; // node is not visible
1918  writeBox(t,gt,format,m_truncated==Truncated,reNumber);
1919  m_written=TRUE;
1920  QList<DotNode> *nl = toChildren ? m_children : m_parents;
1921  if (nl)
1922  {
1923  if (toChildren)
1924  {
1925  QListIterator<DotNode> dnli1(*nl);
1927  DotNode *cn;
1928  for (dnli1.toFirst();(cn=dnli1.current());++dnli1,++dnli2)
1929  {
1930  if (cn->isVisible())
1931  {
1932  //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",cn->label().data());
1933  writeArrow(t,gt,format,cn,dnli2.current(),topDown,backArrows,reNumber);
1934  }
1935  cn->write(t,gt,format,topDown,toChildren,backArrows,reNumber);
1936  }
1937  }
1938  else // render parents
1939  {
1940  QListIterator<DotNode> dnli(*nl);
1941  DotNode *pn;
1942  for (dnli.toFirst();(pn=dnli.current());++dnli)
1943  {
1944  if (pn->isVisible())
1945  {
1946  //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",pn->label().data());
1947  writeArrow(t,
1948  gt,
1949  format,
1950  pn,
1951  pn->m_edgeInfo->at(pn->m_children->findRef(this)),
1952  FALSE,
1953  backArrows,
1954  reNumber
1955  );
1956  }
1957  pn->write(t,gt,format,TRUE,FALSE,backArrows,reNumber);
1958  }
1959  }
1960  }
1961  //printf("end DotNode::write(%d) name=%s\n",distance,m_label.data());
1962 }
1963 
1964 void DotNode::writeXML(FTextStream &t,bool isClassGraph)
1965 {
1966  t << " <node id=\"" << m_number << "\">" << endl;
1967  t << " <label>" << convertToXML(m_label) << "</label>" << endl;
1968  if (!m_url.isEmpty())
1969  {
1970  QCString url(m_url);
1971  const char *refPtr = url.data();
1972  char *urlPtr = strchr(url.rawData(),'$');
1973  if (urlPtr)
1974  {
1975  *urlPtr++='\0';
1976  t << " <link refid=\"" << convertToXML(urlPtr) << "\"";
1977  if (*refPtr!='\0')
1978  {
1979  t << " external=\"" << convertToXML(refPtr) << "\"";
1980  }
1981  t << "/>" << endl;
1982  }
1983  }
1984  if (m_children)
1985  {
1988  DotNode *childNode;
1989  EdgeInfo *edgeInfo;
1990  for (;(childNode=nli.current());++nli,++eli)
1991  {
1992  edgeInfo=eli.current();
1993  t << " <childnode refid=\"" << childNode->m_number << "\" relation=\"";
1994  if (isClassGraph)
1995  {
1996  switch(edgeInfo->m_color)
1997  {
1998  case EdgeInfo::Blue: t << "public-inheritance"; break;
1999  case EdgeInfo::Green: t << "protected-inheritance"; break;
2000  case EdgeInfo::Red: t << "private-inheritance"; break;
2001  case EdgeInfo::Purple: t << "usage"; break;
2002  case EdgeInfo::Orange: t << "template-instance"; break;
2003  case EdgeInfo::Orange2: t << "type-constraint"; break;
2004  case EdgeInfo::Grey: ASSERT(0); break;
2005  }
2006  }
2007  else // include graph
2008  {
2009  t << "include";
2010  }
2011  t << "\">" << endl;
2012  if (!edgeInfo->m_label.isEmpty())
2013  {
2014  int p=0;
2015  int ni;
2016  while ((ni=edgeInfo->m_label.find('\n',p))!=-1)
2017  {
2018  t << " <edgelabel>"
2019  << convertToXML(edgeInfo->m_label.mid(p,ni-p))
2020  << "</edgelabel>" << endl;
2021  p=ni+1;
2022  }
2023  t << " <edgelabel>"
2024  << convertToXML(edgeInfo->m_label.right(edgeInfo->m_label.length()-p))
2025  << "</edgelabel>" << endl;
2026  }
2027  t << " </childnode>" << endl;
2028  }
2029  }
2030  t << " </node>" << endl;
2031 }
2032 
2033 void DotNode::writeDocbook(FTextStream &t,bool isClassGraph)
2034 {
2035  t << " <node id=\"" << m_number << "\">" << endl;
2036  t << " <label>" << convertToXML(m_label) << "</label>" << endl;
2037  if (!m_url.isEmpty())
2038  {
2039  QCString url(m_url);
2040  const char *refPtr = url.data();
2041  char *urlPtr = strchr(url.rawData(),'$');
2042  if (urlPtr)
2043  {
2044  *urlPtr++='\0';
2045  t << " <link refid=\"" << convertToXML(urlPtr) << "\"";
2046  if (*refPtr!='\0')
2047  {
2048  t << " external=\"" << convertToXML(refPtr) << "\"";
2049  }
2050  t << "/>" << endl;
2051  }
2052  }
2053  if (m_children)
2054  {
2057  DotNode *childNode;
2058  EdgeInfo *edgeInfo;
2059  for (;(childNode=nli.current());++nli,++eli)
2060  {
2061  edgeInfo=eli.current();
2062  t << " <childnode refid=\"" << childNode->m_number << "\" relation=\"";
2063  if (isClassGraph)
2064  {
2065  switch(edgeInfo->m_color)
2066  {
2067  case EdgeInfo::Blue: t << "public-inheritance"; break;
2068  case EdgeInfo::Green: t << "protected-inheritance"; break;
2069  case EdgeInfo::Red: t << "private-inheritance"; break;
2070  case EdgeInfo::Purple: t << "usage"; break;
2071  case EdgeInfo::Orange: t << "template-instance"; break;
2072  case EdgeInfo::Orange2: t << "type-constraint"; break;
2073  case EdgeInfo::Grey: ASSERT(0); break;
2074  }
2075  }
2076  else // include graph
2077  {
2078  t << "include";
2079  }
2080  t << "\">" << endl;
2081  if (!edgeInfo->m_label.isEmpty())
2082  {
2083  int p=0;
2084  int ni;
2085  while ((ni=edgeInfo->m_label.find('\n',p))!=-1)
2086  {
2087  t << " <edgelabel>"
2088  << convertToXML(edgeInfo->m_label.mid(p,ni-p))
2089  << "</edgelabel>" << endl;
2090  p=ni+1;
2091  }
2092  t << " <edgelabel>"
2093  << convertToXML(edgeInfo->m_label.right(edgeInfo->m_label.length()-p))
2094  << "</edgelabel>" << endl;
2095  }
2096  t << " </childnode>" << endl;
2097  }
2098  }
2099  t << " </node>" << endl;
2100 }
2101 
2102 
2104 {
2105  const char* nodePrefix = " node-";
2106 
2107  t << " node = {" << endl;
2108  t << nodePrefix << "id = " << m_number << ';' << endl;
2109  t << nodePrefix << "label = '" << m_label << "';" << endl;
2110 
2111  if (!m_url.isEmpty())
2112  {
2113  QCString url(m_url);
2114  const char *refPtr = url.data();
2115  char *urlPtr = strchr(url.rawData(),'$');
2116  if (urlPtr)
2117  {
2118  *urlPtr++='\0';
2119  t << nodePrefix << "link = {" << endl << " "
2120  << nodePrefix << "link-id = '" << urlPtr << "';" << endl;
2121 
2122  if (*refPtr!='\0')
2123  {
2124  t << " " << nodePrefix << "link-external = '"
2125  << refPtr << "';" << endl;
2126  }
2127  t << " };" << endl;
2128  }
2129  }
2130  if (m_children)
2131  {
2134  DotNode *childNode;
2135  EdgeInfo *edgeInfo;
2136  for (;(childNode=nli.current());++nli,++eli)
2137  {
2138  edgeInfo=eli.current();
2139  t << " node-child = {" << endl;
2140  t << " child-id = '" << childNode->m_number << "';" << endl;
2141  t << " relation = ";
2142 
2143  switch(edgeInfo->m_color)
2144  {
2145  case EdgeInfo::Blue: t << "public-inheritance"; break;
2146  case EdgeInfo::Green: t << "protected-inheritance"; break;
2147  case EdgeInfo::Red: t << "private-inheritance"; break;
2148  case EdgeInfo::Purple: t << "usage"; break;
2149  case EdgeInfo::Orange: t << "template-instance"; break;
2150  case EdgeInfo::Orange2: t << "type-constraint"; break;
2151  case EdgeInfo::Grey: ASSERT(0); break;
2152  }
2153  t << ';' << endl;
2154 
2155  if (!edgeInfo->m_label.isEmpty())
2156  {
2157  t << " edgelabel = <<_EnD_oF_dEf_TeXt_" << endl
2158  << edgeInfo->m_label << endl
2159  << "_EnD_oF_dEf_TeXt_;" << endl;
2160  }
2161  t << " }; /* node-child */" << endl;
2162  } /* for (;childNode...) */
2163  }
2164  t << " }; /* node */" << endl;
2165 }
2166 
2167 
2169 {
2170  m_written=FALSE;
2171  if (m_parents!=0)
2172  {
2174  DotNode *pn;
2175  for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
2176  {
2177  if (pn->m_written)
2178  {
2179  pn->clearWriteFlag();
2180  }
2181  }
2182  }
2183  if (m_children!=0)
2184  {
2186  DotNode *cn;
2187  for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
2188  {
2189  if (cn->m_written)
2190  {
2191  cn->clearWriteFlag();
2192  }
2193  }
2194  }
2195 }
2196 
2198 {
2199  if (m_children)
2200  {
2202  DotNode *cn;
2203  for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
2204  {
2205  if (cn->m_subgraphId==-1) // uncolored child node
2206  {
2207  cn->m_subgraphId=curColor;
2208  cn->markAsVisible();
2209  cn->colorConnectedNodes(curColor);
2210  //printf("coloring node %s (%p): %d\n",cn->m_label.data(),cn,cn->m_subgraphId);
2211  }
2212  }
2213  }
2214 
2215  if (m_parents)
2216  {
2218  DotNode *pn;
2219  for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
2220  {
2221  if (pn->m_subgraphId==-1) // uncolored parent node
2222  {
2223  pn->m_subgraphId=curColor;
2224  pn->markAsVisible();
2225  pn->colorConnectedNodes(curColor);
2226  //printf("coloring node %s (%p): %d\n",pn->m_label.data(),pn,pn->m_subgraphId);
2227  }
2228  }
2229  }
2230 }
2231 
2233 {
2234  if (!m_url.isEmpty()) return this;
2235  //printf("findDocNode(): `%s'\n",m_label.data());
2236  if (m_parents)
2237  {
2239  DotNode *pn;
2240  for (dnli.toFirst();(pn=dnli.current());++dnli)
2241  {
2242  if (!pn->m_hasDoc)
2243  {
2244  pn->m_hasDoc=TRUE;
2245  const DotNode *dn = pn->findDocNode();
2246  if (dn) return dn;
2247  }
2248  }
2249  }
2250  if (m_children)
2251  {
2253  DotNode *cn;
2254  for (dnli.toFirst();(cn=dnli.current());++dnli)
2255  {
2256  if (!cn->m_hasDoc)
2257  {
2258  cn->m_hasDoc=TRUE;
2259  const DotNode *dn = cn->findDocNode();
2260  if (dn) return dn;
2261  }
2262  }
2263  }
2264  return 0;
2265 }
2266 
2267 //--------------------------------------------------------------------
2268 
2270 
2272  const char *path,const char *fileName,int id) const
2273 {
2274  QDir d(path);
2276  QCString imgExt = getDotImageExtension();
2277  QCString imgFmt = Config_getEnum("DOT_IMAGE_FORMAT");
2278  baseName.sprintf("inherit_graph_%d",id);
2279  QCString imgName = baseName+"."+ imgExt;
2280  QCString mapName = baseName+".map";
2281  QCString absImgName = QCString(d.absPath().data())+"/"+imgName;
2282  QCString absMapName = QCString(d.absPath().data())+"/"+mapName;
2283  QCString absBaseName = QCString(d.absPath().data())+"/"+baseName;
2284  QListIterator<DotNode> dnli2(*m_rootNodes);
2285  DotNode *node;
2286 
2287  // compute md5 checksum of the graph were are about to generate
2288  QGString theGraph;
2289  FTextStream md5stream(&theGraph);
2291  md5stream << " rankdir=\"LR\";" << endl;
2292  for (dnli2.toFirst();(node=dnli2.current());++dnli2)
2293  {
2294  if (node->m_subgraphId==n->m_subgraphId)
2295  {
2296  node->clearWriteFlag();
2297  }
2298  }
2299  for (dnli2.toFirst();(node=dnli2.current());++dnli2)
2300  {
2301  if (node->m_subgraphId==n->m_subgraphId)
2302  {
2304  }
2305  }
2306  writeGraphFooter(md5stream);
2307  resetReNumbering();
2308  uchar md5_sig[16];
2309  QCString sigStr(33);
2310  MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
2311  MD5SigToString(md5_sig,sigStr.rawData(),33);
2312  bool regenerate=FALSE;
2313  if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
2314  !checkDeliverables(absImgName,absMapName))
2315  {
2316  regenerate=TRUE;
2317  // image was new or has changed
2318  QCString dotName=absBaseName+".dot";
2319  QFile f(dotName);
2320  if (!f.open(IO_WriteOnly)) return;
2321  FTextStream t(&f);
2322  t << theGraph;
2323  f.close();
2324  resetReNumbering();
2325 
2326  DotRunner *dotRun = new DotRunner(dotName,d.absPath().data(),TRUE,absImgName);
2327  dotRun->addJob(imgFmt,absImgName);
2328  dotRun->addJob(MAP_CMD,absMapName);
2329  DotManager::instance()->addRun(dotRun);
2330  }
2331  else
2332  {
2333  removeDotGraph(absBaseName+".dot");
2334  }
2335  Doxygen::indexList->addImageFile(imgName);
2336  // write image and map in a table row
2337  QCString mapLabel = escapeCharsInString(n->m_label,FALSE);
2338  if (imgExt=="svg") // vector graphics
2339  {
2340  if (regenerate || !writeSVGFigureLink(out,QCString(),baseName,absImgName))
2341  {
2342  if (regenerate)
2343  {
2345  FALSE,QCString(),FALSE,0);
2346  }
2347  int mapId = DotManager::instance()->addSVGObject(fileName,baseName,
2348  absImgName,QCString());
2349  out << "<!-- SVG " << mapId << " -->" << endl;
2350  }
2351  }
2352  else // normal bitmap
2353  {
2354  out << "<img src=\"" << imgName << "\" border=\"0\" alt=\"\" usemap=\"#"
2355  << mapLabel << "\"/>" << endl;
2356 
2357  if (regenerate || !insertMapFile(out,absMapName,QCString(),mapLabel))
2358  {
2359  int mapId = DotManager::instance()->addMap(fileName,absMapName,QCString(),
2360  FALSE,QCString(),mapLabel);
2361  out << "<!-- MAP " << mapId << " -->" << endl;
2362  }
2363  }
2364 }
2365 
2367  const char *path,const char *fileName) const
2368 {
2369  //printf("DotGfxHierarchyTable::writeGraph(%s)\n",name);
2370  //printf("m_rootNodes=%p count=%d\n",m_rootNodes,m_rootNodes->count());
2371 
2372  if (m_rootSubgraphs->count()==0) return;
2373 
2374  QDir d(path);
2375  // store the original directory
2376  if (!d.exists())
2377  {
2378  err("Output dir %s does not exist!\n",path); exit(1);
2379  }
2380 
2381  // put each connected subgraph of the hierarchy in a row of the HTML output
2382  out << "<table border=\"0\" cellspacing=\"10\" cellpadding=\"0\">" << endl;
2383 
2384  QListIterator<DotNode> dnli(*m_rootSubgraphs);
2385  DotNode *n;
2386  int count=0;
2387  for (dnli.toFirst();(n=dnli.current());++dnli)
2388  {
2389  out << "<tr><td>";
2390  createGraph(n,out,path,fileName,count++);
2391  out << "</td></tr>" << endl;
2392  }
2393  out << "</table>" << endl;
2394 }
2395 
2397 {
2398  //printf("addHierarchy `%s' baseClasses=%d\n",cd->name().data(),cd->baseClasses()->count());
2399  if (cd->subClasses())
2400  {
2401  BaseClassListIterator bcli(*cd->subClasses());
2402  BaseClassDef *bcd;
2403  for ( ; (bcd=bcli.current()) ; ++bcli )
2404  {
2405  ClassDef *bClass=bcd->classDef;
2406  //printf(" Trying sub class=`%s' usedNodes=%d\n",bClass->name().data(),m_usedNodes->count());
2407  if (bClass->isVisibleInHierarchy() && hasVisibleRoot(bClass->baseClasses()))
2408  {
2409  DotNode *bn;
2410  //printf(" Node `%s' Found visible class=`%s'\n",n->m_label.data(),
2411  // bClass->name().data());
2412  if ((bn=m_usedNodes->find(bClass->name()))) // node already present
2413  {
2414  if (n->m_children==0 || n->m_children->findRef(bn)==-1) // no arrow yet
2415  {
2416  n->addChild(bn,bcd->prot);
2417  bn->addParent(n);
2418  //printf(" Adding node %s to existing base node %s (c=%d,p=%d)\n",
2419  // n->m_label.data(),
2420  // bn->m_label.data(),
2421  // bn->m_children ? bn->m_children->count() : 0,
2422  // bn->m_parents ? bn->m_parents->count() : 0
2423  // );
2424  }
2425  //else
2426  //{
2427  // printf(" Class already has an arrow!\n");
2428  //}
2429  }
2430  else
2431  {
2432  QCString tmp_url="";
2433  if (bClass->isLinkable() && !bClass->isHidden())
2434  {
2435  tmp_url=bClass->getReference()+"$"+bClass->getOutputFileBase();
2436  if (!bClass->anchor().isEmpty())
2437  {
2438  tmp_url+="#"+bClass->anchor();
2439  }
2440  }
2441  QCString tooltip = bClass->briefDescriptionAsTooltip();
2442  bn = new DotNode(m_curNodeNumber++,
2443  bClass->displayName(),
2444  tooltip,
2445  tmp_url.data()
2446  );
2447  n->addChild(bn,bcd->prot);
2448  bn->addParent(n);
2449  //printf(" Adding node %s to new base node %s (c=%d,p=%d)\n",
2450  // n->m_label.data(),
2451  // bn->m_label.data(),
2452  // bn->m_children ? bn->m_children->count() : 0,
2453  // bn->m_parents ? bn->m_parents->count() : 0
2454  // );
2455  //printf(" inserting %s (%p)\n",bClass->name().data(),bn);
2456  m_usedNodes->insert(bClass->name(),bn); // add node to the used list
2457  }
2458  if (!bClass->visited && !hideSuper && bClass->subClasses())
2459  {
2460  bool wasVisited=bClass->visited;
2461  bClass->visited=TRUE;
2462  addHierarchy(bn,bClass,wasVisited);
2463  }
2464  }
2465  }
2466  }
2467  //printf("end addHierarchy\n");
2468 }
2469 
2471 {
2473  ClassDef *cd;
2474  for (cli.toLast();(cd=cli.current());--cli)
2475  {
2476  //printf("Trying %s subClasses=%d\n",cd->name().data(),cd->subClasses()->count());
2477  if (cd->getLanguage()==SrcLangExt_VHDL &&
2479  )
2480  {
2481  continue;
2482  }
2483  if (!hasVisibleRoot(cd->baseClasses()) &&
2484  cd->isVisibleInHierarchy()
2485  ) // root node in the forest
2486  {
2487  QCString tmp_url="";
2488  if (cd->isLinkable() && !cd->isHidden())
2489  {
2490  tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
2491  if (!cd->anchor().isEmpty())
2492  {
2493  tmp_url+="#"+cd->anchor();
2494  }
2495  }
2496  //printf("Inserting root class %s\n",cd->name().data());
2497  QCString tooltip = cd->briefDescriptionAsTooltip();
2498  DotNode *n = new DotNode(m_curNodeNumber++,
2499  cd->displayName(),
2500  tooltip,
2501  tmp_url.data());
2502 
2503  //m_usedNodes->clear();
2504  m_usedNodes->insert(cd->name(),n);
2505  m_rootNodes->insert(0,n);
2506  if (!cd->visited && cd->subClasses())
2507  {
2508  addHierarchy(n,cd,cd->visited);
2509  cd->visited=TRUE;
2510  }
2511  }
2512  }
2513 }
2514 
2516 {
2517  m_curNodeNumber=0;
2518  m_rootNodes = new QList<DotNode>;
2519  m_usedNodes = new QDict<DotNode>(1009);
2520  m_usedNodes->setAutoDelete(TRUE);
2521  m_rootSubgraphs = new DotNodeList;
2522 
2523  // build a graph with each class as a node and the inheritance relations
2524  // as edges
2527  addClassList(Doxygen::classSDict);
2528  addClassList(Doxygen::hiddenClasses);
2529  // m_usedNodes now contains all nodes in the graph
2530 
2531  // color the graph into a set of independent subgraphs
2532  bool done=FALSE;
2533  int curColor=0;
2534  QListIterator<DotNode> dnli(*m_rootNodes);
2535  while (!done) // there are still nodes to color
2536  {
2537  DotNode *n;
2538  done=TRUE; // we are done unless there are still uncolored nodes
2539  for (dnli.toLast();(n=dnli.current());--dnli)
2540  {
2541  if (n->m_subgraphId==-1) // not yet colored
2542  {
2543  //printf("Starting at node %s (%p): %d\n",n->m_label.data(),n,curColor);
2544  done=FALSE; // still uncolored nodes
2545  n->m_subgraphId=curColor;
2546  n->markAsVisible();
2547  n->colorConnectedNodes(curColor);
2548  curColor++;
2549  const DotNode *dn=n->findDocNode();
2550  if (dn!=0)
2551  m_rootSubgraphs->inSort(dn);
2552  else
2553  m_rootSubgraphs->inSort(n);
2554  }
2555  }
2556  }
2557 
2558  //printf("Number of independent subgraphs: %d\n",curColor);
2559  //QListIterator<DotNode> dnli2(*m_rootSubgraphs);
2560  //DotNode *n;
2561  //for (dnli2.toFirst();(n=dnli2.current());++dnli2)
2562  //{
2563  // printf("Node %s color=%d (c=%d,p=%d)\n",
2564  // n->m_label.data(),n->m_subgraphId,
2565  // n->m_children?n->m_children->count():0,
2566  // n->m_parents?n->m_parents->count():0);
2567  //}
2568 }
2569 
2571 {
2572  //printf("DotGfxHierarchyTable::~DotGfxHierarchyTable\n");
2573 
2574  //QDictIterator<DotNode> di(*m_usedNodes);
2575  //DotNode *n;
2576  //for (;(n=di.current());++di)
2577  //{
2578  // printf("Node %p: %s\n",n,n->label().data());
2579  //}
2580 
2581  delete m_rootNodes;
2582  delete m_usedNodes;
2583  delete m_rootSubgraphs;
2584 }
2585 
2586 //--------------------------------------------------------------------
2587 
2589 
2591  const char *label,const char *usedName,const char *templSpec,bool base,int distance)
2592 {
2593  if (Config_getBool("HIDE_UNDOC_CLASSES") && !cd->isLinkable()) return;
2594 
2595  int edgeStyle = (label || prot==EdgeInfo::Orange || prot==EdgeInfo::Orange2) ? EdgeInfo::Dashed : EdgeInfo::Solid;
2597  if (cd->isAnonymous())
2598  {
2599  className="anonymous:";
2600  className+=label;
2601  }
2602  else if (usedName) // name is a typedef
2603  {
2604  className=usedName;
2605  }
2606  else if (templSpec) // name has a template part
2607  {
2608  className=insertTemplateSpecifierInScope(cd->name(),templSpec);
2609  }
2610  else // just a normal name
2611  {
2612  className=cd->displayName();
2613  }
2614  //printf("DotClassGraph::addClass(class=`%s',parent=%s,prot=%d,label=%s,dist=%d,usedName=%s,templSpec=%s,base=%d)\n",
2615  // className.data(),n->m_label.data(),prot,label,distance,usedName,templSpec,base);
2616  DotNode *bn = m_usedNodes->find(className);
2617  if (bn) // class already inserted
2618  {
2619  if (base)
2620  {
2621  n->addChild(bn,prot,edgeStyle,label);
2622  bn->addParent(n);
2623  }
2624  else
2625  {
2626  bn->addChild(n,prot,edgeStyle,label);
2627  n->addParent(bn);
2628  }
2629  bn->setDistance(distance);
2630  //printf(" add exiting node %s of %s\n",bn->m_label.data(),n->m_label.data());
2631  }
2632  else // new class
2633  {
2634  QCString displayName=className;
2635  if (Config_getBool("HIDE_SCOPE_NAMES")) displayName=stripScope(displayName);
2636  QCString tmp_url;
2637  if (cd->isLinkable() && !cd->isHidden())
2638  {
2639  tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
2640  if (!cd->anchor().isEmpty())
2641  {
2642  tmp_url+="#"+cd->anchor();
2643  }
2644  }
2645  QCString tooltip = cd->briefDescriptionAsTooltip();
2646  bn = new DotNode(m_curNodeNumber++,
2647  displayName,
2648  tooltip,
2649  tmp_url.data(),
2650  FALSE, // rootNode
2651  cd
2652  );
2653  if (base)
2654  {
2655  n->addChild(bn,prot,edgeStyle,label);
2656  bn->addParent(n);
2657  }
2658  else
2659  {
2660  bn->addChild(n,prot,edgeStyle,label);
2661  n->addParent(bn);
2662  }
2663  bn->setDistance(distance);
2664  m_usedNodes->insert(className,bn);
2665  //printf(" add new child node `%s' to %s hidden=%d url=%s\n",
2666  // className.data(),n->m_label.data(),cd->isHidden(),tmp_url.data());
2667 
2668  buildGraph(cd,bn,base,distance+1);
2669  }
2670 }
2671 
2673 {
2674  while (queue.count()>0)
2675  {
2676  DotNode *n = queue.take(0);
2677  if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
2678  {
2679  bool truncated = FALSE;
2680  if (n->m_children)
2681  {
2683  DotNode *dn;
2684  for (li.toFirst();(dn=li.current());++li)
2685  {
2686  if (!dn->isVisible())
2687  truncated = TRUE;
2688  else
2689  queue.append(dn);
2690  }
2691  }
2692  if (n->m_parents && includeParents)
2693  {
2695  DotNode *dn;
2696  for (li.toFirst();(dn=li.current());++li)
2697  {
2698  if (!dn->isVisible())
2699  truncated = TRUE;
2700  else
2701  queue.append(dn);
2702  }
2703  }
2704  n->markAsTruncated(truncated);
2705  }
2706  }
2707 }
2708 
2710  int maxNodes,bool includeParents)
2711 {
2712  QList<DotNode> childQueue;
2713  QList<DotNode> parentQueue;
2714  QArray<int> childTreeWidth;
2715  QArray<int> parentTreeWidth;
2716  childQueue.append(rootNode);
2717  if (includeParents) parentQueue.append(rootNode);
2718  bool firstNode=TRUE; // flag to force reprocessing rootNode in the parent loop
2719  // despite being marked visible in the child loop
2720  while ((childQueue.count()>0 || parentQueue.count()>0) && maxNodes>0)
2721  {
2722  static int maxDistance = Config_getInt("MAX_DOT_GRAPH_DEPTH");
2723  if (childQueue.count()>0)
2724  {
2725  DotNode *n = childQueue.take(0);
2726  int distance = n->distance();
2727  if (!n->isVisible() && distance<=maxDistance) // not yet processed
2728  {
2729  if (distance>0)
2730  {
2731  int oldSize=(int)childTreeWidth.size();
2732  if (distance>oldSize)
2733  {
2734  childTreeWidth.resize(QMAX(childTreeWidth.size(),(uint)distance));
2735  int i; for (i=oldSize;i<distance;i++) childTreeWidth[i]=0;
2736  }
2737  childTreeWidth[distance-1]+=n->label().length();
2738  }
2739  n->markAsVisible();
2740  maxNodes--;
2741  // add direct children
2742  if (n->m_children)
2743  {
2745  DotNode *dn;
2746  for (li.toFirst();(dn=li.current());++li)
2747  {
2748  childQueue.append(dn);
2749  }
2750  }
2751  }
2752  }
2753  if (includeParents && parentQueue.count()>0)
2754  {
2755  DotNode *n = parentQueue.take(0);
2756  if ((!n->isVisible() || firstNode) && n->distance()<=maxDistance) // not yet processed
2757  {
2758  firstNode=FALSE;
2759  int distance = n->distance();
2760  if (distance>0)
2761  {
2762  int oldSize = (int)parentTreeWidth.size();
2763  if (distance>oldSize)
2764  {
2765  parentTreeWidth.resize(QMAX(parentTreeWidth.size(),(uint)distance));
2766  int i; for (i=oldSize;i<distance;i++) parentTreeWidth[i]=0;
2767  }
2768  parentTreeWidth[distance-1]+=n->label().length();
2769  }
2770  n->markAsVisible();
2771  maxNodes--;
2772  // add direct parents
2773  if (n->m_parents)
2774  {
2776  DotNode *dn;
2777  for (li.toFirst();(dn=li.current());++li)
2778  {
2779  parentQueue.append(dn);
2780  }
2781  }
2782  }
2783  }
2784  }
2785  if (Config_getBool("UML_LOOK")) return FALSE; // UML graph are always top to bottom
2786  int maxWidth=0;
2787  int maxHeight=(int)QMAX(childTreeWidth.size(),parentTreeWidth.size());
2788  uint i;
2789  for (i=0;i<childTreeWidth.size();i++)
2790  {
2791  if (childTreeWidth.at(i)>maxWidth) maxWidth=childTreeWidth.at(i);
2792  }
2793  for (i=0;i<parentTreeWidth.size();i++)
2794  {
2795  if (parentTreeWidth.at(i)>maxWidth) maxWidth=parentTreeWidth.at(i);
2796  }
2797  //printf("max tree width=%d, max tree height=%d\n",maxWidth,maxHeight);
2798  return maxWidth>80 && maxHeight<12; // used metric to decide to render the tree
2799  // from left to right instead of top to bottom,
2800  // with the idea to render very wide trees in
2801  // left to right order.
2802 }
2803 
2805 {
2806  static bool templateRelations = Config_getBool("TEMPLATE_RELATIONS");
2807  //printf("DocClassGraph::buildGraph(%s,distance=%d,base=%d)\n",
2808  // cd->name().data(),distance,base);
2809  // ---- Add inheritance relations
2810 
2811  if (m_graphType == DotNode::Inheritance || m_graphType==DotNode::Collaboration)
2812  {
2813  BaseClassList *bcl = base ? cd->baseClasses() : cd->subClasses();
2814  if (bcl)
2815  {
2816  BaseClassListIterator bcli(*bcl);
2817  BaseClassDef *bcd;
2818  for ( ; (bcd=bcli.current()) ; ++bcli )
2819  {
2820  //printf("-------- inheritance relation %s->%s templ=`%s'\n",
2821  // cd->name().data(),bcd->classDef->name().data(),bcd->templSpecifiers.data());
2822  addClass(bcd->classDef,n,bcd->prot,0,bcd->usedName,
2823  bcd->templSpecifiers,base,distance);
2824  }
2825  }
2826  }
2827  if (m_graphType == DotNode::Collaboration)
2828  {
2829  // ---- Add usage relations
2830 
2831  UsesClassDict *dict =
2832  base ? cd->usedImplementationClasses() :
2834  ;
2835  if (dict)
2836  {
2837  UsesClassDictIterator ucdi(*dict);
2838  UsesClassDef *ucd;
2839  for (;(ucd=ucdi.current());++ucdi)
2840  {
2841  QCString label;
2842  QDictIterator<void> dvi(*ucd->accessors);
2843  const char *s;
2844  bool first=TRUE;
2845  int count=0;
2846  int maxLabels=10;
2847  for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count)
2848  {
2849  if (first)
2850  {
2851  label=s;
2852  first=FALSE;
2853  }
2854  else
2855  {
2856  label+=QCString("\n")+s;
2857  }
2858  }
2859  if (count==maxLabels) label+="\n...";
2860  //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data());
2861  addClass(ucd->classDef,n,EdgeInfo::Purple,label,0,
2862  ucd->templSpecifiers,base,distance);
2863  }
2864  }
2865  }
2866  if (templateRelations && base)
2867  {
2869  if (dict)
2870  {
2871  ConstraintClassDictIterator ccdi(*dict);
2872  ConstraintClassDef *ccd;
2873  for (;(ccd=ccdi.current());++ccdi)
2874  {
2875  QCString label;
2876  QDictIterator<void> dvi(*ccd->accessors);
2877  const char *s;
2878  bool first=TRUE;
2879  int count=0;
2880  int maxLabels=10;
2881  for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count)
2882  {
2883  if (first)
2884  {
2885  label=s;
2886  first=FALSE;
2887  }
2888  else
2889  {
2890  label+=QCString("\n")+s;
2891  }
2892  }
2893  if (count==maxLabels) label+="\n...";
2894  //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data());
2895  addClass(ccd->classDef,n,EdgeInfo::Orange2,label,0,
2896  0,TRUE,distance);
2897  }
2898  }
2899  }
2900 
2901  // ---- Add template instantiation relations
2902 
2903  if (templateRelations)
2904  {
2905  if (base) // template relations for base classes
2906  {
2907  ClassDef *templMaster=cd->templateMaster();
2908  if (templMaster)
2909  {
2910  QDictIterator<ClassDef> cli(*templMaster->getTemplateInstances());
2911  ClassDef *templInstance;
2912  for (;(templInstance=cli.current());++cli)
2913  {
2914  if (templInstance==cd)
2915  {
2916  addClass(templMaster,n,EdgeInfo::Orange,cli.currentKey(),0,
2917  0,TRUE,distance);
2918  }
2919  }
2920  }
2921  }
2922  else // template relations for super classes
2923  {
2924  QDict<ClassDef> *templInstances = cd->getTemplateInstances();
2925  if (templInstances)
2926  {
2927  QDictIterator<ClassDef> cli(*templInstances);
2928  ClassDef *templInstance;
2929  for (;(templInstance=cli.current());++cli)
2930  {
2931  addClass(templInstance,n,EdgeInfo::Orange,cli.currentKey(),0,
2932  0,FALSE,distance);
2933  }
2934  }
2935  }
2936  }
2937 }
2938 
2940 {
2941  //printf("--------------- DotClassGraph::DotClassGraph `%s'\n",cd->displayName().data());
2942  m_graphType = t;
2943  QCString tmp_url="";
2944  if (cd->isLinkable() && !cd->isHidden())
2945  {
2946  tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
2947  if (!cd->anchor().isEmpty())
2948  {
2949  tmp_url+="#"+cd->anchor();
2950  }
2951  }
2952  QCString className = cd->displayName();
2953  QCString tooltip = cd->briefDescriptionAsTooltip();
2954  m_startNode = new DotNode(m_curNodeNumber++,
2955  className,
2956  tooltip,
2957  tmp_url.data(),
2958  TRUE, // is a root node
2959  cd
2960  );
2961  m_startNode->setDistance(0);
2962  m_usedNodes = new QDict<DotNode>(1009);
2963  m_usedNodes->insert(className,m_startNode);
2964 
2965  //printf("Root node %s\n",cd->name().data());
2966  //if (m_recDepth>0)
2967  //{
2968  buildGraph(cd,m_startNode,TRUE,1);
2969  if (t==DotNode::Inheritance) buildGraph(cd,m_startNode,FALSE,1);
2970  //}
2971 
2972  static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES");
2973  //int directChildNodes = 1;
2974  //if (m_startNode->m_children!=0)
2975  // directChildNodes+=m_startNode->m_children->count();
2976  //if (t==DotNode::Inheritance && m_startNode->m_parents!=0)
2977  // directChildNodes+=m_startNode->m_parents->count();
2978  //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
2979  //openNodeQueue.append(m_startNode);
2980  m_lrRank = determineVisibleNodes(m_startNode,maxNodes,t==DotNode::Inheritance);
2981  QList<DotNode> openNodeQueue;
2982  openNodeQueue.append(m_startNode);
2983  determineTruncatedNodes(openNodeQueue,t==DotNode::Inheritance);
2984 
2985  m_diskName = cd->getFileBase().copy();
2986 }
2987 
2989 {
2990  static bool umlLook = Config_getBool("UML_LOOK");
2991  if (m_graphType==DotNode::Inheritance)
2992  return m_startNode->m_children==0 && m_startNode->m_parents==0;
2993  else
2994  return !umlLook && m_startNode->m_children==0;
2995 }
2996 
2998 {
2999  static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES");
3000  int numNodes = 0;
3001  numNodes+= m_startNode->m_children ? m_startNode->m_children->count() : 0;
3002  if (m_graphType==DotNode::Inheritance)
3003  {
3004  numNodes+= m_startNode->m_parents ? m_startNode->m_parents->count() : 0;
3005  }
3006  return numNodes>=maxNodes;
3007 }
3008 
3010 {
3011  deleteNodes(m_startNode);
3012  delete m_usedNodes;
3013 }
3014 
3015 /*! Computes a 16 byte md5 checksum for a given dot graph.
3016  * The md5 checksum is returned as a 32 character ASCII string.
3017  */
3021  bool lrRank,
3022  bool renderParents,
3023  bool backArrows,
3024  const QCString &title,
3025  QCString &graphStr
3026  )
3027 {
3028  bool reNumber=TRUE;
3029 
3030  //printf("computeMd5Signature\n");
3031  QGString buf;
3032  FTextStream md5stream(&buf);
3033  writeGraphHeader(md5stream,title);
3034  if (lrRank)
3035  {
3036  md5stream << " rankdir=\"LR\";" << endl;
3037  }
3038  root->clearWriteFlag();
3039  root->write(md5stream,
3040  gt,
3041  format,
3043  TRUE,
3044  backArrows,
3045  reNumber);
3046  if (renderParents && root->m_parents)
3047  {
3048  QListIterator<DotNode> dnli(*root->m_parents);
3049  DotNode *pn;
3050  for (dnli.toFirst();(pn=dnli.current());++dnli)
3051  {
3052  if (pn->isVisible())
3053  {
3054  root->writeArrow(md5stream, // stream
3055  gt, // graph type
3056  format, // output format
3057  pn, // child node
3058  pn->m_edgeInfo->at(pn->m_children->findRef(root)), // edge info
3059  FALSE, // topDown?
3060  backArrows, // point back?
3061  reNumber // renumber nodes
3062  );
3063  }
3064  pn->write(md5stream, // stream
3065  gt, // graph type
3066  format, // output format
3067  TRUE, // topDown?
3068  FALSE, // toChildren?
3069  backArrows, // backward pointing arrows?
3070  reNumber // renumber nodes?
3071  );
3072  }
3073  }
3074  writeGraphFooter(md5stream);
3075  uchar md5_sig[16];
3076  QCString sigStr(33);
3077  MD5Buffer((const unsigned char *)buf.data(),buf.length(),md5_sig);
3078  MD5SigToString(md5_sig,sigStr.rawData(),33);
3079  if (reNumber)
3080  {
3081  resetReNumbering();
3082  }
3083  graphStr=buf.data();
3084  //printf("md5: %s | file: %s\n",sigStr,baseName.data());
3085  return sigStr;
3086 }
3087 
3088 static bool updateDotGraph(DotNode *root,
3090  const QCString &baseName,
3092  bool lrRank,
3093  bool renderParents,
3094  bool backArrows,
3095  const QCString &title=QCString()
3096  )
3097 {
3098  QCString theGraph;
3099  // TODO: write graph to theGraph, then compute md5 checksum
3101  root,gt,format,lrRank,renderParents,
3102  backArrows,title,theGraph);
3103  QFile f(baseName+".dot");
3104  if (f.open(IO_WriteOnly))
3105  {
3106  FTextStream t(&f);
3107  t << theGraph;
3108  }
3109  return checkAndUpdateMd5Signature(baseName,md5); // graph needs to be regenerated
3110 }
3111 
3113 {
3114  QCString result=m_diskName.copy();
3115  switch (m_graphType)
3116  {
3118  result+="_coll_graph";
3119  break;
3120  //case Interface:
3121  // result+="_intf_graph";
3122  // break;
3123  case DotNode::Inheritance:
3124  result+="_inherit_graph";
3125  break;
3126  default:
3127  ASSERT(0);
3128  break;
3129  }
3130  return result;
3131 }
3132 
3134  GraphOutputFormat graphFormat,
3135  EmbeddedOutputFormat textFormat,
3136  const char *path,
3137  const char *fileName,
3138  const char *relPath,
3139  bool /*isTBRank*/,
3140  bool generateImageMap,
3141  int graphId) const
3142 {
3143  QDir d(path);
3144  // store the original directory
3145  if (!d.exists())
3146  {
3147  err("Output dir %s does not exist!\n",path); exit(1);
3148  }
3149  static bool usePDFLatex = Config_getBool("USE_PDFLATEX");
3150 
3152  QCString mapName;
3153  switch (m_graphType)
3154  {
3156  mapName="coll_map";
3157  break;
3158  //case Interface:
3159  // mapName="intf_map";
3160  // break;
3161  case DotNode::Inheritance:
3162  mapName="inherit_map";
3163  break;
3164  default:
3165  ASSERT(0);
3166  break;
3167  }
3168  baseName = convertNameToFile(diskName());
3169 
3170  // derive target file names from baseName
3171  QCString imgExt = getDotImageExtension();
3172  QCString imgFmt = Config_getEnum("DOT_IMAGE_FORMAT");
3173  QCString absBaseName = d.absPath().utf8()+"/"+baseName;
3174  QCString absDotName = absBaseName+".dot";
3175  QCString absMapName = absBaseName+".map";
3176  QCString absPdfName = absBaseName+".pdf";
3177  QCString absEpsName = absBaseName+".eps";
3178  QCString absImgName = absBaseName+"."+imgExt;
3179 
3180  bool regenerate = FALSE;
3181  if (updateDotGraph(m_startNode,
3182  m_graphType,
3183  absBaseName,
3184  graphFormat,
3185  m_lrRank,
3186  m_graphType==DotNode::Inheritance,
3187  TRUE,
3188  m_startNode->label()
3189  ) ||
3190  !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
3191  usePDFLatex ? absPdfName : absEpsName,
3192  graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
3193  )
3194  {
3195  regenerate=TRUE;
3196  if (graphFormat==GOF_BITMAP) // run dot to create a bitmap image
3197  {
3198  DotRunner *dotRun = new DotRunner(absDotName,
3199  d.absPath().data(),TRUE,absImgName);
3200  dotRun->addJob(imgFmt,absImgName);
3201  if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
3202  DotManager::instance()->addRun(dotRun);
3203 
3204  }
3205  else if (graphFormat==GOF_EPS) // run dot to create a .eps image
3206  {
3207  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
3208  if (usePDFLatex)
3209  {
3210  dotRun->addJob("pdf",absPdfName);
3211  }
3212  else
3213  {
3214  dotRun->addJob("ps",absEpsName);
3215  }
3216  DotManager::instance()->addRun(dotRun);
3217  }
3218  }
3219  Doxygen::indexList->addImageFile(baseName+"."+imgExt);
3220 
3221  if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
3222  {
3223  out << "<para>" << endl;
3224  out << " <figure>" << endl;
3225  out << " <title>";
3226  switch (m_graphType)
3227  {
3229  out << "Collaboration graph";
3230  break;
3231  case DotNode::Inheritance:
3232  out << "Inheritance graph";
3233  break;
3234  default:
3235  ASSERT(0);
3236  break;
3237  }
3238  out << "</title>" << endl;
3239  out << " <mediaobject>" << endl;
3240  out << " <imageobject>" << endl;
3241  out << " <imagedata";
3242  out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"1\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
3243  out << "</imagedata>" << endl;
3244  out << " </imageobject>" << endl;
3245  out << " </mediaobject>" << endl;
3246  out << " </figure>" << endl;
3247  out << "</para>" << endl;
3248  }
3249  else if (graphFormat==GOF_BITMAP && generateImageMap) // produce HTML to include the image
3250  {
3251  QCString mapLabel = escapeCharsInString(m_startNode->m_label,FALSE)+"_"+
3252  escapeCharsInString(mapName,FALSE);
3253  if (imgExt=="svg") // add link to SVG file without map file
3254  {
3255  out << "<div class=\"center\">";
3256  if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
3257  {
3258  if (regenerate)
3259  {
3260  DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
3261  }
3262  int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
3263  out << "<!-- SVG " << mapId << " -->" << endl;
3264  }
3265  out << "</div>" << endl;
3266  }
3267  else // add link to bitmap file with image map
3268  {
3269  out << "<div class=\"center\">";
3270  out << "<img src=\"" << relPath << baseName << "."
3271  << imgExt << "\" border=\"0\" usemap=\"#"
3272  << mapLabel << "\" alt=\"";
3273  switch (m_graphType)
3274  {
3276  out << "Collaboration graph";
3277  break;
3278  case DotNode::Inheritance:
3279  out << "Inheritance graph";
3280  break;
3281  default:
3282  ASSERT(0);
3283  break;
3284  }
3285  out << "\"/>";
3286  out << "</div>" << endl;
3287  if (regenerate || !insertMapFile(out,absMapName,relPath,mapLabel))
3288  {
3289  int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
3290  FALSE,QCString(),mapLabel);
3291  out << "<!-- MAP " << mapId << " -->" << endl;
3292  }
3293  }
3294  }
3295  else if (graphFormat==GOF_EPS) // produce tex to include the .eps image
3296  {
3297  if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
3298  {
3299  int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE /*TRUE*/);
3300  out << endl << "% FIG " << figId << endl;
3301  }
3302  }
3303  if (!regenerate) removeDotGraph(absDotName);
3304 
3305  return baseName;
3306 }
3307 
3308 //--------------------------------------------------------------------
3309 
3311 {
3312  QDictIterator<DotNode> dni(*m_usedNodes);
3313  DotNode *node;
3314  for (;(node=dni.current());++dni)
3315  {
3316  node->writeXML(t,TRUE);
3317  }
3318 }
3319 
3321 {
3322  QDictIterator<DotNode> dni(*m_usedNodes);
3323  DotNode *node;
3324  for (;(node=dni.current());++dni)
3325  {
3326  node->writeDocbook(t,TRUE);
3327  }
3328 }
3329 
3331 {
3332  QDictIterator<DotNode> dni(*m_usedNodes);
3333  DotNode *node;
3334  for (;(node=dni.current());++dni)
3335  {
3336  node->writeDEF(t);
3337  }
3338 }
3339 
3340 //--------------------------------------------------------------------
3341 
3343 
3345 {
3346  QList<IncludeInfo> *includeFiles =
3347  m_inverse ? fd->includedByFileList() : fd->includeFileList();
3348  if (includeFiles)
3349  {
3350  QListIterator<IncludeInfo> ili(*includeFiles);
3351  IncludeInfo *ii;
3352  for (;(ii=ili.current());++ili)
3353  {
3354  FileDef *bfd = ii->fileDef;
3355  QCString in = ii->includeName;
3356  //printf(">>>> in=`%s' bfd=%p\n",ii->includeName.data(),bfd);
3357  bool doc=TRUE,src=FALSE;
3358  if (bfd)
3359  {
3360  in = bfd->absFilePath();
3361  doc = bfd->isLinkable() && !bfd->isHidden();
3362  src = bfd->generateSourceFile();
3363  }
3364  if (doc || src || !Config_getBool("HIDE_UNDOC_RELATIONS"))
3365  {
3366  QCString url="";
3367  if (bfd) url=bfd->getOutputFileBase().copy();
3368  if (!doc && src)
3369  {
3370  url=bfd->getSourceFileBase();
3371  }
3372  DotNode *bn = m_usedNodes->find(in);
3373  if (bn) // file is already a node in the graph
3374  {
3375  n->addChild(bn,0,0,0);
3376  bn->addParent(n);
3377  bn->setDistance(distance);
3378  }
3379  else
3380  {
3381  QCString tmp_url;
3382  QCString tooltip;
3383  if (bfd)
3384  {
3385  tmp_url=doc || src ? bfd->getReference()+"$"+url : QCString();
3386  tooltip = bfd->briefDescriptionAsTooltip();
3387  }
3388  bn = new DotNode(
3389  m_curNodeNumber++, // n
3390  ii->includeName, // label
3391  tooltip, // tip
3392  tmp_url, // url
3393  FALSE, // rootNode
3394  0 // cd
3395  );
3396  n->addChild(bn,0,0,0);
3397  bn->addParent(n);
3398  m_usedNodes->insert(in,bn);
3399  bn->setDistance(distance);
3400 
3401  if (bfd) buildGraph(bn,bfd,distance+1);
3402  }
3403  }
3404  }
3405  }
3406 }
3407 
3409 {
3410  while (queue.count()>0 && maxNodes>0)
3411  {
3412  static int maxDistance = Config_getInt("MAX_DOT_GRAPH_DEPTH");
3413  DotNode *n = queue.take(0);
3414  if (!n->isVisible() && n->distance()<=maxDistance) // not yet processed
3415  {
3416  n->markAsVisible();
3417  maxNodes--;
3418  // add direct children
3419  if (n->m_children)
3420  {
3422  DotNode *dn;
3423  for (li.toFirst();(dn=li.current());++li)
3424  {
3425  queue.append(dn);
3426  }
3427  }
3428  }
3429  }
3430 }
3431 
3433 {
3434  while (queue.count()>0)
3435  {
3436  DotNode *n = queue.take(0);
3437  if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
3438  {
3439  bool truncated = FALSE;
3440  if (n->m_children)
3441  {
3443  DotNode *dn;
3444  for (li.toFirst();(dn=li.current());++li)
3445  {
3446  if (!dn->isVisible())
3447  truncated = TRUE;
3448  else
3449  queue.append(dn);
3450  }
3451  }
3452  n->markAsTruncated(truncated);
3453  }
3454  }
3455 }
3456 
3457 
3459 {
3460  m_inverse = inverse;
3461  ASSERT(fd!=0);
3462  m_diskName = fd->getFileBase().copy();
3463  QCString tmp_url=fd->getReference()+"$"+fd->getFileBase();
3464  m_startNode = new DotNode(m_curNodeNumber++,
3465  fd->docName(),
3466  "",
3467  tmp_url.data(),
3468  TRUE // root node
3469  );
3470  m_startNode->setDistance(0);
3471  m_usedNodes = new QDict<DotNode>(1009);
3472  m_usedNodes->insert(fd->absFilePath(),m_startNode);
3473  buildGraph(m_startNode,fd,1);
3474 
3475  static int nodes = Config_getInt("DOT_GRAPH_MAX_NODES");
3476  int maxNodes = nodes;
3477  //int directChildNodes = 1;
3478  //if (m_startNode->m_children!=0)
3479  // directChildNodes+=m_startNode->m_children->count();
3480  //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
3481  QList<DotNode> openNodeQueue;
3482  openNodeQueue.append(m_startNode);
3483  determineVisibleNodes(openNodeQueue,maxNodes);
3484  openNodeQueue.clear();
3485  openNodeQueue.append(m_startNode);
3486  determineTruncatedNodes(openNodeQueue);
3487 }
3488 
3490 {
3491  deleteNodes(m_startNode);
3492  delete m_usedNodes;
3493 }
3494 
3496 {
3497  QCString result=m_diskName.copy();
3498  if (m_inverse) result+="_dep";
3499  result+="_incl";
3500  return convertNameToFile(result);
3501 }
3502 
3504  GraphOutputFormat graphFormat,
3505  EmbeddedOutputFormat textFormat,
3506  const char *path,
3507  const char *fileName,
3508  const char *relPath,
3509  bool generateImageMap,
3510  int graphId
3511  ) const
3512 {
3513  QDir d(path);
3514  // store the original directory
3515  if (!d.exists())
3516  {
3517  err("Output dir %s does not exist!\n",path); exit(1);
3518  }
3519  static bool usePDFLatex = Config_getBool("USE_PDFLATEX");
3520 
3521  QCString baseName=m_diskName;
3522  if (m_inverse) baseName+="_dep";
3523  baseName+="_incl";
3524  baseName=convertNameToFile(baseName);
3525  QCString mapName=escapeCharsInString(m_startNode->m_label,FALSE);
3526  if (m_inverse) mapName+="dep";
3527 
3528  QCString imgExt = getDotImageExtension();
3529  QCString imgFmt = Config_getEnum("DOT_IMAGE_FORMAT");
3530  QCString absBaseName = d.absPath().utf8()+"/"+baseName;
3531  QCString absDotName = absBaseName+".dot";
3532  QCString absMapName = absBaseName+".map";
3533  QCString absPdfName = absBaseName+".pdf";
3534  QCString absEpsName = absBaseName+".eps";
3535  QCString absImgName = absBaseName+"."+imgExt;
3536 
3537  bool regenerate = FALSE;
3538  if (updateDotGraph(m_startNode,
3540  absBaseName,
3541  graphFormat,
3542  FALSE, // lrRank
3543  FALSE, // renderParents
3544  m_inverse, // backArrows
3545  m_startNode->label()
3546  ) ||
3547  !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
3548  usePDFLatex ? absPdfName : absEpsName,
3549  graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
3550  )
3551  {
3552  regenerate=TRUE;
3553  if (graphFormat==GOF_BITMAP)
3554  {
3555  // run dot to create a bitmap image
3556  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
3557  dotRun->addJob(imgFmt,absImgName);
3558  if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
3559  DotManager::instance()->addRun(dotRun);
3560  }
3561  else if (graphFormat==GOF_EPS)
3562  {
3563  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
3564  if (usePDFLatex)
3565  {
3566  dotRun->addJob("pdf",absPdfName);
3567  }
3568  else
3569  {
3570  dotRun->addJob("ps",absEpsName);
3571  }
3572  DotManager::instance()->addRun(dotRun);
3573  }
3574  }
3575  Doxygen::indexList->addImageFile(baseName+"."+imgExt);
3576 
3577  if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
3578  {
3579  out << "<para>" << endl;
3580  out << " <figure>" << endl;
3581  out << " <title>Dependency diagram";
3582  out << "</title>" << endl;
3583  out << " <mediaobject>" << endl;
3584  out << " <imageobject>" << endl;
3585  out << " <imagedata";
3586  out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"1\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
3587  out << "</imagedata>" << endl;
3588  out << " </imageobject>" << endl;
3589  out << " </mediaobject>" << endl;
3590  out << " </figure>" << endl;
3591  out << "</para>" << endl;
3592  }
3593  else if (graphFormat==GOF_BITMAP && generateImageMap)
3594  {
3595  if (imgExt=="svg") // Scalable vector graphics
3596  {
3597  out << "<div class=\"center\">";
3598  if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
3599  {
3600  if (regenerate)
3601  {
3602  DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
3603  }
3604  int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
3605  out << "<!-- SVG " << mapId << " -->" << endl;
3606  }
3607  out << "</div>" << endl;
3608  }
3609  else // bitmap graphics
3610  {
3611  out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." << imgExt << "\" border=\"0\" usemap=\"#" << mapName << "\" alt=\"\"/>";
3612  out << "</div>" << endl;
3613 
3614  QCString absMapName = absBaseName+".map";
3615  if (regenerate || !insertMapFile(out,absMapName,relPath,mapName))
3616  {
3617  int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
3618  FALSE,QCString(),mapName);
3619  out << "<!-- MAP " << mapId << " -->" << endl;
3620  }
3621  }
3622  }
3623  else if (graphFormat==GOF_EPS) // encapsulated postscript
3624  {
3625  if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
3626  {
3627  int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
3628  out << endl << "% FIG " << figId << endl;
3629  }
3630  }
3631  if (!regenerate) removeDotGraph(absDotName);
3632 
3633  return baseName;
3634 }
3635 
3637 {
3638  return m_startNode->m_children==0;
3639 }
3640 
3642 {
3643  static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES");
3644  int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0;
3645  return numNodes>=maxNodes;
3646 }
3647 
3649 {
3650  QDictIterator<DotNode> dni(*m_usedNodes);
3651  DotNode *node;
3652  for (;(node=dni.current());++dni)
3653  {
3654  node->writeXML(t,FALSE);
3655  }
3656 }
3657 
3659 {
3660  QDictIterator<DotNode> dni(*m_usedNodes);
3661  DotNode *node;
3662  for (;(node=dni.current());++dni)
3663  {
3664  node->writeDocbook(t,FALSE);
3665  }
3666 }
3667 
3668 //-------------------------------------------------------------
3669 
3671 
3673 {
3674  MemberSDict *refs = m_inverse ? md->getReferencedByMembers() : md->getReferencesMembers();
3675  if (refs)
3676  {
3677  MemberSDict::Iterator mri(*refs);
3678  MemberDef *rmd;
3679  for (;(rmd=mri.current());++mri)
3680  {
3681  if (rmd->showInCallGraph())
3682  {
3683  QCString uniqueId;
3684  uniqueId=rmd->getReference()+"$"+
3685  rmd->getOutputFileBase()+"#"+rmd->anchor();
3686  DotNode *bn = m_usedNodes->find(uniqueId);
3687  if (bn) // file is already a node in the graph
3688  {
3689  n->addChild(bn,0,0,0);
3690  bn->addParent(n);
3691  bn->setDistance(distance);
3692  }
3693  else
3694  {
3695  QCString name;
3696  if (Config_getBool("HIDE_SCOPE_NAMES"))
3697  {
3698  name = rmd->getOuterScope()==m_scope ?
3699  rmd->name() : rmd->qualifiedName();
3700  }
3701  else
3702  {
3703  name = rmd->qualifiedName();
3704  }
3705  QCString tooltip = rmd->briefDescriptionAsTooltip();
3706  bn = new DotNode(
3707  m_curNodeNumber++,
3708  linkToText(rmd->getLanguage(),name,FALSE),
3709  tooltip,
3710  uniqueId,
3711  0 //distance
3712  );
3713  n->addChild(bn,0,0,0);
3714  bn->addParent(n);
3715  bn->setDistance(distance);
3716  m_usedNodes->insert(uniqueId,bn);
3717 
3718  buildGraph(bn,rmd,distance+1);
3719  }
3720  }
3721  }
3722  }
3723 }
3724 
3726 {
3727  while (queue.count()>0 && maxNodes>0)
3728  {
3729  static int maxDistance = Config_getInt("MAX_DOT_GRAPH_DEPTH");
3730  DotNode *n = queue.take(0);
3731  if (!n->isVisible() && n->distance()<=maxDistance) // not yet processed
3732  {
3733  n->markAsVisible();
3734  maxNodes--;
3735  // add direct children
3736  if (n->m_children)
3737  {
3739  DotNode *dn;
3740  for (li.toFirst();(dn=li.current());++li)
3741  {
3742  queue.append(dn);
3743  }
3744  }
3745  }
3746  }
3747 }
3748 
3750 {
3751  while (queue.count()>0)
3752  {
3753  DotNode *n = queue.take(0);
3754  if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
3755  {
3756  bool truncated = FALSE;
3757  if (n->m_children)
3758  {
3760  DotNode *dn;
3761  for (li.toFirst();(dn=li.current());++li)
3762  {
3763  if (!dn->isVisible())
3764  truncated = TRUE;
3765  else
3766  queue.append(dn);
3767  }
3768  }
3769  n->markAsTruncated(truncated);
3770  }
3771  }
3772 }
3773 
3774 
3775 
3777 {
3778  m_inverse = inverse;
3779  m_diskName = md->getOutputFileBase()+"_"+md->anchor();
3780  m_scope = md->getOuterScope();
3781  QCString uniqueId;
3782  uniqueId = md->getReference()+"$"+
3783  md->getOutputFileBase()+"#"+md->anchor();
3784  QCString name;
3785  if (Config_getBool("HIDE_SCOPE_NAMES"))
3786  {
3787  name = md->name();
3788  }
3789  else
3790  {
3791  name = md->qualifiedName();
3792  }
3793  m_startNode = new DotNode(m_curNodeNumber++,
3795  "",
3796  uniqueId.data(),
3797  TRUE // root node
3798  );
3799  m_startNode->setDistance(0);
3800  m_usedNodes = new QDict<DotNode>(1009);
3801  m_usedNodes->insert(uniqueId,m_startNode);
3802  buildGraph(m_startNode,md,1);
3803 
3804  static int nodes = Config_getInt("DOT_GRAPH_MAX_NODES");
3805  int maxNodes = nodes;
3806  //int directChildNodes = 1;
3807  //if (m_startNode->m_children!=0)
3808  // directChildNodes+=m_startNode->m_children->count();
3809  //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
3810  QList<DotNode> openNodeQueue;
3811  openNodeQueue.append(m_startNode);
3812  determineVisibleNodes(openNodeQueue,maxNodes);
3813  openNodeQueue.clear();
3814  openNodeQueue.append(m_startNode);
3815  determineTruncatedNodes(openNodeQueue);
3816 }
3817 
3819 {
3820  deleteNodes(m_startNode);
3821  delete m_usedNodes;
3822 }
3823 
3825  EmbeddedOutputFormat textFormat,
3826  const char *path,const char *fileName,
3827  const char *relPath,bool generateImageMap,int
3828  graphId) const
3829 {
3830  QDir d(path);
3831  // store the original directory
3832  if (!d.exists())
3833  {
3834  err("Output dir %s does not exist!\n",path); exit(1);
3835  }
3836  static bool usePDFLatex = Config_getBool("USE_PDFLATEX");
3837 
3838  QCString baseName = m_diskName + (m_inverse ? "_icgraph" : "_cgraph");
3839  QCString mapName = baseName;
3840 
3841  QCString imgExt = getDotImageExtension();
3842  QCString imgFmt = Config_getEnum("DOT_IMAGE_FORMAT");
3843  QCString absBaseName = d.absPath().utf8()+"/"+baseName;
3844  QCString absDotName = absBaseName+".dot";
3845  QCString absMapName = absBaseName+".map";
3846  QCString absPdfName = absBaseName+".pdf";
3847  QCString absEpsName = absBaseName+".eps";
3848  QCString absImgName = absBaseName+"."+imgExt;
3849 
3850  bool regenerate = FALSE;
3851  if (updateDotGraph(m_startNode,
3853  absBaseName,
3854  graphFormat,
3855  TRUE, // lrRank
3856  FALSE, // renderParents
3857  m_inverse, // backArrows
3858  m_startNode->label()
3859  ) ||
3860  !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
3861  usePDFLatex ? absPdfName : absEpsName,
3862  graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
3863  )
3864  {
3865  regenerate=TRUE;
3866  if (graphFormat==GOF_BITMAP)
3867  {
3868  // run dot to create a bitmap image
3869  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
3870  dotRun->addJob(imgFmt,absImgName);
3871  if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
3872  DotManager::instance()->addRun(dotRun);
3873 
3874  }
3875  else if (graphFormat==GOF_EPS)
3876  {
3877  // run dot to create a .eps image
3878  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
3879  if (usePDFLatex)
3880  {
3881  dotRun->addJob("pdf",absPdfName);
3882  }
3883  else
3884  {
3885  dotRun->addJob("ps",absEpsName);
3886  }
3887  DotManager::instance()->addRun(dotRun);
3888 
3889  }
3890  }
3891  Doxygen::indexList->addImageFile(baseName+"."+imgExt);
3892 
3893  if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
3894  {
3895  out << "<para>" << endl;
3896  out << " <figure>" << endl;
3897  out << " <title>Call diagram";
3898  out << "</title>" << endl;
3899  out << " <mediaobject>" << endl;
3900  out << " <imageobject>" << endl;
3901  out << " <imagedata";
3902  out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"1\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
3903  out << "</imagedata>" << endl;
3904  out << " </imageobject>" << endl;
3905  out << " </mediaobject>" << endl;
3906  out << " </figure>" << endl;
3907  out << "</para>" << endl;
3908  }
3909  else if (graphFormat==GOF_BITMAP && generateImageMap)
3910  {
3911  if (imgExt=="svg") // Scalable vector graphics
3912  {
3913  out << "<div class=\"center\">";
3914  if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
3915  {
3916  if (regenerate)
3917  {
3918  DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
3919  }
3920  int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
3921  out << "<!-- SVG " << mapId << " -->" << endl;
3922  }
3923  out << "</div>" << endl;
3924  }
3925  else // bitmap graphics
3926  {
3927  out << "<div class=\"center\"><img src=\"" << relPath << baseName << "."
3928  << imgExt << "\" border=\"0\" usemap=\"#"
3929  << mapName << "\" alt=\"";
3930  out << "\"/>";
3931  out << "</div>" << endl;
3932 
3933  if (regenerate || !insertMapFile(out,absMapName,relPath,mapName))
3934  {
3935  int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
3936  FALSE,QCString(),mapName);
3937  out << "<!-- MAP " << mapId << " -->" << endl;
3938  }
3939  }
3940  }
3941  else if (graphFormat==GOF_EPS) // encapsulated postscript
3942  {
3943  if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
3944  {
3945  int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
3946  out << endl << "% FIG " << figId << endl;
3947  }
3948  }
3949  if (!regenerate) removeDotGraph(absDotName);
3950 
3951  return baseName;
3952 }
3953 
3955 {
3956  return m_startNode->m_children==0;
3957 }
3958 
3960 {
3961  static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES");
3962  int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0;
3963  return numNodes>=maxNodes;
3964 }
3965 
3966 //-------------------------------------------------------------
3967 static void writeDotDirDepGraph(FTextStream &t,DirDef *dd,bool linkRelations);
3968 
3970 {
3971 }
3972 
3974 {
3975 }
3976 
3978  GraphOutputFormat graphFormat,
3979  EmbeddedOutputFormat textFormat,
3980  const char *path,
3981  const char *fileName,
3982  const char *relPath,
3983  bool generateImageMap,
3984  int graphId,
3985  bool linkRelations) const
3986 {
3987  QDir d(path);
3988  // store the original directory
3989  if (!d.exists())
3990  {
3991  err("Output dir %s does not exist!\n",path); exit(1);
3992  }
3993  static bool usePDFLatex = Config_getBool("USE_PDFLATEX");
3994 
3995  QCString baseName=m_dir->getOutputFileBase()+"_dep";
3996  QCString mapName=escapeCharsInString(baseName,FALSE);
3997 
3998  QCString imgExt = getDotImageExtension();
3999  QCString imgFmt = Config_getEnum("DOT_IMAGE_FORMAT");
4000  QCString absBaseName = d.absPath().utf8()+"/"+baseName;
4001  QCString absDotName = absBaseName+".dot";
4002  QCString absMapName = absBaseName+".map";
4003  QCString absPdfName = absBaseName+".pdf";
4004  QCString absEpsName = absBaseName+".eps";
4005  QCString absImgName = absBaseName+"."+imgExt;
4006 
4007  // compute md5 checksum of the graph were are about to generate
4008  QGString theGraph;
4009  FTextStream md5stream(&theGraph);
4010  //m_dir->writeDepGraph(md5stream);
4011  writeDotDirDepGraph(md5stream,m_dir,linkRelations);
4012  uchar md5_sig[16];
4013  QCString sigStr(33);
4014  MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
4015  MD5SigToString(md5_sig,sigStr.rawData(),33);
4016  bool regenerate=FALSE;
4017  if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
4018  !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
4019  usePDFLatex ? absPdfName : absEpsName,
4020  graphFormat==GOF_BITMAP && generateImageMap ? absMapName : QCString())
4021  )
4022  {
4023  regenerate=TRUE;
4024 
4025  QFile f(absDotName);
4026  if (!f.open(IO_WriteOnly))
4027  {
4028  err("Cannot create file %s.dot for writing!\n",baseName.data());
4029  }
4030  FTextStream t(&f);
4031  t << theGraph.data();
4032  f.close();
4033 
4034  if (graphFormat==GOF_BITMAP)
4035  {
4036  // run dot to create a bitmap image
4037  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
4038  dotRun->addJob(imgFmt,absImgName);
4039  if (generateImageMap) dotRun->addJob(MAP_CMD,absMapName);
4040  DotManager::instance()->addRun(dotRun);
4041  }
4042  else if (graphFormat==GOF_EPS)
4043  {
4044  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
4045  if (usePDFLatex)
4046  {
4047  dotRun->addJob("pdf",absPdfName);
4048  }
4049  else
4050  {
4051  dotRun->addJob("ps",absEpsName);
4052  }
4053  DotManager::instance()->addRun(dotRun);
4054  }
4055  }
4056  Doxygen::indexList->addImageFile(baseName+"."+imgExt);
4057 
4058  if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
4059  {
4060  out << "<para>" << endl;
4061  out << " <figure>" << endl;
4062  out << " <title>Directory Dependency diagram";
4063  out << "</title>" << endl;
4064  out << " <mediaobject>" << endl;
4065  out << " <imageobject>" << endl;
4066  out << " <imagedata";
4067  out << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"1\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
4068  out << "</imagedata>" << endl;
4069  out << " </imageobject>" << endl;
4070  out << " </mediaobject>" << endl;
4071  out << " </figure>" << endl;
4072  out << "</para>" << endl;
4073  }
4074  else if (graphFormat==GOF_BITMAP && generateImageMap)
4075  {
4076  if (imgExt=="svg") // Scalable vector graphics
4077  {
4078  out << "<div class=\"center\">";
4079  if (regenerate || !writeSVGFigureLink(out,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
4080  {
4081  if (regenerate)
4082  {
4083  DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
4084  }
4085  int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
4086  out << "<!-- SVG " << mapId << " -->" << endl;
4087  }
4088  out << "</div>" << endl;
4089  }
4090  else // bitmap graphics
4091  {
4092  out << "<div class=\"center\"><img src=\"" << relPath << baseName << "."
4093  << imgExt << "\" border=\"0\" usemap=\"#"
4094  << mapName << "\" alt=\"";
4095  out << convertToXML(m_dir->displayName());
4096  out << "\"/>";
4097  out << "</div>" << endl;
4098 
4099  if (regenerate || !insertMapFile(out,absMapName,relPath,mapName))
4100  {
4101  int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
4102  TRUE,QCString(),mapName);
4103  out << "<!-- MAP " << mapId << " -->" << endl;
4104  }
4105  }
4106  }
4107  else if (graphFormat==GOF_EPS)
4108  {
4109  if (regenerate || !writeVecGfxFigure(out,baseName,absBaseName))
4110  {
4111  int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
4112  out << endl << "% FIG " << figId << endl;
4113  }
4114  }
4115  if (!regenerate) removeDotGraph(absDotName);
4116 
4117  return baseName;
4118 }
4119 
4121 {
4122  return m_dir->depGraphIsTrivial();
4123 }
4124 
4125 //-------------------------------------------------------------
4126 
4127 void generateGraphLegend(const char *path)
4128 {
4129  QDir d(path);
4130  // store the original directory
4131  if (!d.exists())
4132  {
4133  err("Output dir %s does not exist!\n",path); exit(1);
4134  }
4135 
4136  QGString theGraph;
4137  FTextStream md5stream(&theGraph);
4139  md5stream << " Node9 [shape=\"box\",label=\"Inherited\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",fillcolor=\"grey75\",style=\"filled\" fontcolor=\"black\"];\n";
4140  md5stream << " Node10 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
4141  md5stream << " Node10 [shape=\"box\",label=\"PublicBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPublicBase" << Doxygen::htmlFileExtension << "\"];\n";
4142  md5stream << " Node11 -> Node10 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
4143  md5stream << " Node11 [shape=\"box\",label=\"Truncated\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"red\",URL=\"$classTruncated" << Doxygen::htmlFileExtension << "\"];\n";
4144  md5stream << " Node13 -> Node9 [dir=\"back\",color=\"darkgreen\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
4145  md5stream << " Node13 [shape=\"box\",label=\"ProtectedBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classProtectedBase" << Doxygen::htmlFileExtension << "\"];\n";
4146  md5stream << " Node14 -> Node9 [dir=\"back\",color=\"firebrick4\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
4147  md5stream << " Node14 [shape=\"box\",label=\"PrivateBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPrivateBase" << Doxygen::htmlFileExtension << "\"];\n";
4148  md5stream << " Node15 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
4149  md5stream << " Node15 [shape=\"box\",label=\"Undocumented\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"grey75\"];\n";
4150  md5stream << " Node16 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
4151  md5stream << " Node16 [shape=\"box\",label=\"Templ< int >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
4152  md5stream << " Node17 -> Node16 [dir=\"back\",color=\"orange\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"< int >\",fontname=\"" << FONTNAME << "\"];\n";
4153  md5stream << " Node17 [shape=\"box\",label=\"Templ< T >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
4154  md5stream << " Node18 -> Node9 [dir=\"back\",color=\"darkorchid3\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"m_usedClass\",fontname=\"" << FONTNAME << "\"];\n";
4155  md5stream << " Node18 [shape=\"box\",label=\"Used\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classUsed" << Doxygen::htmlFileExtension << "\"];\n";
4156  writeGraphFooter(md5stream);
4157  uchar md5_sig[16];
4158  QCString sigStr(33);
4159  MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
4160  MD5SigToString(md5_sig,sigStr.rawData(),33);
4161  QCString absBaseName = (QCString)path+"/graph_legend";
4162  QCString absDotName = absBaseName+".dot";
4163  QCString imgExt = getDotImageExtension();
4164  QCString imgFmt = Config_getEnum("DOT_IMAGE_FORMAT");
4165  QCString imgName = "graph_legend."+imgExt;
4166  QCString absImgName = absBaseName+"."+imgExt;
4167  if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
4168  !checkDeliverables(absImgName))
4169  {
4170  QFile dotFile(absDotName);
4171  if (!dotFile.open(IO_WriteOnly))
4172  {
4173  err("Could not open file %s for writing\n",dotFile.name().data());
4174  return;
4175  }
4176 
4177  FTextStream dotText(&dotFile);
4178  dotText << theGraph;
4179  dotFile.close();
4180 
4181  // run dot to generate the a bitmap image from the graph
4182 
4183  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),TRUE,absImgName);
4184  dotRun->addJob(imgFmt,absImgName);
4185  DotManager::instance()->addRun(dotRun);
4186  }
4187  else
4188  {
4189  removeDotGraph(absDotName);
4190  }
4191  Doxygen::indexList->addImageFile(imgName);
4192 
4193  if (imgExt=="svg")
4194  {
4196  absBaseName+Config_getString("HTML_FILE_EXTENSION"),
4197  "graph_legend",
4198  absImgName,QCString());
4199  }
4200 
4201 }
4202 
4203 void writeDotGraphFromFile(const char *inFile,const char *outDir,
4204  const char *outFile,GraphOutputFormat format)
4205 {
4206  QDir d(outDir);
4207  if (!d.exists())
4208  {
4209  err("Output dir %s does not exist!\n",outDir); exit(1);
4210  }
4211 
4212  QCString imgExt = getDotImageExtension();
4213  QCString imgFmt = Config_getEnum("DOT_IMAGE_FORMAT");
4214  QCString imgName = (QCString)outFile+"."+imgExt;
4215  QCString absImgName = d.absPath().utf8()+"/"+imgName;
4216  QCString absOutFile = d.absPath().utf8()+"/"+outFile;
4217 
4218  DotRunner dotRun(inFile,d.absPath().data(),FALSE,absImgName);
4219  if (format==GOF_BITMAP)
4220  dotRun.addJob(imgFmt,absImgName);
4221  else // format==GOF_EPS
4222  {
4223  if (Config_getBool("USE_PDFLATEX"))
4224  {
4225  dotRun.addJob("pdf",absOutFile+".pdf");
4226  }
4227  else
4228  {
4229  dotRun.addJob("ps",absOutFile+".eps");
4230  }
4231  }
4232 
4233  dotRun.preventCleanUp();
4234  if (!dotRun.run())
4235  {
4236  return;
4237  }
4238 
4239  if (format==GOF_BITMAP) checkDotResult(getDotImageExtension(),absImgName);
4240 
4241  Doxygen::indexList->addImageFile(imgName);
4242 
4243 }
4244 
4245 
4246 /*! Writes user defined image map to the output.
4247  * \param t text stream to write to
4248  * \param inFile just the basename part of the filename
4249  * \param outDir output directory
4250  * \param relPath relative path the to root of the output dir
4251  * \param baseName the base name of the output files
4252  * \param context the scope in which this graph is found (for resolving links)
4253  * \param graphId a unique id for this graph, use for dynamic sections
4254  */
4256  const QCString &inFile, const QCString &outDir,
4257  const QCString &relPath, const QCString &baseName,
4258  const QCString &context,int graphId)
4259 {
4260 
4261  QDir d(outDir);
4262  if (!d.exists())
4263  {
4264  err("Output dir %s does not exist!\n",outDir.data()); exit(1);
4265  }
4266 
4267  QCString mapName = baseName+".map";
4268  QCString imgExt = getDotImageExtension();
4269  QCString imgFmt = Config_getEnum("DOT_IMAGE_FORMAT");
4270  QCString imgName = baseName+"."+imgExt;
4271  QCString absOutFile = d.absPath().utf8()+"/"+mapName;
4272 
4273  DotRunner dotRun(inFile,d.absPath().data(),FALSE);
4274  dotRun.addJob(MAP_CMD,absOutFile);
4275  dotRun.preventCleanUp();
4276  if (!dotRun.run())
4277  {
4278  return;
4279  }
4280 
4281  if (imgExt=="svg") // vector graphics
4282  {
4283  //writeSVGFigureLink(t,relPath,inFile,inFile+".svg");
4284  //DotFilePatcher patcher(inFile+".svg");
4285  QCString svgName=outDir+"/"+baseName+".svg";
4286  writeSVGFigureLink(t,relPath,baseName,svgName);
4287  DotFilePatcher patcher(svgName);
4288  patcher.addSVGConversion(relPath,TRUE,context,TRUE,graphId);
4289  patcher.run();
4290  }
4291  else // bitmap graphics
4292  {
4293  t << "<img src=\"" << relPath << imgName << "\" alt=\""
4294  << imgName << "\" border=\"0\" usemap=\"#" << mapName << "\"/>" << endl
4295  << "<map name=\"" << mapName << "\" id=\"" << mapName << "\">";
4296 
4297  convertMapFile(t, absOutFile, relPath ,TRUE, context);
4298 
4299  t << "</map>" << endl;
4300  }
4301  d.remove(absOutFile);
4302 }
4303 
4304 //-------------------------------------------------------------
4305 
4307 {
4308  m_curNodeId = 0;
4309  QCString tmp_url = gd->getReference()+"$"+gd->getOutputFileBase();
4310  m_usedNodes = new QDict<DotNode>(1009);
4311  m_rootNode = new DotNode(m_curNodeId++, gd->groupTitle(), "", tmp_url, TRUE );
4312  m_rootNode->markAsVisible();
4313  m_usedNodes->insert(gd->name(), m_rootNode );
4314  m_edges.setAutoDelete(TRUE);
4315 
4316  m_diskName = gd->getOutputFileBase();
4317 
4318  buildGraph( gd );
4319 }
4320 
4322 {
4323  delete m_usedNodes;
4324 }
4325 
4327 {
4328  QCString tmp_url;
4329  //===========================
4330  // hierarchy.
4331 
4332  // Write parents
4333  GroupList *groups = gd->partOfGroups();
4334  if ( groups )
4335  {
4336  GroupListIterator gli(*groups);
4337  GroupDef *d;
4338  for (gli.toFirst();(d=gli.current());++gli)
4339  {
4340  DotNode* nnode = m_usedNodes->find(d->name());
4341  if ( !nnode )
4342  { // add node
4343  tmp_url = d->getReference()+"$"+d->getOutputFileBase();
4344  QCString tooltip = d->briefDescriptionAsTooltip();
4345  nnode = new DotNode(m_curNodeId++, d->groupTitle(), tooltip, tmp_url );
4346  nnode->markAsVisible();
4347  m_usedNodes->insert(d->name(), nnode );
4348  }
4349  tmp_url = "";
4350  addEdge( nnode, m_rootNode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
4351  }
4352  }
4353 
4354  // Add subgroups
4355  if ( gd->getSubGroups() && gd->getSubGroups()->count() )
4356  {
4357  QListIterator<GroupDef> defli(*gd->getSubGroups());
4358  GroupDef *def;
4359  for (;(def=defli.current());++defli)
4360  {
4361  DotNode* nnode = m_usedNodes->find(def->name());
4362  if ( !nnode )
4363  { // add node
4364  tmp_url = def->getReference()+"$"+def->getOutputFileBase();
4365  QCString tooltip = def->briefDescriptionAsTooltip();
4366  nnode = new DotNode(m_curNodeId++, def->groupTitle(), tooltip, tmp_url );
4367  nnode->markAsVisible();
4368  m_usedNodes->insert(def->name(), nnode );
4369  }
4370  tmp_url = "";
4371  addEdge( m_rootNode, nnode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
4372  }
4373  }
4374 
4375  //=======================
4376  // Write collaboration
4377 
4378  // Add members
4379  addMemberList( gd->getMemberList(MemberListType_allMembersList) );
4380 
4381  // Add classes
4382  if ( gd->getClasses() && gd->getClasses()->count() )
4383  {
4384  ClassSDict::Iterator defli(*gd->getClasses());
4385  ClassDef *def;
4386  for (;(def=defli.current());++defli)
4387  {
4388  tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
4389  if (!def->anchor().isEmpty())
4390  {
4391  tmp_url+="#"+def->anchor();
4392  }
4393  addCollaborationMember( def, tmp_url, DotGroupCollaboration::tclass );
4394  }
4395  }
4396 
4397  // Add namespaces
4398  if ( gd->getNamespaces() && gd->getNamespaces()->count() )
4399  {
4401  NamespaceDef *def;
4402  for (;(def=defli.current());++defli)
4403  {
4404  tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
4405  addCollaborationMember( def, tmp_url, DotGroupCollaboration::tnamespace );
4406  }
4407  }
4408 
4409  // Add files
4410  if ( gd->getFiles() && gd->getFiles()->count() )
4411  {
4412  QListIterator<FileDef> defli(*gd->getFiles());
4413  FileDef *def;
4414  for (;(def=defli.current());++defli)
4415  {
4416  tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
4417  addCollaborationMember( def, tmp_url, DotGroupCollaboration::tfile );
4418  }
4419  }
4420 
4421  // Add pages
4422  if ( gd->getPages() && gd->getPages()->count() )
4423  {
4424  PageSDict::Iterator defli(*gd->getPages());
4425  PageDef *def;
4426  for (;(def=defli.current());++defli)
4427  {
4428  tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
4429  addCollaborationMember( def, tmp_url, DotGroupCollaboration::tpages );
4430  }
4431  }
4432 
4433  // Add directories
4434  if ( gd->getDirs() && gd->getDirs()->count() )
4435  {
4436  QListIterator<DirDef> defli(*gd->getDirs());
4437  DirDef *def;
4438  for (;(def=defli.current());++defli)
4439  {
4440  tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
4441  addCollaborationMember( def, tmp_url, DotGroupCollaboration::tdir );
4442  }
4443  }
4444 }
4445 
4447 {
4448  if ( !( ml && ml->count()) ) return;
4449  MemberListIterator defli(*ml);
4450  MemberDef *def;
4451  for (;(def=defli.current());++defli)
4452  {
4454  +"#"+def->anchor();
4455  addCollaborationMember( def, tmp_url, DotGroupCollaboration::tmember );
4456  }
4457 }
4458 
4460  DotNode* _pNStart, DotNode* _pNEnd, EdgeType _eType,
4461  const QCString& _label, const QCString& _url )
4462 {
4463  // search a existing link.
4464  QListIterator<Edge> lli(m_edges);
4465  Edge* newEdge = 0;
4466  for ( lli.toFirst(); (newEdge=lli.current()); ++lli)
4467  {
4468  if ( newEdge->pNStart==_pNStart &&
4469  newEdge->pNEnd==_pNEnd &&
4470  newEdge->eType==_eType
4471  )
4472  { // edge already found
4473  break;
4474  }
4475  }
4476  if ( newEdge==0 ) // new link
4477  {
4478  newEdge = new Edge(_pNStart,_pNEnd,_eType);
4479  m_edges.append( newEdge );
4480  }
4481 
4482  if (!_label.isEmpty())
4483  {
4484  newEdge->links.append(new Link(_label,_url));
4485  }
4486 
4487  return newEdge;
4488 }
4489 
4491  Definition* def, QCString& url, EdgeType eType )
4492 {
4493  // Create group nodes
4494  if ( !def->partOfGroups() )
4495  return;
4496  GroupListIterator gli(*def->partOfGroups());
4497  GroupDef *d;
4498  QCString tmp_str;
4499  for (;(d=gli.current());++gli)
4500  {
4501  DotNode* nnode = m_usedNodes->find(d->name());
4502  if ( nnode != m_rootNode )
4503  {
4504  if ( nnode==0 )
4505  { // add node
4506  tmp_str = d->getReference()+"$"+d->getOutputFileBase();
4507  QCString tooltip = d->briefDescriptionAsTooltip();
4508  nnode = new DotNode(m_curNodeId++, d->groupTitle(), tooltip, tmp_str );
4509  nnode->markAsVisible();
4510  m_usedNodes->insert(d->name(), nnode );
4511  }
4512  tmp_str = def->qualifiedName();
4513  addEdge( m_rootNode, nnode, eType, tmp_str, url );
4514  }
4515  }
4516 }
4517 
4518 
4520  GraphOutputFormat graphFormat, EmbeddedOutputFormat textFormat,
4521  const char *path, const char *fileName, const char *relPath,
4522  bool writeImageMap,int graphId) const
4523 {
4524  QDir d(path);
4525  // store the original directory
4526  if (!d.exists())
4527  {
4528  err("Output dir %s does not exist!\n",path); exit(1);
4529  }
4530  static bool usePDFLatex = Config_getBool("USE_PDFLATEX");
4531 
4532  QGString theGraph;
4533  FTextStream md5stream(&theGraph);
4534  writeGraphHeader(md5stream,m_rootNode->label());
4535 
4536  // clean write flags
4537  QDictIterator<DotNode> dni(*m_usedNodes);
4538  DotNode *pn;
4539  for (dni.toFirst();(pn=dni.current());++dni)
4540  {
4541  pn->clearWriteFlag();
4542  }
4543 
4544  // write other nodes.
4545  for (dni.toFirst();(pn=dni.current());++dni)
4546  {
4547  pn->write(md5stream,DotNode::Inheritance,graphFormat,TRUE,FALSE,FALSE,FALSE);
4548  }
4549 
4550  // write edges
4551  QListIterator<Edge> eli(m_edges);
4552  Edge* edge;
4553  for (eli.toFirst();(edge=eli.current());++eli)
4554  {
4555  edge->write( md5stream );
4556  }
4557 
4558  writeGraphFooter(md5stream);
4559  resetReNumbering();
4560  uchar md5_sig[16];
4561  QCString sigStr(33);
4562  MD5Buffer((const unsigned char *)theGraph.data(),theGraph.length(),md5_sig);
4563  MD5SigToString(md5_sig,sigStr.rawData(),33);
4564  QCString imgExt = getDotImageExtension();
4565  QCString imgFmt = Config_getEnum("DOT_IMAGE_FORMAT");
4566  QCString baseName = m_diskName;
4567  QCString imgName = baseName+"."+imgExt;
4568  QCString absPath = d.absPath().data();
4569  QCString absBaseName = absPath+"/"+baseName;
4570  QCString absDotName = absBaseName+".dot";
4571  QCString absImgName = absBaseName+"."+imgExt;
4572  QCString absMapName = absBaseName+".map";
4573  QCString absPdfName = absBaseName+".pdf";
4574  QCString absEpsName = absBaseName+".eps";
4575  bool regenerate=FALSE;
4576  if (checkAndUpdateMd5Signature(absBaseName,sigStr) ||
4577  !checkDeliverables(graphFormat==GOF_BITMAP ? absImgName :
4578  usePDFLatex ? absPdfName : absEpsName,
4579  graphFormat==GOF_BITMAP /*&& generateImageMap*/ ? absMapName : QCString())
4580  )
4581  {
4582  regenerate=TRUE;
4583 
4584  QFile dotfile(absDotName);
4585  if (dotfile.open(IO_WriteOnly))
4586  {
4587  FTextStream tdot(&dotfile);
4588  tdot << theGraph;
4589  dotfile.close();
4590  }
4591 
4592  if (graphFormat==GOF_BITMAP) // run dot to create a bitmap image
4593  {
4594  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
4595  dotRun->addJob(imgFmt,absImgName);
4596  if (writeImageMap) dotRun->addJob(MAP_CMD,absMapName);
4597  DotManager::instance()->addRun(dotRun);
4598 
4599  }
4600  else if (graphFormat==GOF_EPS)
4601  {
4602  DotRunner *dotRun = new DotRunner(absDotName,d.absPath().data(),FALSE);
4603  if (usePDFLatex)
4604  {
4605  dotRun->addJob("pdf",absPdfName);
4606  }
4607  else
4608  {
4609  dotRun->addJob("ps",absEpsName);
4610  }
4611  DotManager::instance()->addRun(dotRun);
4612  }
4613 
4614  }
4615  if (graphFormat==GOF_BITMAP && textFormat==EOF_DocBook)
4616  {
4617  t << "<para>" << endl;
4618  t << " <figure>" << endl;
4619  t << " <title>Group Collaboration diagram";
4620  t << "</title>" << endl;
4621  t << " <mediaobject>" << endl;
4622  t << " <imageobject>" << endl;
4623  t << " <imagedata";
4624  t << " width=\"50%\" align=\"center\" valign=\"middle\" scalefit=\"1\" fileref=\"" << relPath << baseName << "." << imgExt << "\">";
4625  t << "</imagedata>" << endl;
4626  t << " </imageobject>" << endl;
4627  t << " </mediaobject>" << endl;
4628  t << " </figure>" << endl;
4629  t << "</para>" << endl;
4630  }
4631  else if (graphFormat==GOF_BITMAP && writeImageMap)
4632  {
4633  QCString mapLabel = escapeCharsInString(baseName,FALSE);
4634  t << "<center><table><tr><td>";
4635 
4636  if (imgExt=="svg")
4637  {
4638  t << "<div class=\"center\">";
4639  if (regenerate || !writeSVGFigureLink(t,relPath,baseName,absImgName)) // need to patch the links in the generated SVG file
4640  {
4641  if (regenerate)
4642  {
4643  DotManager::instance()->addSVGConversion(absImgName,relPath,FALSE,QCString(),TRUE,graphId);
4644  }
4645  int mapId = DotManager::instance()->addSVGObject(fileName,baseName,absImgName,relPath);
4646  t << "<!-- SVG " << mapId << " -->" << endl;
4647  }
4648  t << "</div>" << endl;
4649  }
4650  else
4651  {
4652  t << "<img src=\"" << relPath << imgName
4653  << "\" border=\"0\" alt=\"\" usemap=\"#"
4654  << mapLabel << "\"/>" << endl;
4655  if (regenerate || !insertMapFile(t,absMapName,relPath,mapLabel))
4656  {
4657  int mapId = DotManager::instance()->addMap(fileName,absMapName,relPath,
4658  FALSE,QCString(),mapLabel);
4659  t << "<!-- MAP " << mapId << " -->" << endl;
4660  }
4661  }
4662  t << "</td></tr></table></center>" << endl;
4663  }
4664  else if (graphFormat==GOF_EPS)
4665  {
4666  if (regenerate || !writeVecGfxFigure(t,baseName,absBaseName))
4667  {
4668  int figId = DotManager::instance()->addFigure(fileName,baseName,absBaseName,FALSE);
4669  t << endl << "% FIG " << figId << endl;
4670  }
4671  }
4672  if (!regenerate) removeDotGraph(absDotName);
4673 
4674  return baseName;
4675 }
4676 
4678 {
4679  const char* linkTypeColor[] = {
4680  "darkorchid3"
4681  ,"orange"
4682  ,"blueviolet"
4683  ,"darkgreen"
4684  ,"firebrick4"
4685  ,"grey75"
4686  ,"midnightblue"
4687  };
4688  QCString arrowStyle = "dir=\"none\", style=\"dashed\"";
4689  t << " Node" << pNStart->number();
4690  t << "->";
4691  t << "Node" << pNEnd->number();
4692 
4693  t << " [shape=plaintext";
4694  if (links.count()>0) // there are links
4695  {
4696  t << ", ";
4697  // HTML-like edge labels crash on my Mac with Graphviz 2.0! and
4698  // are not supported by older version of dot.
4699  //
4700  //t << label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">";
4701  //QListIterator<Link> lli(links);
4702  //Link *link;
4703  //for( lli.toFirst(); (link=lli.current()); ++lli)
4704  //{
4705  // t << "<TR><TD";
4706  // if ( !link->url.isEmpty() )
4707  // t << " HREF=\"" << link->url << "\"";
4708  // t << ">" << link->label << "</TD></TR>";
4709  //}
4710  //t << "</TABLE>>";
4711 
4712  t << "label=\"";
4713  QListIterator<Link> lli(links);
4714  Link *link;
4715  bool first=TRUE;
4716  int count=0;
4717  const int maxLabels = 10;
4718  for( lli.toFirst(); (link=lli.current()) && count<maxLabels; ++lli,++count)
4719  {
4720  if (first) first=FALSE; else t << "\\n";
4721  t << convertLabel(link->label);
4722  }
4723  if (count==maxLabels) t << "\\n...";
4724  t << "\"";
4725 
4726  }
4727  switch( eType )
4728  {
4729  case thierarchy:
4730  arrowStyle = "dir=\"back\", style=\"solid\"";
4731  break;
4732  default:
4733  t << ", color=\"" << linkTypeColor[(int)eType] << "\"";
4734  break;
4735  }
4736  t << ", " << arrowStyle;
4737  t << "];" << endl;
4738 }
4739 
4741 {
4742  return m_usedNodes->count() <= 1;
4743 }
4744 
4746  const QCString &title) const
4747 {
4748  t << "digraph ";
4749  if (title.isEmpty())
4750  {
4751  t << "\"Dot Graph\"";
4752  }
4753  else
4754  {
4755  t << "\"" << convertToXML(title) << "\"";
4756  }
4757  t << endl;
4758  t << "{" << endl;
4759  if (Config_getBool("DOT_TRANSPARENT"))
4760  {
4761  t << " bgcolor=\"transparent\";" << endl;
4762  }
4763  t << " edge [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\","
4764  "labelfontname=\"" << FONTNAME << "\",labelfontsize=\"" << FONTSIZE << "\"];\n";
4765  t << " node [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\",shape=record];\n";
4766  t << " rankdir=LR;\n";
4767 }
4768 
4769 void writeDotDirDepGraph(FTextStream &t,DirDef *dd,bool linkRelations)
4770 {
4771  t << "digraph \"" << dd->displayName() << "\" {\n";
4772  if (Config_getBool("DOT_TRANSPARENT"))
4773  {
4774  t << " bgcolor=transparent;\n";
4775  }
4776  t << " compound=true\n";
4777  t << " node [ fontsize=\"" << FONTSIZE << "\", fontname=\"" << FONTNAME << "\"];\n";
4778  t << " edge [ labelfontsize=\"" << FONTSIZE << "\", labelfontname=\"" << FONTNAME << "\"];\n";
4779 
4780  QDict<DirDef> dirsInGraph(257);
4781 
4782  dirsInGraph.insert(dd->getOutputFileBase(),dd);
4783  if (dd->parent())
4784  {
4785  t << " subgraph cluster" << dd->parent()->getOutputFileBase() << " {\n";
4786  t << " graph [ bgcolor=\"#ddddee\", pencolor=\"black\", label=\""
4787  << dd->parent()->shortName()
4788  << "\" fontname=\"" << FONTNAME << "\", fontsize=\"" << FONTSIZE << "\", URL=\"";
4790  t << "\"]\n";
4791  }
4792  if (dd->isCluster())
4793  {
4794  t << " subgraph cluster" << dd->getOutputFileBase() << " {\n";
4795  t << " graph [ bgcolor=\"#eeeeff\", pencolor=\"black\", label=\"\""
4796  << " URL=\"" << dd->getOutputFileBase() << Doxygen::htmlFileExtension
4797  << "\"];\n";
4798  t << " " << dd->getOutputFileBase() << " [shape=plaintext label=\""
4799  << dd->shortName() << "\"];\n";
4800 
4801  // add nodes for sub directories
4802  QListIterator<DirDef> sdi(dd->subDirs());
4803  DirDef *sdir;
4804  for (sdi.toFirst();(sdir=sdi.current());++sdi)
4805  {
4806  t << " " << sdir->getOutputFileBase() << " [shape=box label=\""
4807  << sdir->shortName() << "\"";
4808  if (sdir->isCluster())
4809  {
4810  t << " color=\"red\"";
4811  }
4812  else
4813  {
4814  t << " color=\"black\"";
4815  }
4816  t << " fillcolor=\"white\" style=\"filled\"";
4817  t << " URL=\"" << sdir->getOutputFileBase()
4818  << Doxygen::htmlFileExtension << "\"";
4819  t << "];\n";
4820  dirsInGraph.insert(sdir->getOutputFileBase(),sdir);
4821  }
4822  t << " }\n";
4823  }
4824  else
4825  {
4826  t << " " << dd->getOutputFileBase() << " [shape=box, label=\""
4827  << dd->shortName() << "\", style=\"filled\", fillcolor=\"#eeeeff\","
4828  << " pencolor=\"black\", URL=\"" << dd->getOutputFileBase()
4829  << Doxygen::htmlFileExtension << "\"];\n";
4830  }
4831  if (dd->parent())
4832  {
4833  t << " }\n";
4834  }
4835 
4836  // add nodes for other used directories
4837  QDictIterator<UsedDir> udi(*dd->usedDirs());
4838  UsedDir *udir;
4839  //printf("*** For dir %s\n",shortName().data());
4840  for (udi.toFirst();(udir=udi.current());++udi)
4841  // for each used dir (=directly used or a parent of a directly used dir)
4842  {
4843  const DirDef *usedDir=udir->dir();
4844  DirDef *dir=dd;
4845  while (dir)
4846  {
4847  //printf("*** check relation %s->%s same_parent=%d !%s->isParentOf(%s)=%d\n",
4848  // dir->shortName().data(),usedDir->shortName().data(),
4849  // dir->parent()==usedDir->parent(),
4850  // usedDir->shortName().data(),
4851  // shortName().data(),
4852  // !usedDir->isParentOf(this)
4853  // );
4854  if (dir!=usedDir && dir->parent()==usedDir->parent() &&
4855  !usedDir->isParentOf(dd))
4856  // include if both have the same parent (or no parent)
4857  {
4858  t << " " << usedDir->getOutputFileBase() << " [shape=box label=\""
4859  << usedDir->shortName() << "\"";
4860  if (usedDir->isCluster())
4861  {
4862  if (!Config_getBool("DOT_TRANSPARENT"))
4863  {
4864  t << " fillcolor=\"white\" style=\"filled\"";
4865  }
4866  t << " color=\"red\"";
4867  }
4868  t << " URL=\"" << usedDir->getOutputFileBase()
4869  << Doxygen::htmlFileExtension << "\"];\n";
4870  dirsInGraph.insert(usedDir->getOutputFileBase(),usedDir);
4871  break;
4872  }
4873  dir=dir->parent();
4874  }
4875  }
4876 
4877  // add relations between all selected directories
4878  DirDef *dir;
4879  QDictIterator<DirDef> di(dirsInGraph);
4880  for (di.toFirst();(dir=di.current());++di) // foreach dir in the graph
4881  {
4882  QDictIterator<UsedDir> udi(*dir->usedDirs());
4883  UsedDir *udir;
4884  for (udi.toFirst();(udir=udi.current());++udi) // foreach used dir
4885  {
4886  const DirDef *usedDir=udir->dir();
4887  if ((dir!=dd || !udir->inherited()) && // only show direct dependendies for this dir
4888  (usedDir!=dd || !udir->inherited()) && // only show direct dependendies for this dir
4889  !usedDir->isParentOf(dir) && // don't point to own parent
4890  dirsInGraph.find(usedDir->getOutputFileBase())) // only point to nodes that are in the graph
4891  {
4892  QCString relationName;
4893  relationName.sprintf("dir_%06d_%06d",dir->dirCount(),usedDir->dirCount());
4894  if (Doxygen::dirRelations.find(relationName)==0)
4895  {
4896  // new relation
4897  Doxygen::dirRelations.append(relationName,
4898  new DirRelation(relationName,dir,udir));
4899  }
4900  int nrefs = udir->filePairs().count();
4901  t << " " << dir->getOutputFileBase() << "->"
4902  << usedDir->getOutputFileBase();
4903  t << " [headlabel=\"" << nrefs << "\", labeldistance=1.5";
4904  if (linkRelations)
4905  {
4906  t << " headhref=\"" << relationName << Doxygen::htmlFileExtension << "\"";
4907  }
4908  t << "];\n";
4909  }
4910  }
4911  }
4912 
4913  t << "}\n";
4914 }
static QCString name
Definition: declinfo.cpp:673
void portable_sleep(int ms)
Definition: portable.cpp:425
TruncState m_truncated
does the node have non-visible children/parents
Definition: dot.h:116
Traverses directory structures and contents in a platform-independent way.
Definition: qdir.h:52
QCString convertToXML(const char *s)
Definition: util.cpp:5717
QCString m_patchFile
Definition: dot.h:425
bool isParentOf(DirDef *dir) const
Definition: dirdef.cpp:603
ClassDef * classDef
Definition: classdef.h:520
QList< EdgeInfo > * m_edgeInfo
edge info for each child
Definition: dot.h:109
bool resize(uint newlen)
Definition: qcstring.h:225
Q_EXPORT int qstrncmp(const char *str1, const char *str2, uint len)
Definition: qcstring.h:101
char * rawData() const
Definition: qcstring.h:216
static SDict< DirRelation > dirRelations
Definition: doxygen.h:140
void portable_sysTimerStop()
Definition: portable.cpp:415
QCString m_tooltip
node&#39;s tooltip
Definition: dot.h:105
bool isTrivial() const
Definition: dot.cpp:4120
static void resetReNumbering()
Definition: dot.cpp:436
QCString anchor() const
Definition: docparser.h:830
MemberGroupList * getMemberGroupList() const
Definition: memberlist.h:78
int addSVGConversion(const QCString &file, const QCString &relPath, bool urlOnly, const QCString &context, bool zoomable, int graphId)
Definition: dot.cpp:1292
char * data() const
Definition: qgstring.h:42
bool determineVisibleNodes(DotNode *rootNode, int maxNodes, bool includeParents)
Definition: dot.cpp:2709
static QCString scope
Definition: declinfo.cpp:668
void writeBox(FTextStream &t, GraphType gt, GraphOutputFormat f, bool hasNonReachableChildren, bool reNumber=FALSE)
Definition: dot.cpp:1722
void addParent(DotNode *n)
Definition: dot.cpp:1507
BaseClassList * subClasses() const
Definition: classdef.cpp:4404
virtual QCString getReference() const
void addClass(ClassDef *cd, DotNode *n, int prot, const char *label, const char *usedName, const char *templSpec, bool base, int distance)
Definition: dot.cpp:2590
bool isLinkable() const
Definition: filedef.h:117
EmbeddedOutputFormat
Definition: dot.h:43
void clearWriteFlag()
Definition: dot.cpp:2168
static int getDotFontSize()
Definition: dot.cpp:244
bool isTooBig() const
Definition: dot.cpp:3959
const CleanupItem & cleanup() const
Definition: dot.h:380
QCString mapFile
Definition: dot.h:403
const QCString & shortName() const
Definition: dirdef.h:57
static QCString result
bool isEmpty() const
Definition: qcstring.h:189
virtual bool remove(const QString &fileName, bool acceptAbsPath=TRUE)
Definition: qdir.cpp:915
QCString getReference() const
Definition: memberdef.cpp:1001
QCString escapeCharsInString(const char *name, bool allowDots, bool allowUnderscore)
Definition: util.cpp:5242
static void writeBoxMemberList(FTextStream &t, char prot, MemberList *ml, ClassDef *scope, bool isStatic=FALSE, const QDict< void > *skipNames=0)
Definition: dot.cpp:1651
DotClassGraph(ClassDef *cd, DotNode::GraphType t)
Definition: dot.cpp:2939
DotConstString m_postArgs
Definition: dot.h:386
static bool insertMapFile(FTextStream &out, const QCString &mapFile, const QCString &relPath, const QCString &mapLabel)
Definition: dot.cpp:694
void msg(const char *fmt,...)
Definition: message.cpp:107
static bool checkAndUpdateMd5Signature(const QCString &baseName, const QCString &md5)
Definition: dot.cpp:731
Definition: dot.h:356
QDict< void > * accessors
Definition: classdef.h:597
bool isEmpty() const
Definition: dot.h:338
uint length() const
Definition: qgstring.h:40
void write(FTextStream &t, GraphType gt, GraphOutputFormat f, bool topDown, bool toChildren, bool backArrows, bool reNumber)
Definition: dot.cpp:1906
QString name() const
Definition: qfile.h:121
uint length() const
Definition: qcstring.h:195
#define IO_Raw
Definition: qiodevice.h:56
static bool checkDeliverables(const QCString &file1, const QCString &file2=QCString())
Definition: dot.cpp:758
static ClassSDict * hiddenClasses
Definition: doxygen.h:100
int contains(char c, bool cs=TRUE) const
Definition: qcstring.cpp:153
bool isTrivial() const
Definition: dot.cpp:3636
void append(const type *d)
Definition: qlist.h:73
void determineVisibleNodes(QList< DotNode > &queue, int &maxNodes)
Definition: dot.cpp:3725
const char * portable_getenv(const char *variable)
Definition: portable.cpp:317
#define IO_WriteOnly
Definition: qiodevice.h:62
bool generateSourceFile() const
Definition: filedef.cpp:1396
void determineTruncatedNodes(QList< DotNode > &queue)
Definition: dot.cpp:3432
const char *const * arrowStyleMap
Definition: dot.cpp:218
static QCString htmlFileExtension
Definition: doxygen.h:130
MemberList * members() const
Definition: membergroup.h:88
void writeDotImageMapFromFile(FTextStream &t, const QCString &inFile, const QCString &outDir, const QCString &relPath, const QCString &baseName, const QCString &context, int graphId)
Definition: dot.cpp:4255
int findRef(const type *d) const
Definition: qlist.h:89
bool resize(uint size)
Definition: qarray.h:69
~DotNode()
Definition: dot.cpp:1473
static QCString convertLabel(const QCString &l)
Definition: dot.cpp:1564
void addCollaborationMember(Definition *def, QCString &url, EdgeType eType)
Definition: dot.cpp:4490
bool m_checkResult
Definition: dot.h:390
bool m_isRoot
indicates if this is a root node
Definition: dot.h:113
DotConstString file
Definition: dot.h:362
virtual QCString trLegendTitle()=0
bool isCluster() const
Definition: dirdef.h:62
void enqueue(DotRunner *runner)
Definition: dot.cpp:1160
Definition: dot.h:60
DirDef * m_dir
Definition: dot.h:268
TruncState isTruncated() const
Definition: dot.h:90
static bool format(QChar::Decomposition tag, QString &str, int index, int len)
Definition: qstring.cpp:11496
static void writeSVGNotSupported(FTextStream &out)
Definition: dot.cpp:611
static DotManager * instance()
Definition: dot.cpp:1221
const DotNode * findDocNode() const
Definition: dot.cpp:2232
const bool FALSE
Definition: qglobal.h:370
friend class Iterator
Definition: sortdict.h:598
void start()
void buildGraph(DotNode *n, FileDef *fd, int distance)
Definition: dot.cpp:3344
QCString writeGraph(FTextStream &t, GraphOutputFormat gf, EmbeddedOutputFormat ef, const char *path, const char *fileName, const char *relPath, bool writeImageMap=TRUE, int graphId=-1) const
Definition: dot.cpp:4519
static QCString replaceRef(const QCString &buf, const QCString relPath, bool urlOnly, const QCString &context, const QCString &target=QCString())
Definition: dot.cpp:286
QCString m_label
Definition: dot.h:54
DotConstString m_postCmd
Definition: dot.h:387
void writeDEF(FTextStream &t)
Definition: dot.cpp:2103
error
Definition: include.cc:26
static QCString stripPath(const QCString &s)
Definition: tagreader.cpp:1287
type * at(uint i) const
Definition: qlist.h:94
int readLine(char *data, uint maxlen)
Definition: qfile.cpp:268
Protection prot
Definition: classdef.h:530
~DotCallGraph()
Definition: dot.cpp:3818
static QCString className
Definition: declinfo.cpp:669
int readBlock(char *data, uint len)
Definition: qfile_unix.cpp:473
Definition: qarray.h:46
QCString left(uint len) const
Definition: qcstring.cpp:213
void portable_unsetenv(const char *variable)
Definition: portable.cpp:283
The QString class provides an abstraction of Unicode text and the classic C null-terminated char arra...
Definition: qstring.h:350
boost::graph_traits< ModuleGraph >::edge_descriptor Edge
Definition: ModuleGraph.h:24
static const char * normalEdgeStyleMap[]
Definition: dot.cpp:181
bool isSignal() const
Definition: memberdef.cpp:4130
QList< Map > m_maps
Definition: dot.h:424
void write(FTextStream &t) const
Definition: dot.cpp:4677
TFile * outFile
Definition: makeDST.cxx:36
QCString label() const
Definition: dot.h:87
Simplified and optimized version of QTextStream.
Definition: ftextstream.h:11
bool isSlot() const
Definition: memberdef.cpp:4135
string dir
void writeDocbook(FTextStream &t, bool isClassGraph)
Definition: dot.cpp:2033
QCString file() const
Definition: docparser.h:827
DotConstString m_file
Definition: dot.h:388
QCString displayName(bool includeScope=TRUE) const
Definition: classdef.cpp:312
int find(char c, int index=0, bool cs=TRUE) const
Definition: qcstring.cpp:41
int count() const
Definition: sortdict.h:284
void addChild(DotNode *n, int edgeColor=EdgeInfo::Purple, int edgeStyle=EdgeInfo::Solid, const char *edgeLab=0, const char *edgeURL=0, int edgeLabCol=-1)
Definition: dot.cpp:1480
GraphType
Definition: dot.h:63
QCString diskName() const
Definition: dot.cpp:3495
DotInclDepGraph(FileDef *fd, bool inverse)
Definition: dot.cpp:3458
QCString getFileBase() const
Definition: filedef.h:88
ClassDef * classDef
Definition: classdef.h:477
const char * groupTitle() const
Definition: groupdef.h:54
GroupList * partOfGroups() const
void append(const char *key, const T *d)
Definition: sortdict.h:135
static constexpr double mg
Definition: Units.h:145
static QCString args
Definition: declinfo.cpp:674
QCString templSpecifiers
Definition: classdef.h:485
static QStrList * l
Definition: config.cpp:1044
virtual QString absPath() const
Definition: qdir.cpp:276
QCString getReference() const
Definition: classdef.cpp:3814
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition: qcstring.cpp:95
QCString file() const
Definition: dot.cpp:881
ClassSDict * getClasses() const
Definition: groupdef.h:101
QAsciiDict< Entry > cl
static int m_curNodeNumber
Definition: dot.h:163
QList< Link > links
Definition: dot.h:304
int addMap(const QCString &file, const QCString &mapFile, const QCString &relPath, bool urlOnly, const QCString &context, const QCString &label)
Definition: dot.cpp:1267
unsigned char uchar
Definition: nybbler.cc:11
int distance() const
Definition: dot.h:91
bool run()
Definition: dot.cpp:946
GroupList * getSubGroups() const
Definition: groupdef.h:103
Protection protection() const
Definition: classdef.cpp:4414
MemberSDict * getReferencesMembers() const
static const char svgZoomHeader[]
Definition: dot.cpp:59
int m_style
Definition: dot.h:53
QCString copy() const
Definition: qcstring.h:250
DotConstString m_path
Definition: dot.h:389
void portable_sysTimerStart()
Definition: portable.cpp:410
virtual QCString trAndMore(const QCString &number)=0
QCString displayName(bool=TRUE) const
Definition: dirdef.h:56
static QCString escapeTooltip(const QCString &tooltip)
Definition: dot.cpp:1634
int addFigure(const QCString &baseName, const QCString &figureName, bool heightCheck)
Definition: dot.cpp:902
void removeParent(DotNode *n)
Definition: dot.cpp:1521
#define IO_ReadOnly
Definition: qiodevice.h:61
static void writeGraphHeader(FTextStream &t, const QCString &title=QCString())
Definition: dot.cpp:251
void setAutoDelete(bool val)
Definition: sortdict.h:222
static EdgeProperties normalEdgeProps
Definition: dot.cpp:222
#define Config_getEnum(val)
Definition: config.cpp:663
#define QMIN(a, b)
Definition: qglobal.h:391
QCString m_url
Definition: dot.h:55
static void removeDotGraph(const QCString &dotName)
Definition: dot.cpp:714
static void deleteNodes(DotNode *node, SDict< DotNode > *skipNodes=0)
Definition: dot.cpp:1443
virtual QCString trGraphicalHierarchy()=0
#define Config_getInt(val)
Definition: config.cpp:661
void buildGraph(ClassDef *cd, DotNode *n, bool base, int distance)
Definition: dot.cpp:2804
void writeDEF(FTextStream &t)
Definition: dot.cpp:3330
int reNumberNode(int number, bool doReNumbering)
Definition: dot.cpp:403
QCString writeGraph(FTextStream &t, GraphOutputFormat gf, EmbeddedOutputFormat ef, const char *path, const char *fileName, const char *relPath, bool TBRank=TRUE, bool imageMap=TRUE, int graphId=-1) const
Definition: dot.cpp:3133
bool isTrivial() const
Definition: dot.cpp:4740
ClassDef * templateMaster() const
Definition: classdef.cpp:4439
static const char * umlArrowStyleMap[]
Definition: dot.cpp:198
def cli(ctx)
Definition: main.py:7
Definition: sortdict.h:73
int dirCount() const
Definition: dirdef.h:65
static EdgeProperties umlEdgeProps
Definition: dot.cpp:227
uint count() const
Definition: qlist.h:66
QCString m_url
url of the node (format: remote$local)
Definition: dot.h:106
DotConstString path
Definition: dot.h:361
int writeBlock(const char *data, uint len)
Definition: qfile_unix.cpp:537
MemberSDict * getReferencedByMembers() const
static QArray< int > s_newNumber
Definition: dot.cpp:400
const char * data() const
Definition: qstring.h:542
QList< DotRunner > m_dotRuns
Definition: dot.h:473
friend QCString computeMd5Signature(DotNode *root, GraphType gt, GraphOutputFormat f, bool lrRank, bool renderParents, bool backArrows, const QCString &title, QCString &graphStr)
Definition: dot.cpp:3018
#define QMAX(a, b)
Definition: qglobal.h:390
QCString ref() const
Definition: docparser.h:829
const QCString & name() const
Definition: definition.h:114
static void writeDotDirDepGraph(FTextStream &t, DirDef *dd, bool linkRelations)
Definition: dot.cpp:4769
const double e
QCString getFileBase() const
Definition: classdef.cpp:3591
void determineTruncatedNodes(QList< DotNode > &queue)
Definition: dot.cpp:3749
PageSDict * getPages() const
Definition: groupdef.h:104
int qstricmp(const char *str1, const char *str2)
Definition: qcstring.cpp:567
bool run()
Definition: dot.cpp:819
static QCString stripProtectionPrefix(const QCString &s)
Definition: dot.cpp:1710
fileName
Definition: dumpTree.py:9
#define nodes
int m_labColor
Definition: dot.h:56
bool m_cleanUp
Definition: dot.h:393
static bool updateDotGraph(DotNode *root, DotNode::GraphType gt, const QCString &baseName, GraphOutputFormat format, bool lrRank, bool renderParents, bool backArrows, const QCString &title=QCString())
Definition: dot.cpp:3088
DotConstString m_imageName
Definition: dot.h:391
static QDir current()
Definition: qdir.cpp:978
QCString linkToText(SrcLangExt lang, const char *link, bool isFileName)
Definition: util.cpp:4659
bool isTrivial() const
Definition: dot.cpp:3954
static void unsetDotFontPath()
Definition: dot.cpp:466
void MD5Buffer(const unsigned char *buf, unsigned int len, unsigned char sig[16])
Definition: md5.c:275
virtual bool rename(const QString &name, const QString &newName, bool acceptAbsPaths=TRUE)
Definition: qdir_unix.cpp:119
static const char * normalEdgeColorMap[]
Definition: dot.cpp:160
bool isRunning() const
Definition: qthread.cpp:64
QCString getSourceFileBase() const
Definition: filedef.cpp:1771
QCString right(uint len) const
Definition: qcstring.cpp:231
QList< IncludeInfo > * includeFileList() const
Definition: filedef.h:124
void setDistance(int distance)
Definition: dot.cpp:1559
SrcLangExt getLanguage() const
std::void_t< T > n
void set(const QCString &s)
Definition: dot.h:339
void addHierarchy(DotNode *n, ClassDef *cd, bool hide)
Definition: dot.cpp:2396
QList< DotNode > * m_children
list of child nodes (outgoing arrows)
Definition: dot.h:108
bool open(int)
Definition: qfile_unix.cpp:134
int addSVGObject(const QCString &baseName, const QCString &figureName, const QCString &relPath)
Definition: dot.cpp:931
void markAsTruncated(bool b=TRUE)
Definition: dot.h:102
static bool readSVGSize(const QCString &fileName, int *width, int *height)
Definition: dot.cpp:572
bool isAnonymous() const
Definition: classdef.cpp:4749
bool m_written
used to mark a node as written
Definition: dot.h:111
QCString context
Definition: dot.h:406
~DotInclDepGraph()
Definition: dot.cpp:3489
QCString anchor() const
Definition: classdef.cpp:4606
static bool writeVecGfxFigure(FTextStream &out, const QCString &baseName, const QCString &figureName)
Definition: dot.cpp:518
void writeXML(FTextStream &t, bool isClassGraph)
Definition: dot.cpp:1964
const DirList & subDirs() const
Definition: dirdef.h:61
NamespaceSDict * getNamespaces() const
Definition: groupdef.h:102
int addSVGConversion(const QCString &relPath, bool urlOnly, const QCString &context, bool zoomable, int graphId)
Definition: dot.cpp:916
const QDict< UsedDir > * usedDirs() const
Definition: dirdef.h:66
void determineTruncatedNodes(QList< DotNode > &queue, bool includeParents)
Definition: dot.cpp:2672
SDict< DotFilePatcher > m_dotMaps
Definition: dot.h:474
QCString & prepend(const char *s)
Definition: qcstring.cpp:387
void writeDotGraphFromFile(const char *inFile, const char *outDir, const char *outFile, GraphOutputFormat format)
Definition: dot.cpp:4203
int m_color
Definition: dot.h:52
p
Definition: test.py:223
#define FONTNAME
Definition: dot.cpp:54
A bunch of utility functions.
int m_number
Definition: dot.h:103
void clear()
Definition: qlist.h:82
ConstraintClassDict * templateTypeConstraints() const
Definition: classdef.cpp:4469
int addFigure(const QCString &file, const QCString &baseName, const QCString &figureName, bool heightCheck)
Definition: dot.cpp:1280
const char * data() const
Definition: qcstring.h:207
Definition: dirdef.h:44
DotDirDeps(DirDef *dir)
Definition: dot.cpp:3969
char portable_pathListSeparator()
Definition: portable.cpp:364
bool isLinkable() const
Definition: classdef.cpp:2729
QCString anchor() const
Definition: memberdef.cpp:1031
void addImageFile(const char *name)
Definition: index.h:147
void writeArrow(FTextStream &t, GraphType gt, GraphOutputFormat f, DotNode *cn, EdgeInfo *ei, bool topDown, bool pointBack=TRUE, bool reNumber=FALSE)
Definition: dot.cpp:1854
#define Config_getString(val)
Definition: config.cpp:660
type * current() const
Definition: qlist.h:146
DotGroupCollaboration(GroupDef *gd)
Definition: dot.cpp:4306
QCString includeName
Definition: filedef.h:50
~DotDirDeps()
Definition: dot.cpp:3973
QList< DotNode > * m_parents
list of parent nodes (incoming arrows)
Definition: dot.h:107
#define Config_getBool(val)
Definition: config.cpp:664
const char *const * edgeColorMap
Definition: dot.cpp:217
ClassDef * getClassDef() const
Definition: memberdef.cpp:4070
uint count() const
Definition: dot.cpp:1179
Definition: dot.h:46
QCString getDotImageExtension(void)
Definition: util.cpp:8562
bool isTrivial() const
Definition: dot.cpp:2988
DotManager()
Definition: dot.cpp:1230
virtual ~DotManager()
Definition: dot.cpp:1257
bool isObjCMethod() const
Definition: memberdef.cpp:3956
#define MAP_CMD
Definition: dot.cpp:51
bool depGraphIsTrivial() const
Definition: dirdef.cpp:613
virtual QCString qualifiedName() const
QCString diskName() const
Definition: dot.cpp:3112
DotRunner * dequeue()
Definition: dot.cpp:1167
Definition: dot.h:42
void writeXML(FTextStream &t)
Definition: dot.cpp:3648
void addClassList(ClassSDict *cl)
Definition: dot.cpp:2470
static void checkDotResult(const char *imgExt, const char *imgName)
Definition: dot.cpp:662
QCString & insert(uint index, const char *s)
Definition: qcstring.cpp:355
static const char * umlEdgeStyleMap[]
Definition: dot.cpp:208
void initClassHierarchy(ClassSDict *cl)
Definition: util.cpp:5211
int findParent(DotNode *n)
Definition: dot.cpp:778
void err(const char *fmt,...)
Definition: message.cpp:226
bool visited
Definition: classdef.h:402
static QCString getDotFontName()
Definition: dot.cpp:233
int compareValues(const DotNode *n1, const DotNode *n2) const
Definition: dot.cpp:786
QCString absFilePath() const
Definition: filedef.h:96
bool isTooBig() const
Definition: dot.cpp:2997
void addPostProcessing(const char *cmd, const char *args)
Definition: dot.cpp:813
QCString relPath
Definition: dot.h:404
DotRunnerQueue * m_queue
Definition: dot.h:449
static DotManager * m_theInstance
Definition: dot.h:475
QCString mid(uint index, uint len=0xffffffff) const
Definition: qcstring.cpp:246
QCString getOutputFileBase() const
Definition: filedef.h:83
int number() const
Definition: dot.h:88
friend class DotNodeList
Definition: dot.h:122
The QFile class is an I/O device that operates on files.
Definition: qfile.h:50
static bool readBoundingBox(const char *fileName, int *width, int *height, bool isEps)
Definition: dot.cpp:479
int addSVGObject(const QCString &file, const QCString &baseName, const QCString &figureNAme, const QCString &relPath)
Definition: dot.cpp:1305
QCString & sprintf(const char *format,...)
Definition: qcstring.cpp:27
bool run()
Definition: dot.cpp:1317
TFile * inFile
Definition: makeDST.cxx:36
void buildGraph(GroupDef *gd)
Definition: dot.cpp:4326
#define FONTSIZE
Definition: dot.cpp:55
static void writeGraphFooter(FTextStream &t)
Definition: dot.cpp:281
DotConstString m_dotExe
Definition: dot.h:383
QCString getOutputFileBase() const
Definition: classdef.cpp:3533
MemberGroupSDict * getMemberGroupSDict() const
Definition: classdef.cpp:4524
static const char * normalArrowStyleMap[]
Definition: dot.cpp:171
uint size() const
void portable_setenv(const char *name, const char *value)
Definition: portable.cpp:202
virtual Definition * getOuterScope() const
bool isHidden() const
FileDef * fileDef
Definition: filedef.h:49
QCString getOutputFileBase() const
Definition: dirdef.cpp:113
QCString writeGraph(FTextStream &t, GraphOutputFormat gf, EmbeddedOutputFormat ef, const char *path, const char *fileName, const char *relPath, bool writeImageMap=TRUE, int graphId=-1) const
Definition: dot.cpp:3824
static QString fromUtf8(const char *, int len=-1)
Definition: qstring.cpp:14523
UsesClassDict * usedByImplementationClasses() const
Definition: classdef.cpp:4459
MemberList * getMemberList(MemberListType lt) const
Definition: groupdef.cpp:1593
void addJob(const char *format, const char *output)
Definition: dot.cpp:807
QCString templSpecifiers
Definition: classdef.h:538
void cleanup()
Definition: dot.cpp:1207
bool showInCallGraph() const
Definition: memberdef.cpp:4458
void writeGraph(FTextStream &t, const char *path, const char *fileName) const
Definition: dot.cpp:2366
QCString qualifiedName() const
Definition: memberdef.cpp:3968
bool isVisibleInHierarchy()
Definition: classdef.cpp:2743
QList< DotConstString > m_jobs
Definition: dot.h:385
~DotClassGraph()
Definition: dot.cpp:3009
void MD5SigToString(unsigned char signature[16], char *str, int len)
Definition: md5.c:285
QList< IncludeInfo > * includedByFileList() const
Definition: filedef.h:125
QDict< ClassDef > * getTemplateInstances() const
Definition: classdef.cpp:4434
static bool writeSVGFigureLink(FTextStream &out, const QCString &relPath, const QCString &baseName, const QCString &absImgName)
Definition: dot.cpp:618
DotFilePatcher(const char *patchFile)
Definition: dot.cpp:875
static const char * umlEdgeColorMap[]
Definition: dot.cpp:187
QCString stripScope(const char *name)
Definition: util.cpp:5605
void run()
Definition: dot.cpp:1193
DotNode(int n, const char *lab, const char *tip, const char *url, bool rootNode=FALSE, ClassDef *cd=0)
Definition: dot.cpp:1452
void writeGraphHeader(FTextStream &t, const QCString &title) const
Definition: dot.cpp:4745
ClassDef * m_classDef
class representing this node (can be 0)
Definition: dot.h:114
int addMap(const QCString &mapFile, const QCString &relPath, bool urlOnly, const QCString &context, const QCString &label)
Definition: dot.cpp:886
CleanupItem m_cleanupItem
Definition: dot.h:394
A model of a page symbol.
Definition: pagedef.h:29
Edge * addEdge(DotNode *_pNStart, DotNode *_pNEnd, EdgeType _eType, const QCString &_label, const QCString &_url)
Definition: dot.cpp:4459
static int m_curNodeNumber
Definition: dot.h:244
static void setDotFontPath(const char *path)
Definition: dot.cpp:444
static int m_curNodeNumber
Definition: dot.h:221
void writeDocbook(FTextStream &t)
Definition: dot.cpp:3320
void line(double t, double *p, double &x, double &y, double &z)
QCString convertNameToFile(const char *name, bool allowDots, bool allowUnderscore)
Definition: util.cpp:5354
static int m_curNodeNumber
Definition: dot.h:194
QCString usedName
Definition: classdef.h:525
int m_subgraphId
Definition: dot.h:82
void writeXML(FTextStream &t)
Definition: dot.cpp:3310
QCString doc
QCString getOutputFileBase() const
Definition: memberdef.cpp:941
QCString insertTemplateSpecifierInScope(const QCString &scope, const QCString &templ)
Definition: util.cpp:5527
FileList * getFiles() const
Definition: groupdef.h:100
bool m_hasDoc
used to mark a node as documented
Definition: dot.h:112
bool isEmpty() const
Definition: qgstring.h:39
bool hasVisibleRoot(BaseClassList *bcl)
Definition: util.cpp:5224
static QCString g_dotFontPath
Definition: dot.cpp:442
bool m_deleted
used to mark a node as deleted
Definition: dot.h:110
QCString m_label
label text
Definition: dot.h:104
Translator * theTranslator
Definition: language.cpp:157
QCString writeGraph(FTextStream &out, GraphOutputFormat gf, EmbeddedOutputFormat ef, const char *path, const char *fileName, const char *relPath, bool writeImageMap=TRUE, int graphId=-1, bool linkRelations=TRUE) const
Definition: dot.cpp:3977
bool m_multiTargets
Definition: dot.h:384
list x
Definition: train.py:276
bool m_visible
is the node visible in the output
Definition: dot.h:115
bool isFunction() const
Definition: memberdef.cpp:4160
void colorConnectedNodes(int curColor)
Definition: dot.cpp:2197
friend class Iterator
Definition: sortdict.h:289
GraphOutputFormat
Definition: dot.h:42
QCString briefDescriptionAsTooltip() const
list cmd
Definition: getreco.py:22
UsesClassDict * usedImplementationClasses() const
Definition: classdef.cpp:4454
int m_distance
shortest path to the root node
Definition: dot.h:117
static const char svgZoomFooter[]
Definition: dot.cpp:103
QCString writeGraph(FTextStream &t, GraphOutputFormat gf, EmbeddedOutputFormat ef, const char *path, const char *fileName, const char *relPath, bool writeImageMap=TRUE, int graphId=-1) const
Definition: dot.cpp:3503
static QCString baseName
Definition: scanner.cpp:10890
DirDef * parent() const
Definition: dirdef.h:64
const char * cs
void markAsVisible(bool b=TRUE)
Definition: dot.h:101
QList< DotWorkerThread > m_workers
Definition: dot.h:477
void wait()
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:51
QCString getOutputFileBase() const
Definition: groupdef.cpp:1512
DotConstString m_imgExt
Definition: dot.h:392
DotRunnerQueue * m_queue
Definition: dot.h:476
const char *const * edgeStyleMap
Definition: dot.cpp:219
type * take(uint i)
Definition: qlist.h:81
Q_EXPORT int qstrcmp(const char *str1, const char *str2)
Definition: qcstring.h:95
Definition: dot.h:42
void determineVisibleNodes(QList< DotNode > &queue, int &maxNodes)
Definition: dot.cpp:3408
T * find(const char *key)
Definition: sortdict.h:232
void close()
Definition: qfile_unix.cpp:614
QCString utf8() const
Definition: qstring.cpp:14507
bool remove(uint i)
Definition: qlist.h:76
DotWorkerThread(DotRunnerQueue *queue)
Definition: dot.cpp:1187
static bool convertMapFile(FTextStream &t, const char *mapName, const QCString relPath, bool urlOnly=FALSE, const QCString &context=QCString())
Definition: dot.cpp:370
QCString label
Definition: dot.h:407
type & at(uint i) const
Definition: qarray.h:98
uint size() const
Definition: qarray.h:65
DotRunner(const QCString &file, const QCString &fontPath, bool checkResult, const QCString &imageName=QCString())
Definition: dot.cpp:793
unsigned uint
Definition: qglobal.h:351
void deleteNode(DotNodeList &deletedList, SDict< DotNode > *skipNodes=0)
Definition: dot.cpp:1526
void setAutoDelete(bool enable)
Definition: qlist.h:99
static ClassSDict * classSDict
Definition: doxygen.h:99
#define Map
Definition: vhdlcode.cpp:19862
QCString externalLinkTarget()
Definition: util.cpp:7850
ClassDef * classDef
Definition: classdef.h:592
FILE * portable_fopen(const char *fileName, const char *mode)
Definition: portable.cpp:344
void addRun(DotRunner *run)
Definition: dot.cpp:1262
QAsciiDict< Entry > ns
std::string nl(std::size_t i=1)
const QCString & docName() const
Definition: filedef.h:99
DirList * getDirs() const
Definition: groupdef.h:105
type * toLast()
Definition: qlist.h:136
MemberList * getMemberList(MemberListType lt)
Definition: classdef.cpp:4021
static QCString * s
Definition: config.cpp:1042
virtual bool exists() const
Definition: qdir.cpp:820
bool isVisible() const
Definition: dot.h:89
const bool TRUE
Definition: qglobal.h:371
static IndexList * indexList
Definition: doxygen.h:149
void createGraph(DotNode *rootNode, FTextStream &t, const char *path, const char *fileName, int id) const
Definition: dot.cpp:2271
bool atEnd() const
Definition: qfile.cpp:239
Portable versions of functions that are platform dependent.
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition: util.cpp:5088
DotCallGraph(MemberDef *md, bool inverse)
Definition: dot.cpp:3776
bool isTooBig() const
Definition: dot.cpp:3641
QTextStream & endl(QTextStream &s)
void buildGraph(DotNode *n, MemberDef *md, int distance)
Definition: dot.cpp:3672
BaseClassList * baseClasses() const
Definition: classdef.cpp:4399
void addMemberList(class MemberList *ml)
Definition: dot.cpp:4446
const char * data() const
Definition: dot.h:337
void removeChild(DotNode *n)
Definition: dot.cpp:1516
QCString externalRef(const QCString &relPath, const QCString &ref, bool href)
Definition: util.cpp:7856
bool exists() const
Definition: qfileinfo.cpp:265
#define ASSERT(x)
Definition: qglobal.h:590
QList< DotRunner::CleanupItem > m_cleanupItems
Definition: dot.h:450
int portable_system(const char *command, const char *args, bool commandHasConsole)
Definition: portable.cpp:33
QDict< void > * accessors
Definition: classdef.h:482
type * toFirst()
Definition: qlist.h:135
void generateGraphLegend(const char *path)
Definition: dot.cpp:4127
static int s_max_newNumber
Definition: dot.cpp:401
static int idealThreadCount()
void writeDocbook(FTextStream &t)
Definition: dot.cpp:3658
def rename(src, dest)