Orb/Doxygen/src/dot.cpp
changeset 0 42188c7ea2d9
child 4 468f4c8d3d5b
equal deleted inserted replaced
-1:000000000000 0:42188c7ea2d9
       
     1 /*****************************************************************************
       
     2  *
       
     3  * 
       
     4  *
       
     5  *
       
     6  * Copyright (C) 1997-2008 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 #ifdef _WIN32
       
    20 #include <windows.h>
       
    21 #define BITMAP W_BITMAP
       
    22 #endif
       
    23 
       
    24 #include <stdlib.h>
       
    25 
       
    26 #include "dot.h"
       
    27 #include "doxygen.h"
       
    28 #include "message.h"
       
    29 #include "util.h"
       
    30 #include "config.h"
       
    31 #include "language.h"
       
    32 #include "defargs.h"
       
    33 #include "docparser.h"
       
    34 #include "debug.h"
       
    35 #include "pagedef.h"
       
    36 #include "portable.h"
       
    37 #include "dirdef.h"
       
    38 
       
    39 #include <qdir.h>
       
    40 #include <qfile.h>
       
    41 #include <qtextstream.h>
       
    42 #include <md5.h>
       
    43 
       
    44 #define MAP_CMD "cmapx"
       
    45 
       
    46 //#define FONTNAME "FreeSans"
       
    47 #define FONTNAME getDotFontName()
       
    48 #define FONTSIZE getDotFontSize()
       
    49 
       
    50 //--------------------------------------------------------------------
       
    51 
       
    52 static const int maxCmdLine = 40960;
       
    53 
       
    54 /*! mapping from protection levels to color names */
       
    55 static const char *edgeColorMap[] =
       
    56 {
       
    57   "midnightblue",  // Public
       
    58   "darkgreen",     // Protected
       
    59   "firebrick4",    // Private
       
    60   "darkorchid3",   // "use" relation
       
    61   "grey75",        // Undocumented
       
    62   "orange"         // template relation
       
    63 };
       
    64 
       
    65 static const char *arrowStyle[] =
       
    66 {
       
    67   "empty",         // Public
       
    68   "empty",         // Protected
       
    69   "empty",         // Private
       
    70   "open",          // "use" relation
       
    71   0,               // Undocumented
       
    72   0                // template relation
       
    73 };
       
    74 
       
    75 static const char *edgeStyleMap[] =
       
    76 {
       
    77   "solid",         // inheritance
       
    78   "dashed"         // usage
       
    79 };
       
    80 
       
    81 static QCString getDotFontName()
       
    82 {
       
    83   static QCString dotFontName = Config_getString("DOT_FONTNAME");
       
    84   if (dotFontName.isEmpty()) dotFontName="FreeSans";
       
    85   return dotFontName;
       
    86 }
       
    87 
       
    88 static int getDotFontSize()
       
    89 {
       
    90   static int dotFontSize = Config_getInt("DOT_FONTSIZE");
       
    91   if (dotFontSize<4) dotFontSize=4;
       
    92   return dotFontSize;
       
    93 }
       
    94 
       
    95 static void writeGraphHeader(QTextStream &t)
       
    96 {
       
    97   t << "digraph G" << endl;
       
    98   t << "{" << endl;
       
    99   if (Config_getBool("DOT_TRANSPARENT"))
       
   100   {
       
   101     t << "  bgcolor=\"transparent\";" << endl;
       
   102   }
       
   103   t << "  edge [fontname=\"" << FONTNAME << "\","
       
   104        "fontsize=\"" << FONTSIZE << "\","
       
   105        "labelfontname=\"" << FONTNAME << "\","
       
   106        "labelfontsize=\"" << FONTSIZE << "\"];\n";
       
   107   t << "  node [fontname=\"" << FONTNAME << "\","
       
   108        "fontsize=\"" << FONTSIZE << "\",shape=record];\n";
       
   109 }
       
   110 
       
   111 static void writeGraphFooter(QTextStream &t)
       
   112 {
       
   113   t << "}" << endl;
       
   114 }
       
   115 
       
   116 /*! converts the rectangles in a client site image map into a stream
       
   117  *  \param t the stream to which the result is written.
       
   118  *  \param mapName the name of the map file.
       
   119  *  \param relPath the relative path to the root of the output directory
       
   120  *                 (used in case CREATE_SUBDIRS is enabled).
       
   121  *  \param urlOnly if FALSE the url field in the map contains an external
       
   122  *                 references followed by a $ and then the URL.
       
   123  *  \param context the context (file, class, or namespace) in which the
       
   124  *                 map file was found
       
   125  *  \returns TRUE if succesful.
       
   126  */
       
   127 static bool convertMapFile(QTextStream &t,const char *mapName,
       
   128                            const QCString relPath, bool urlOnly=FALSE,
       
   129                            const QString &context=QString())
       
   130 {
       
   131   QFile f(mapName);
       
   132   if (!f.open(IO_ReadOnly)) 
       
   133   {
       
   134     err("Error opening map file %s for inclusion in the docs!\n"
       
   135         "If you installed Graphviz/dot after a previous failing run, \n"
       
   136         "try deleting the output directory and rerun doxygen.\n",mapName);
       
   137     return FALSE;
       
   138   }
       
   139   const int maxLineLen=10240;
       
   140   while (!f.atEnd()) // foreach line
       
   141   {
       
   142     QCString buf(maxLineLen);
       
   143     int numBytes = f.readLine(buf.data(),maxLineLen);
       
   144     buf[numBytes-1]='\0';
       
   145 
       
   146     if (buf.left(5)=="<area")
       
   147     {
       
   148       // search for href="...", store ... part in link
       
   149       int indexS = buf.find("href=\""), indexE;
       
   150       if (indexS!=-1 && (indexE=buf.find('"',indexS+6))!=-1)
       
   151       {
       
   152         QCString link = buf.mid(indexS+6,indexE-indexS-6);
       
   153         QCString result;
       
   154         QCString *dest;
       
   155         if (urlOnly) // for user defined dot graphs
       
   156         {
       
   157           if (link.left(5)=="\\ref ") // \ref url
       
   158           {
       
   159             result="href=\"";
       
   160             // fake ref node to resolve the url
       
   161             DocRef *df = new DocRef( (DocNode*) 0, link.mid(5), context );
       
   162             if (!df->ref().isEmpty())
       
   163             {
       
   164               if ((dest=Doxygen::tagDestinationDict[df->ref()])) 
       
   165                 result += *dest + "/";
       
   166             }
       
   167             else if (!relPath.isEmpty())
       
   168             {
       
   169               result += relPath;
       
   170             }
       
   171             if (!df->file().isEmpty())  
       
   172               result += df->file().data() + Doxygen::htmlFileExtension;
       
   173             if (!df->anchor().isEmpty()) 
       
   174               result += "#" + df->anchor();
       
   175             delete df;
       
   176             result += "\"";
       
   177           }
       
   178           else
       
   179           {
       
   180             result = "href=\"" + link + "\"";
       
   181           }
       
   182         }
       
   183         else // ref$url (external ref via tag file), or $url (local ref)
       
   184         {
       
   185           int marker = link.find('$');
       
   186           if (marker!=-1)
       
   187           {
       
   188             QCString ref = link.left(marker);
       
   189             QCString url = link.mid(marker+1);
       
   190             if (!ref.isEmpty())
       
   191             {
       
   192               result = "doxygen=\"" + ref + ":";
       
   193               if ((dest=Doxygen::tagDestinationDict[ref])) result += *dest + "/";
       
   194               result += "\" ";
       
   195             }
       
   196             result+= "href=\"";
       
   197             if (!ref.isEmpty())
       
   198             {
       
   199               if ((dest=Doxygen::tagDestinationDict[ref])) result += *dest + "/";
       
   200             }
       
   201             else if (!relPath.isEmpty())
       
   202             {
       
   203               result += relPath;
       
   204             }
       
   205             result+= url + "\"";
       
   206           }
       
   207           else // should not happen, but handle properly anyway
       
   208           {
       
   209             result = "href=\"" + link + "\"";
       
   210           }
       
   211         }
       
   212         QCString leftPart = buf.left(indexS);
       
   213         QCString rightPart = buf.mid(indexE+1);
       
   214         buf = leftPart + result + rightPart;
       
   215       }
       
   216       t << buf;
       
   217     }
       
   218   }
       
   219   return TRUE;
       
   220 }
       
   221 
       
   222 static QArray<int> s_newNumber;
       
   223 static int s_max_newNumber=0;
       
   224 
       
   225 inline int reNumberNode(int number, bool doReNumbering)
       
   226 {
       
   227   if (!doReNumbering) 
       
   228   {
       
   229     return number;
       
   230   } 
       
   231   else 
       
   232   {
       
   233     int s = s_newNumber.size();
       
   234     if (number>=s) 
       
   235     {
       
   236       int ns=0;
       
   237       ns = s * 3 / 2 + 5; // new size
       
   238       if (number>=ns) // number still doesn't fit
       
   239       {
       
   240         ns = number * 3 / 2 + 5;
       
   241       }
       
   242       s_newNumber.resize(ns);
       
   243       for (int i=s;i<ns;i++) // clear new part of the array
       
   244       {
       
   245         s_newNumber.at(i)=0;
       
   246       }
       
   247     }
       
   248     int i = s_newNumber.at(number);
       
   249     if (i == 0) // not yet mapped
       
   250     {
       
   251       i = ++s_max_newNumber; // start from 1
       
   252       s_newNumber.at(number) = i;
       
   253     }
       
   254     return i;
       
   255   }
       
   256 }
       
   257 
       
   258 static void resetReNumbering() 
       
   259 {
       
   260   s_max_newNumber=0;
       
   261   s_newNumber.resize(s_max_newNumber);
       
   262 }
       
   263 
       
   264 static QCString g_dotFontPath;
       
   265 
       
   266 static void setDotFontPath(const char *path)
       
   267 {
       
   268   ASSERT(g_dotFontPath.isEmpty());
       
   269   g_dotFontPath = portable_getenv("DOTFONTPATH");
       
   270   QCString newFontPath = Config_getString("DOT_FONTPATH");
       
   271   if (!newFontPath.isEmpty() && path)
       
   272   {
       
   273     newFontPath.prepend(path+portable_pathListSeparator());
       
   274   }
       
   275   else if (newFontPath.isEmpty() && path)
       
   276   {
       
   277     newFontPath=path;
       
   278   }
       
   279   else
       
   280   {
       
   281     portable_unsetenv("DOTFONTPATH");
       
   282     return;
       
   283   }
       
   284   portable_setenv("DOTFONTPATH",newFontPath);
       
   285 }
       
   286 
       
   287 static void unsetDotFontPath()
       
   288 {
       
   289   portable_setenv("DOTFONTPATH",g_dotFontPath);
       
   290   g_dotFontPath="";
       
   291 }
       
   292 
       
   293 static bool readBoundingBoxEPS(const char *fileName,int *width,int *height)
       
   294 {
       
   295   QCString bb("%%PageBoundingBox:");
       
   296   QFile f(fileName);
       
   297   if (!f.open(IO_ReadOnly)) return FALSE;
       
   298   const int maxLineLen=1024;
       
   299   char buf[maxLineLen];
       
   300   while (!f.atEnd())
       
   301   {
       
   302     int numBytes = f.readLine(buf,maxLineLen-1); // read line
       
   303     buf[numBytes]='\0';
       
   304     if (strncmp(buf,bb.data(),bb.length()-1)==0) // found PageBoundingBox string
       
   305     {
       
   306       int x,y;
       
   307       if (sscanf(buf+bb.length(),"%d %d %d %d",&x,&y,width,height)!=4)
       
   308       {
       
   309         return FALSE;
       
   310       }
       
   311       return TRUE;
       
   312     }
       
   313   }
       
   314   return FALSE;
       
   315 }
       
   316 
       
   317 // since dot silently reproduces the input file when it does not
       
   318 // support the PNG format, we need to check the result.
       
   319 static void checkDotResult(const QCString &imgName)
       
   320 {
       
   321   if (Config_getEnum("DOT_IMAGE_FORMAT")=="png")
       
   322   {
       
   323     QFile f(imgName);
       
   324     if (f.open(IO_ReadOnly))
       
   325     {
       
   326       char data[4];
       
   327       if (f.readBlock(data,4)==4)
       
   328       {
       
   329         if (!(data[1]=='P' && data[2]=='N' && data[3]=='G'))
       
   330         {
       
   331           err("Error! Image `%s' produced by dot is not a valid PNG!\n"
       
   332               "You should either select a different format "
       
   333               "(DOT_IMAGE_FORMAT in the config file) or install a more "
       
   334               "recent version of graphviz (1.7+)\n",imgName.data()
       
   335              );
       
   336         }
       
   337       }
       
   338       else
       
   339       {
       
   340         err("Error: Could not read image `%s' generated by dot!\n",imgName.data());
       
   341       }
       
   342     }
       
   343     else
       
   344     {
       
   345       err("Error: Could not open image `%s' generated by dot!\n",imgName.data());
       
   346     }
       
   347   }
       
   348 }
       
   349 
       
   350 /*! Checks if a file "baseName".md5 exists. If so the contents
       
   351  *  are compared with \a md5. If equal FALSE is returned. If the .md5
       
   352  *  file does not exist or its contents are not equal to \a md5, 
       
   353  *  a new .md5 is generated with the \a md5 string as contents.
       
   354  */
       
   355 static bool checkAndUpdateMd5Signature(const QCString &baseName,const QCString &md5)
       
   356 {
       
   357   QFile f(baseName+".md5");
       
   358   if (f.open(IO_ReadOnly))
       
   359   {
       
   360     // read checksum
       
   361     QCString md5stored(33);
       
   362     int bytesRead=f.readBlock(md5stored.data(),32);
       
   363     md5stored[32]='\0';
       
   364     // compare checksum
       
   365     if (bytesRead==32 && md5==md5stored)
       
   366     {
       
   367       // bail out if equal
       
   368       return FALSE;
       
   369     }
       
   370   }
       
   371   f.close();
       
   372   // create checksum file
       
   373   if (f.open(IO_WriteOnly))
       
   374   {
       
   375     f.writeBlock(md5.data(),32); 
       
   376     f.close();
       
   377   }
       
   378   return TRUE;
       
   379 }
       
   380 
       
   381 //--------------------------------------------------------------------
       
   382 
       
   383 class DotNodeList : public QList<DotNode>
       
   384 {
       
   385   public:
       
   386     DotNodeList() : QList<DotNode>() {}
       
   387    ~DotNodeList() {}
       
   388    int compareItems(GCI item1,GCI item2)
       
   389    {
       
   390      return stricmp(((DotNode *)item1)->m_label,((DotNode *)item2)->m_label);
       
   391    }
       
   392 };
       
   393 
       
   394 //--------------------------------------------------------------------
       
   395 
       
   396 DotRunner::DotRunner(const char *file) : m_file(file)
       
   397 {
       
   398   m_jobs.setAutoDelete(TRUE);
       
   399 }
       
   400 
       
   401 void DotRunner::addJob(const char *format,const char *output)
       
   402 {
       
   403   QCString args = QCString("-T")+format+" -o \""+output+"\"";
       
   404   m_jobs.append(new QCString(args));
       
   405 }
       
   406 
       
   407 void DotRunner::addPostProcessing(const char *cmd,const char *args)
       
   408 {
       
   409   m_postCmd = cmd;
       
   410   m_postArgs = args;
       
   411 }
       
   412 
       
   413 bool DotRunner::run()
       
   414 {
       
   415   int exitCode=0;
       
   416   static QCString dotExe = Config_getString("DOT_PATH")+"dot";
       
   417   QCString dotArgs;
       
   418   QListIterator<QCString> li(m_jobs);
       
   419   QCString *s;
       
   420   if (Config_getBool("DOT_MULTI_TARGETS"))
       
   421   {
       
   422     dotArgs="\""+m_file+"\"";
       
   423     for (li.toFirst();(s=li.current());++li)
       
   424     {
       
   425       dotArgs+=' ';
       
   426       dotArgs+=*s;
       
   427     }
       
   428     if ((exitCode=portable_system(dotExe,dotArgs,FALSE))!=0)
       
   429     {
       
   430       goto error;
       
   431     }
       
   432   }
       
   433   else
       
   434   {
       
   435     for (li.toFirst();(s=li.current());++li)
       
   436     {
       
   437       dotArgs="\""+m_file+"\" "+*s;
       
   438       if ((exitCode=portable_system(dotExe,dotArgs,FALSE))!=0)
       
   439       {
       
   440         goto error;
       
   441       }
       
   442     }
       
   443   }
       
   444   if (!m_postCmd.isEmpty() && portable_system(m_postCmd,m_postArgs)!=0)
       
   445   {
       
   446     err("Error: Problems running '%s' as a post-processing step for dot output\n",m_postCmd.data());
       
   447     return FALSE;
       
   448   }
       
   449   return TRUE;
       
   450 error:
       
   451   err("Problems running dot: exit code=%d, command='%s', arguments='%s'\n",
       
   452       exitCode,dotExe.data(),dotArgs.data());
       
   453   return FALSE;
       
   454 }
       
   455 
       
   456 //--------------------------------------------------------------------
       
   457 
       
   458 
       
   459 /*! helper function that deletes all nodes in a connected graph, given
       
   460  *  one of the graph's nodes
       
   461  */
       
   462 static void deleteNodes(DotNode *node,SDict<DotNode> *skipNodes=0)
       
   463 {
       
   464   //printf("deleteNodes skipNodes=%p\n",skipNodes);
       
   465   static DotNodeList deletedNodes;
       
   466   deletedNodes.setAutoDelete(TRUE);
       
   467   node->deleteNode(deletedNodes,skipNodes); // collect nodes to be deleted.
       
   468   deletedNodes.clear(); // actually remove the nodes.
       
   469 }
       
   470 
       
   471 DotNode::DotNode(int n,const char *lab,const char *tip, const char *url,
       
   472                  bool isRoot,ClassDef *cd)
       
   473   : m_subgraphId(-1)
       
   474   , m_number(n)
       
   475   , m_label(lab)
       
   476   , m_tooltip(tip)
       
   477   , m_url(url)
       
   478   , m_parents(0)
       
   479   , m_children(0)
       
   480   , m_edgeInfo(0)
       
   481   , m_deleted(FALSE)
       
   482   , m_written(FALSE)
       
   483   , m_hasDoc(FALSE)
       
   484   , m_isRoot(isRoot)
       
   485   , m_classDef(cd)
       
   486   , m_visible(FALSE)
       
   487   , m_truncated(Unknown)
       
   488   , m_distance(1000)
       
   489 {
       
   490 }
       
   491 
       
   492 DotNode::~DotNode()
       
   493 {
       
   494   delete m_children;
       
   495   delete m_parents;
       
   496   delete m_edgeInfo;
       
   497 }
       
   498 
       
   499 void DotNode::addChild(DotNode *n,
       
   500                        int edgeColor,
       
   501                        int edgeStyle,
       
   502                        const char *edgeLab,
       
   503                        const char *edgeURL,
       
   504                        int edgeLabCol
       
   505                       )
       
   506 {
       
   507   if (m_children==0)
       
   508   {
       
   509     m_children = new QList<DotNode>;
       
   510     m_edgeInfo = new QList<EdgeInfo>;
       
   511     m_edgeInfo->setAutoDelete(TRUE);
       
   512   }
       
   513   m_children->append(n);
       
   514   EdgeInfo *ei = new EdgeInfo;
       
   515   ei->m_color = edgeColor;
       
   516   ei->m_style = edgeStyle; 
       
   517   ei->m_label = edgeLab;
       
   518   ei->m_url   = edgeURL;
       
   519   if (edgeLabCol==-1)
       
   520     ei->m_labColor=edgeColor;
       
   521   else
       
   522     ei->m_labColor=edgeLabCol;
       
   523   m_edgeInfo->append(ei);
       
   524 }
       
   525 
       
   526 void DotNode::addParent(DotNode *n)
       
   527 {
       
   528   if (m_parents==0)
       
   529   {
       
   530     m_parents = new QList<DotNode>;
       
   531   }
       
   532   m_parents->append(n);
       
   533 }
       
   534 
       
   535 void DotNode::removeChild(DotNode *n)
       
   536 {
       
   537   if (m_children) m_children->remove(n);
       
   538 }
       
   539 
       
   540 void DotNode::removeParent(DotNode *n)
       
   541 {
       
   542   if (m_parents) m_parents->remove(n);
       
   543 }
       
   544 
       
   545 void DotNode::deleteNode(DotNodeList &deletedList,SDict<DotNode> *skipNodes)
       
   546 {
       
   547   if (m_deleted) return; // avoid recursive loops in case the graph has cycles
       
   548   m_deleted=TRUE;
       
   549   if (m_parents!=0) // delete all parent nodes of this node
       
   550   {
       
   551     QListIterator<DotNode> dnlip(*m_parents);
       
   552     DotNode *pn;
       
   553     for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
       
   554     {
       
   555       //pn->removeChild(this);
       
   556       pn->deleteNode(deletedList,skipNodes);
       
   557     }
       
   558   }
       
   559   if (m_children!=0) // delete all child nodes of this node
       
   560   {
       
   561     QListIterator<DotNode> dnlic(*m_children);
       
   562     DotNode *cn;
       
   563     for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
       
   564     {
       
   565       //cn->removeParent(this);
       
   566       cn->deleteNode(deletedList,skipNodes);
       
   567     }
       
   568   }
       
   569   // add this node to the list of deleted nodes.
       
   570   //printf("skipNodes=%p find(%p)=%p\n",skipNodes,this,skipNodes ? skipNodes->find((int)this) : 0);
       
   571   if (skipNodes==0 || skipNodes->find((char*)this)==0)
       
   572   {
       
   573     //printf("deleting\n");
       
   574     deletedList.append(this);
       
   575   }
       
   576 }
       
   577 
       
   578 void DotNode::setDistance(int distance)
       
   579 {
       
   580   if (distance<m_distance) m_distance = distance;
       
   581 }
       
   582 
       
   583 static QCString convertLabel(const QCString &l)
       
   584 {
       
   585   QCString result;
       
   586   const char *p=l.data();
       
   587   if (p==0) return result;
       
   588   char c;
       
   589   while ((c=*p++))
       
   590   {
       
   591     switch(c)
       
   592     {
       
   593       case '\\': result+="\\\\"; break;
       
   594       case '\n': result+="\\n"; break;
       
   595       case '<':  result+="\\<"; break;
       
   596       case '>':  result+="\\>"; break;
       
   597       case '|':  result+="\\|"; break;
       
   598       case '{':  result+="\\{"; break;
       
   599       case '}':  result+="\\}"; break;
       
   600       case '"':  result+="\\\""; break;
       
   601       default:   result+=c; break;
       
   602     }
       
   603   }
       
   604   return result;
       
   605 }
       
   606 
       
   607 static QCString escapeTooltip(const QCString &tooltip)
       
   608 {
       
   609   QCString result;
       
   610   const char *p=tooltip.data();
       
   611   if (p==0) return result;
       
   612   char c;
       
   613   while ((c=*p++))
       
   614   {
       
   615     switch(c)
       
   616     {
       
   617       case '\\': result+="\\\\"; break;
       
   618       default:   result+=c; break;
       
   619     }
       
   620   }
       
   621   return result;
       
   622 }
       
   623 
       
   624 static void writeBoxMemberList(QTextStream &t,char prot,MemberList *ml,ClassDef *scope)
       
   625 {
       
   626   if (ml)
       
   627   {
       
   628     MemberListIterator mlia(*ml);
       
   629     MemberDef *mma;
       
   630     for (mlia.toFirst();(mma = mlia.current());++mlia)
       
   631     {
       
   632       if (mma->getClassDef() == scope)
       
   633       {
       
   634         t << prot << " " << convertLabel(mma->name());
       
   635         if (!mma->isObjCMethod() && 
       
   636             (mma->isFunction() || mma->isSlot() || mma->isSignal())) t << "()";
       
   637         t << "\\l";
       
   638       }
       
   639     }
       
   640     // write member groups within the memberlist
       
   641     MemberGroupList *mgl = ml->getMemberGroupList();
       
   642     if (mgl)
       
   643     {
       
   644       MemberGroupListIterator mgli(*mgl);
       
   645       MemberGroup *mg;
       
   646       for (mgli.toFirst();(mg=mgli.current());++mgli)
       
   647       {
       
   648         if (mg->members())
       
   649         {
       
   650           writeBoxMemberList(t,prot,mg->members(),scope);
       
   651         }
       
   652       }
       
   653     }
       
   654   }
       
   655 }
       
   656 
       
   657 void DotNode::writeBox(QTextStream &t,
       
   658                        GraphType gt,
       
   659                        GraphOutputFormat /*format*/,
       
   660                        bool hasNonReachableChildren,
       
   661                        bool reNumber)
       
   662 {
       
   663   const char *labCol = 
       
   664           m_url.isEmpty() ? "grey75" :  // non link
       
   665            (
       
   666             (hasNonReachableChildren) ? "red" : "black"
       
   667            );
       
   668   t << "  Node" << reNumberNode(m_number,reNumber) << " [label=\"";
       
   669 
       
   670   if (m_classDef && Config_getBool("UML_LOOK") && 
       
   671       (gt==Inheritance || gt==Collaboration))
       
   672   {
       
   673     t << "{" << convertLabel(m_label);
       
   674     t << "\\n|";
       
   675     writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubAttribs),m_classDef);
       
   676     writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubStaticAttribs),m_classDef);
       
   677     writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::properties),m_classDef);
       
   678     writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberList::pacAttribs),m_classDef);
       
   679     writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberList::pacStaticAttribs),m_classDef);
       
   680     writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proAttribs),m_classDef);
       
   681     writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proStaticAttribs),m_classDef);
       
   682     writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priAttribs),m_classDef);
       
   683     writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priStaticAttribs),m_classDef);
       
   684     t << "|";
       
   685     writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubMethods),m_classDef);
       
   686     writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubStaticMethods),m_classDef);
       
   687     writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubSlots),m_classDef);
       
   688     writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberList::pacMethods),m_classDef);
       
   689     writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberList::pacStaticMethods),m_classDef);
       
   690     writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proMethods),m_classDef);
       
   691     writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proStaticMethods),m_classDef);
       
   692     writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proSlots),m_classDef);
       
   693     writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priMethods),m_classDef);
       
   694     writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priStaticMethods),m_classDef);
       
   695     writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priSlots),m_classDef);
       
   696     if (m_classDef->getMemberGroupSDict())
       
   697     {
       
   698       MemberGroupSDict::Iterator mgdi(*m_classDef->getMemberGroupSDict());
       
   699       MemberGroup *mg;
       
   700       for (mgdi.toFirst();(mg=mgdi.current());++mgdi)
       
   701       {
       
   702         if (mg->members())
       
   703         {
       
   704           writeBoxMemberList(t,'*',mg->members(),m_classDef);
       
   705         }
       
   706       }
       
   707     }
       
   708     t << "}";
       
   709   }
       
   710   else // standard look
       
   711   {
       
   712     t << convertLabel(m_label);
       
   713   }
       
   714   t << "\",height=0.2,width=0.4";
       
   715   if (m_isRoot)
       
   716   {
       
   717     t << ",color=\"black\", fillcolor=\"grey75\", style=\"filled\" fontcolor=\"black\"";
       
   718   }
       
   719   else 
       
   720   {
       
   721     if (!Config_getBool("DOT_TRANSPARENT"))
       
   722     {
       
   723       t << ",color=\"" << labCol << "\", fillcolor=\"white\", style=\"filled\"";
       
   724     }
       
   725     else
       
   726     {
       
   727       t << ",color=\"" << labCol << "\"";
       
   728     }
       
   729     if (!m_url.isEmpty())
       
   730     {
       
   731       int anchorPos = m_url.findRev('#');
       
   732       if (anchorPos==-1)
       
   733       {
       
   734         t << ",URL=\"" << m_url << Doxygen::htmlFileExtension << "\"";
       
   735       }
       
   736       else
       
   737       {
       
   738         t << ",URL=\"" << m_url.left(anchorPos) << Doxygen::htmlFileExtension
       
   739           << m_url.right(m_url.length()-anchorPos) << "\"";
       
   740       }
       
   741     }
       
   742     if (!m_tooltip.isEmpty())
       
   743     {
       
   744       t << ",tooltip=\"" << escapeTooltip(m_tooltip) << "\"";
       
   745     }
       
   746   }
       
   747   t << "];" << endl; 
       
   748 }
       
   749 
       
   750 void DotNode::writeArrow(QTextStream &t,
       
   751                          GraphType gt,
       
   752                          GraphOutputFormat format,
       
   753                          DotNode *cn,
       
   754                          EdgeInfo *ei,
       
   755                          bool topDown, 
       
   756                          bool pointBack,
       
   757                          bool reNumber
       
   758                         )
       
   759 {
       
   760   t << "  Node";
       
   761   if (topDown) 
       
   762     t << reNumberNode(cn->number(),reNumber); 
       
   763   else 
       
   764     t << reNumberNode(m_number,reNumber);
       
   765   t << " -> Node";
       
   766   if (topDown) 
       
   767     t << reNumberNode(m_number,reNumber); 
       
   768   else 
       
   769     t << reNumberNode(cn->number(),reNumber);
       
   770   t << " [";
       
   771   if (pointBack) t << "dir=back,";
       
   772   t << "color=\"" << edgeColorMap[ei->m_color] 
       
   773     << "\",fontsize=\"" << FONTSIZE << "\",style=\"" << edgeStyleMap[ei->m_style] << "\"";
       
   774   if (!ei->m_label.isEmpty())
       
   775   {
       
   776     t << ",label=\"" << convertLabel(ei->m_label) << "\"";
       
   777   }
       
   778   if (Config_getBool("UML_LOOK") &&
       
   779       arrowStyle[ei->m_color] && 
       
   780       (gt==Inheritance || gt==Collaboration)
       
   781      )
       
   782   {
       
   783     if (pointBack) 
       
   784       t << ",arrowtail=\"" << arrowStyle[ei->m_color] << "\""; 
       
   785     else 
       
   786       t << ",arrowhead=\"" << arrowStyle[ei->m_color] << "\"";
       
   787   }
       
   788 
       
   789   if (format==BITMAP) t << ",fontname=\"" << FONTNAME << "\"";
       
   790   t << "];" << endl; 
       
   791 }
       
   792 
       
   793 void DotNode::write(QTextStream &t,
       
   794                     GraphType gt,
       
   795                     GraphOutputFormat format,
       
   796                     bool topDown,
       
   797                     bool toChildren,
       
   798                     bool backArrows,
       
   799                     bool reNumber
       
   800                    )
       
   801 {
       
   802   //printf("DotNode::write(%d) name=%s this=%p written=%d\n",distance,m_label.data(),this,m_written);
       
   803   if (m_written) return; // node already written to the output
       
   804   if (!m_visible) return; // node is not visible
       
   805   writeBox(t,gt,format,m_truncated==Truncated,reNumber);
       
   806   m_written=TRUE;
       
   807   QList<DotNode> *nl = toChildren ? m_children : m_parents; 
       
   808   if (nl)
       
   809   {
       
   810     if (toChildren)
       
   811     {
       
   812       QListIterator<DotNode>  dnli1(*nl);
       
   813       QListIterator<EdgeInfo> dnli2(*m_edgeInfo);
       
   814       DotNode *cn;
       
   815       for (dnli1.toFirst();(cn=dnli1.current());++dnli1,++dnli2)
       
   816       {
       
   817         if (cn->isVisible())
       
   818         {
       
   819           //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",cn->label().data());
       
   820           writeArrow(t,gt,format,cn,dnli2.current(),topDown,backArrows,reNumber);
       
   821         }
       
   822         cn->write(t,gt,format,topDown,toChildren,backArrows,reNumber);
       
   823       }
       
   824     }
       
   825     else // render parents
       
   826     {
       
   827       QListIterator<DotNode> dnli(*nl);
       
   828       DotNode *pn;
       
   829       for (dnli.toFirst();(pn=dnli.current());++dnli)
       
   830       {
       
   831         if (pn->isVisible())
       
   832         {
       
   833           //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",pn->label().data());
       
   834           writeArrow(t,
       
   835               gt,
       
   836               format,
       
   837               pn,
       
   838               pn->m_edgeInfo->at(pn->m_children->findRef(this)),
       
   839               FALSE,
       
   840               backArrows,
       
   841               reNumber
       
   842               );
       
   843         }
       
   844         pn->write(t,gt,format,TRUE,FALSE,backArrows,reNumber);
       
   845       }
       
   846     }
       
   847   }
       
   848   //printf("end DotNode::write(%d) name=%s\n",distance,m_label.data());
       
   849 }
       
   850 
       
   851 void DotNode::writeXML(QTextStream &t,bool isClassGraph)
       
   852 {
       
   853   t << "      <node id=\"" << m_number << "\">" << endl;
       
   854   t << "        <label>" << convertToXML(m_label) << "</label>" << endl;
       
   855   if (!m_url.isEmpty())
       
   856   {
       
   857     QCString url(m_url);
       
   858     char *refPtr = url.data();
       
   859     char *urlPtr = strchr(url.data(),'$');
       
   860     if (urlPtr)
       
   861     {
       
   862       *urlPtr++='\0';
       
   863       t << "        <link refid=\"" << convertToXML(urlPtr) << "\"";
       
   864       if (*refPtr!='\0')
       
   865       {
       
   866         t << " external=\"" << convertToXML(refPtr) << "\"";
       
   867       }
       
   868       t << "/>" << endl;
       
   869     }
       
   870   }
       
   871   if (m_children)
       
   872   {
       
   873     QListIterator<DotNode> nli(*m_children);
       
   874     QListIterator<EdgeInfo> eli(*m_edgeInfo);
       
   875     DotNode *childNode;
       
   876     EdgeInfo *edgeInfo;
       
   877     for (;(childNode=nli.current());++nli,++eli)
       
   878     {
       
   879       edgeInfo=eli.current();
       
   880       t << "        <childnode refid=\"" << childNode->m_number << "\" relation=\"";
       
   881       if (isClassGraph)
       
   882       {
       
   883         switch(edgeInfo->m_color)
       
   884         {
       
   885           case EdgeInfo::Blue:    t << "public-inheritance"; break;
       
   886           case EdgeInfo::Green:   t << "protected-inheritance"; break;
       
   887           case EdgeInfo::Red:     t << "private-inheritance"; break;
       
   888           case EdgeInfo::Purple:  t << "usage"; break;
       
   889           case EdgeInfo::Orange:  t << "template-instance"; break;
       
   890           case EdgeInfo::Grey:    ASSERT(0); break;
       
   891         }
       
   892       }
       
   893       else // include graph
       
   894       {
       
   895         t << "include"; 
       
   896       }
       
   897       t << "\">" << endl;
       
   898       if (!edgeInfo->m_label.isEmpty()) 
       
   899       {
       
   900         int p=0;
       
   901         int ni;
       
   902         while ((ni=edgeInfo->m_label.find('\n',p))!=-1)
       
   903         {
       
   904           t << "          <edgelabel>" 
       
   905             << convertToXML(edgeInfo->m_label.mid(p,ni-p))
       
   906             << "</edgelabel>" << endl;
       
   907           p=ni+1;
       
   908         }
       
   909         t << "          <edgelabel>" 
       
   910           << convertToXML(edgeInfo->m_label.right(edgeInfo->m_label.length()-p))
       
   911           << "</edgelabel>" << endl;
       
   912       }
       
   913       t << "        </childnode>" << endl;
       
   914     } 
       
   915   }
       
   916   t << "      </node>" << endl;
       
   917 }
       
   918 
       
   919 void DotNode::writeXML(XmlStream &xt, bool isClassGraph)
       
   920 {
       
   921 	QString idNumber;
       
   922 	idNumber.setNum(m_number);
       
   923 	XmlElement nodeElem(xt, "node", "id", idNumber);
       
   924 	{
       
   925 		XmlElement nodeLabel(xt, "label");
       
   926 		xt << m_label;
       
   927 	}
       
   928 	if (!m_url.isEmpty()) {
       
   929 		QCString url(m_url);
       
   930 		char *refPtr = url.data();
       
   931 		char *urlPtr = strchr(url.data(),'$');
       
   932 		if (urlPtr) {
       
   933 			*urlPtr++='\0';
       
   934 			AttributeMap linkAttrs;
       
   935 			linkAttrs["refid"] = convertToXML(urlPtr);
       
   936 			if (*refPtr!='\0') {
       
   937 				linkAttrs["external"] = convertToXML(refPtr);
       
   938 			}
       
   939 			XmlElement(xt, "link", linkAttrs);
       
   940 		}
       
   941 	}
       
   942 	if (m_children) {
       
   943 		QListIterator<DotNode> nli(*m_children);
       
   944 		QListIterator<EdgeInfo> eli(*m_edgeInfo);
       
   945 		DotNode *childNode;
       
   946 		EdgeInfo *edgeInfo;
       
   947 		for (;(childNode=nli.current());++nli,++eli) {
       
   948 			edgeInfo=eli.current();
       
   949 			AttributeMap childAttrs;
       
   950 			QString childIdNumber;
       
   951 			childIdNumber.setNum(childNode->m_number);
       
   952 			childAttrs["refid"] = childIdNumber; 
       
   953 			if (isClassGraph) {
       
   954 				switch(edgeInfo->m_color) {
       
   955 					case EdgeInfo::Blue:    childAttrs["relation"] = "public-inheritance"; break;
       
   956 					case EdgeInfo::Green:   childAttrs["relation"] = "protected-inheritance"; break;
       
   957 					case EdgeInfo::Red:     childAttrs["relation"] = "private-inheritance"; break;
       
   958 					case EdgeInfo::Purple:  childAttrs["relation"] = "usage"; break;
       
   959 					case EdgeInfo::Orange:  childAttrs["relation"] = "template-instance"; break;
       
   960 					case EdgeInfo::Grey:    ASSERT(0); break;
       
   961 				}
       
   962 			} else {
       
   963 				// include graph
       
   964 				childAttrs["relation"] = "include"; 
       
   965 			}
       
   966 			XmlElement childnodeElem(xt, "childnode", childAttrs);
       
   967 			if (!edgeInfo->m_label.isEmpty()) {
       
   968 				int p=0;
       
   969 				int ni;
       
   970 				while ((ni=edgeInfo->m_label.find('\n',p))!=-1) {
       
   971 					XmlElement edgelabelElem(xt, "edgelabel");
       
   972 					xt << edgeInfo->m_label.mid(p,ni-p);
       
   973 					p=ni+1;
       
   974 				}
       
   975 				XmlElement edgelabelElem(xt, "edgelabel");
       
   976 				xt << edgeInfo->m_label.right(edgeInfo->m_label.length()-p);
       
   977 			}
       
   978 		} 
       
   979 	}
       
   980 }
       
   981 
       
   982 void DotNode::writeXMLDITA(XmlStream &xt, bool isClassGraph)
       
   983 {
       
   984 	QString idNumber;
       
   985 	idNumber.setNum(m_number);
       
   986 	XmlElement nodeElem(xt, "node", "id", idNumber);
       
   987 	{
       
   988 		XmlElement nodeLabel(xt, "label");
       
   989 		xt << m_label;
       
   990 	}
       
   991 	if (!m_url.isEmpty()) {
       
   992 		QCString url(m_url);
       
   993 		char *refPtr = url.data();
       
   994 		char *urlPtr = strchr(url.data(),'$');
       
   995 		if (urlPtr) {
       
   996 			*urlPtr++='\0';
       
   997 			AttributeMap linkAttrs;
       
   998 			linkAttrs["refid"] = convertToXML(urlPtr);
       
   999 			if (*refPtr!='\0') {
       
  1000 				linkAttrs["external"] = convertToXML(refPtr);
       
  1001 			}
       
  1002 			XmlElement(xt, "link", linkAttrs);
       
  1003 		}
       
  1004 	}
       
  1005 	if (m_children) {
       
  1006 		QListIterator<DotNode> nli(*m_children);
       
  1007 		QListIterator<EdgeInfo> eli(*m_edgeInfo);
       
  1008 		DotNode *childNode;
       
  1009 		EdgeInfo *edgeInfo;
       
  1010 		for (;(childNode=nli.current());++nli,++eli) {
       
  1011 			edgeInfo=eli.current();
       
  1012 			AttributeMap childAttrs;
       
  1013 			QString childIdNumber;
       
  1014 			childIdNumber.setNum(childNode->m_number);
       
  1015 			childAttrs["refid"] = childIdNumber; 
       
  1016 			if (isClassGraph) {
       
  1017 				switch(edgeInfo->m_color) {
       
  1018 					case EdgeInfo::Blue:    childAttrs["relation"] = "public-inheritance"; break;
       
  1019 					case EdgeInfo::Green:   childAttrs["relation"] = "protected-inheritance"; break;
       
  1020 					case EdgeInfo::Red:     childAttrs["relation"] = "private-inheritance"; break;
       
  1021 					case EdgeInfo::Purple:  childAttrs["relation"] = "usage"; break;
       
  1022 					case EdgeInfo::Orange:  childAttrs["relation"] = "template-instance"; break;
       
  1023 					case EdgeInfo::Grey:    ASSERT(0); break;
       
  1024 				}
       
  1025 			} else {
       
  1026 				// include graph
       
  1027 				childAttrs["relation"] = "include"; 
       
  1028 			}
       
  1029 			XmlElement childnodeElem(xt, "childnode", childAttrs);
       
  1030 			if (!edgeInfo->m_label.isEmpty()) {
       
  1031 				int p=0;
       
  1032 				int ni;
       
  1033 				while ((ni=edgeInfo->m_label.find('\n',p))!=-1) {
       
  1034 					XmlElement edgelabelElem(xt, "edgelabel");
       
  1035 					xt << edgeInfo->m_label.mid(p,ni-p);
       
  1036 					p=ni+1;
       
  1037 				}
       
  1038 				XmlElement edgelabelElem(xt, "edgelabel");
       
  1039 				xt << edgeInfo->m_label.right(edgeInfo->m_label.length()-p);
       
  1040 			}
       
  1041 		} 
       
  1042 	}
       
  1043 }
       
  1044 
       
  1045 void DotNode::writeDEF(QTextStream &t)
       
  1046 {
       
  1047   const char* nodePrefix = "        node-";
       
  1048 
       
  1049   t << "      node = {" << endl;
       
  1050   t << nodePrefix << "id    = " << m_number << ';' << endl;
       
  1051   t << nodePrefix << "label = '" << m_label << "';" << endl;
       
  1052 
       
  1053   if (!m_url.isEmpty())
       
  1054   {
       
  1055     QCString url(m_url);
       
  1056     char *refPtr = url.data();
       
  1057     char *urlPtr = strchr(url.data(),'$');
       
  1058     if (urlPtr)
       
  1059     {
       
  1060       *urlPtr++='\0';
       
  1061       t << nodePrefix << "link = {" << endl << "  "
       
  1062         << nodePrefix << "link-id = '" << urlPtr << "';" << endl;
       
  1063 
       
  1064       if (*refPtr!='\0')
       
  1065       {
       
  1066         t << "  " << nodePrefix << "link-external = '"
       
  1067           << refPtr << "';" << endl;
       
  1068       }
       
  1069       t << "        };" << endl;
       
  1070     }
       
  1071   }
       
  1072   if (m_children)
       
  1073   {
       
  1074     QListIterator<DotNode> nli(*m_children);
       
  1075     QListIterator<EdgeInfo> eli(*m_edgeInfo);
       
  1076     DotNode *childNode;
       
  1077     EdgeInfo *edgeInfo;
       
  1078     for (;(childNode=nli.current());++nli,++eli)
       
  1079     {
       
  1080       edgeInfo=eli.current();
       
  1081       t << "        node-child = {" << endl;
       
  1082       t << "          child-id = '" << childNode->m_number << "';" << endl;
       
  1083       t << "          relation = ";
       
  1084 
       
  1085       switch(edgeInfo->m_color)
       
  1086       {
       
  1087         case EdgeInfo::Blue:    t << "public-inheritance"; break;
       
  1088         case EdgeInfo::Green:   t << "protected-inheritance"; break;
       
  1089         case EdgeInfo::Red:     t << "private-inheritance"; break;
       
  1090         case EdgeInfo::Purple:  t << "usage"; break;
       
  1091         case EdgeInfo::Orange:  t << "template-instance"; break;
       
  1092         case EdgeInfo::Grey:    ASSERT(0); break;
       
  1093       }
       
  1094       t << ';' << endl;
       
  1095 
       
  1096       if (!edgeInfo->m_label.isEmpty()) 
       
  1097       {
       
  1098         t << "          edgelabel = <<_EnD_oF_dEf_TeXt_" << endl
       
  1099           << edgeInfo->m_label << endl
       
  1100           << "_EnD_oF_dEf_TeXt_;" << endl;
       
  1101       }
       
  1102       t << "        }; /* node-child */" << endl;
       
  1103     } /* for (;childNode...) */
       
  1104   }
       
  1105   t << "      }; /* node */" << endl;
       
  1106 }
       
  1107 
       
  1108 
       
  1109 void DotNode::clearWriteFlag()
       
  1110 {
       
  1111   m_written=FALSE;
       
  1112   if (m_parents!=0)
       
  1113   {
       
  1114     QListIterator<DotNode> dnlip(*m_parents);
       
  1115     DotNode *pn;
       
  1116     for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
       
  1117     {
       
  1118       if (pn->m_written)
       
  1119       {
       
  1120         pn->clearWriteFlag();
       
  1121       }
       
  1122     }
       
  1123   }
       
  1124   if (m_children!=0)
       
  1125   {
       
  1126     QListIterator<DotNode> dnlic(*m_children);
       
  1127     DotNode *cn;
       
  1128     for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
       
  1129     {
       
  1130       if (cn->m_written)
       
  1131       {
       
  1132         cn->clearWriteFlag();
       
  1133       }
       
  1134     }
       
  1135   }
       
  1136 }
       
  1137 
       
  1138 void DotNode::colorConnectedNodes(int curColor)
       
  1139 { 
       
  1140   if (m_children)
       
  1141   {
       
  1142     QListIterator<DotNode> dnlic(*m_children);
       
  1143     DotNode *cn;
       
  1144     for (dnlic.toFirst();(cn=dnlic.current());++dnlic)
       
  1145     {
       
  1146       if (cn->m_subgraphId==-1) // uncolored child node
       
  1147       {
       
  1148         cn->m_subgraphId=curColor;
       
  1149         cn->markAsVisible();
       
  1150         cn->colorConnectedNodes(curColor);
       
  1151         //printf("coloring node %s (%p): %d\n",cn->m_label.data(),cn,cn->m_subgraphId);
       
  1152       }
       
  1153     }
       
  1154   }
       
  1155 
       
  1156   if (m_parents)
       
  1157   {
       
  1158     QListIterator<DotNode> dnlip(*m_parents);
       
  1159     DotNode *pn;
       
  1160     for (dnlip.toFirst();(pn=dnlip.current());++dnlip)
       
  1161     {
       
  1162       if (pn->m_subgraphId==-1) // uncolored parent node
       
  1163       {
       
  1164         pn->m_subgraphId=curColor;
       
  1165         pn->markAsVisible();
       
  1166         pn->colorConnectedNodes(curColor);
       
  1167         //printf("coloring node %s (%p): %d\n",pn->m_label.data(),pn,pn->m_subgraphId);
       
  1168       }
       
  1169     }
       
  1170   }
       
  1171 }
       
  1172 
       
  1173 const DotNode *DotNode::findDocNode() const
       
  1174 {
       
  1175   if (!m_url.isEmpty()) return this;
       
  1176   //printf("findDocNode(): `%s'\n",m_label.data());
       
  1177   if (m_parents)
       
  1178   {
       
  1179     QListIterator<DotNode> dnli(*m_parents);
       
  1180     DotNode *pn;
       
  1181     for (dnli.toFirst();(pn=dnli.current());++dnli)
       
  1182     {
       
  1183       if (!pn->m_hasDoc)
       
  1184       {
       
  1185         pn->m_hasDoc=TRUE;
       
  1186         const DotNode *dn = pn->findDocNode();
       
  1187         if (dn) return dn;
       
  1188       }
       
  1189     }
       
  1190   }
       
  1191   if (m_children)
       
  1192   {
       
  1193     QListIterator<DotNode> dnli(*m_children);
       
  1194     DotNode *cn;
       
  1195     for (dnli.toFirst();(cn=dnli.current());++dnli)
       
  1196     {
       
  1197       if (!cn->m_hasDoc)
       
  1198       {
       
  1199         cn->m_hasDoc=TRUE;
       
  1200         const DotNode *dn = cn->findDocNode();
       
  1201         if (dn) return dn;
       
  1202       }
       
  1203     }
       
  1204   }
       
  1205   return 0;
       
  1206 }
       
  1207 
       
  1208 //--------------------------------------------------------------------
       
  1209 
       
  1210 int DotGfxHierarchyTable::m_curNodeNumber;
       
  1211 
       
  1212 void DotGfxHierarchyTable::writeGraph(QTextStream &out,const char *path) const
       
  1213 {
       
  1214   //printf("DotGfxHierarchyTable::writeGraph(%s)\n",name);
       
  1215   //printf("m_rootNodes=%p count=%d\n",m_rootNodes,m_rootNodes->count());
       
  1216   if (m_rootSubgraphs->count()==0) return;
       
  1217 
       
  1218   QDir d(path);
       
  1219   // store the original directory
       
  1220   if (!d.exists())
       
  1221   {
       
  1222     err("Error: Output dir %s does not exist!\n",path); exit(1);
       
  1223   }
       
  1224   setDotFontPath(d.absPath());
       
  1225   //QCString oldDir = convertToQCString(QDir::currentDirPath());
       
  1226   // go to the html output directory (i.e. path)
       
  1227   //QDir::setCurrent(d.absPath());
       
  1228   //QDir thisDir;
       
  1229 
       
  1230   // put each connected subgraph of the hierarchy in a row of the HTML output
       
  1231   out << "<table border=\"0\" cellspacing=\"10\" cellpadding=\"0\">" << endl;
       
  1232 
       
  1233   QListIterator<DotNode> dnli(*m_rootSubgraphs);
       
  1234   DotNode *n;
       
  1235   int count=0;
       
  1236   for (dnli.toFirst();(n=dnli.current());++dnli)
       
  1237   {
       
  1238     QCString baseName;
       
  1239     QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
       
  1240     baseName.sprintf("inherit_graph_%d",count++);
       
  1241     baseName = convertNameToFile(baseName);
       
  1242     QCString imgName = baseName+"."+ imgExt;
       
  1243     QCString mapName = baseName+".map";
       
  1244     QCString absImgName = QCString(d.absPath().data())+"/"+imgName;
       
  1245     QCString absMapName = QCString(d.absPath().data())+"/"+mapName;
       
  1246     QCString absBaseName = QCString(d.absPath().data())+"/"+baseName;
       
  1247     QListIterator<DotNode> dnli2(*m_rootNodes);
       
  1248     DotNode *node;
       
  1249 
       
  1250     // compute md5 checksum of the graph were are about to generate
       
  1251     QString theGraph;
       
  1252     QTextStream md5stream(&theGraph,IO_WriteOnly);
       
  1253     md5stream.setEncoding(md5stream.UnicodeUTF8);
       
  1254     writeGraphHeader(md5stream);
       
  1255     md5stream << "  rankdir=LR;" << endl;
       
  1256     for (dnli2.toFirst();(node=dnli2.current());++dnli2)
       
  1257     {
       
  1258       if (node->m_subgraphId==n->m_subgraphId) 
       
  1259       {
       
  1260         node->clearWriteFlag();
       
  1261       }
       
  1262     }
       
  1263     for (dnli2.toFirst();(node=dnli2.current());++dnli2)
       
  1264     {
       
  1265       if (node->m_subgraphId==n->m_subgraphId) 
       
  1266       {
       
  1267         node->write(md5stream,DotNode::Hierarchy,BITMAP,FALSE,TRUE,TRUE,TRUE);
       
  1268       }
       
  1269     }
       
  1270     writeGraphFooter(md5stream);
       
  1271     resetReNumbering();
       
  1272     uchar md5_sig[16];
       
  1273     QCString sigStr(33);
       
  1274     MD5Buffer((const unsigned char *)theGraph.ascii(),theGraph.length(),md5_sig);
       
  1275     MD5SigToString(md5_sig,sigStr.data(),33);
       
  1276     if (checkAndUpdateMd5Signature(absBaseName,sigStr) || 
       
  1277         !QFileInfo(absMapName).exists())
       
  1278     {
       
  1279       // image was new or has changed
       
  1280       QCString dotName=absBaseName+".dot";
       
  1281       QFile f(dotName);
       
  1282       if (!f.open(IO_WriteOnly)) return;
       
  1283       QTextStream t(&f);
       
  1284       t.setEncoding(t.UnicodeUTF8);
       
  1285       t << theGraph;
       
  1286       f.close();
       
  1287       resetReNumbering();
       
  1288 
       
  1289       DotRunner dotRun(dotName);
       
  1290       dotRun.addJob(imgExt,absImgName);
       
  1291       dotRun.addJob(MAP_CMD,absMapName);
       
  1292       if (!dotRun.run())
       
  1293       {
       
  1294         out << "</table>" << endl;
       
  1295         unsetDotFontPath();
       
  1296         return;
       
  1297       }
       
  1298       checkDotResult(absImgName);
       
  1299       if (Config_getBool("DOT_CLEANUP")) d.remove(dotName);
       
  1300     }
       
  1301     Doxygen::indexList.addImageFile(imgName);
       
  1302     // write image and map in a table row
       
  1303     QCString mapLabel = escapeCharsInString(n->m_label,FALSE);
       
  1304     out << "<tr><td><img src=\"" << imgName << "\" border=\"0\" alt=\"\" usemap=\"#"
       
  1305         << mapLabel << "_map\"/>" << endl;
       
  1306     out << "<map name=\"" << mapLabel << "_map\" id=\"" << mapLabel << "\">" << endl;
       
  1307     convertMapFile(out,absMapName,"");
       
  1308     out << "</map></td></tr>" << endl;
       
  1309     //thisDir.remove(mapName);
       
  1310   }
       
  1311   out << "</table>" << endl;
       
  1312 
       
  1313   unsetDotFontPath();
       
  1314 }
       
  1315 
       
  1316 void DotGfxHierarchyTable::addHierarchy(DotNode *n,ClassDef *cd,bool hideSuper)
       
  1317 {
       
  1318   //printf("addHierarchy `%s' baseClasses=%d\n",cd->name().data(),cd->baseClasses()->count());
       
  1319   if (cd->subClasses())
       
  1320   {
       
  1321     BaseClassListIterator bcli(*cd->subClasses());
       
  1322     BaseClassDef *bcd;
       
  1323     for ( ; (bcd=bcli.current()) ; ++bcli )
       
  1324     {
       
  1325       ClassDef *bClass=bcd->classDef; 
       
  1326       //printf("  Trying sub class=`%s' usedNodes=%d\n",bClass->name().data(),m_usedNodes->count());
       
  1327       if (bClass->isVisibleInHierarchy() && hasVisibleRoot(bClass->baseClasses()))
       
  1328       {
       
  1329         DotNode *bn;
       
  1330         //printf("  Node `%s' Found visible class=`%s'\n",n->m_label.data(),
       
  1331         //                                              bClass->name().data());
       
  1332         if ((bn=m_usedNodes->find(bClass->name()))) // node already present 
       
  1333         {
       
  1334           if (n->m_children==0 || n->m_children->findRef(bn)==-1) // no arrow yet
       
  1335           {
       
  1336             n->addChild(bn,bcd->prot);
       
  1337             bn->addParent(n);
       
  1338             //printf("  Adding node %s to existing base node %s (c=%d,p=%d)\n",
       
  1339             //       n->m_label.data(),
       
  1340             //       bn->m_label.data(),
       
  1341             //       bn->m_children ? bn->m_children->count() : 0,
       
  1342             //       bn->m_parents  ? bn->m_parents->count()  : 0
       
  1343             //     );
       
  1344           }
       
  1345           //else
       
  1346           //{
       
  1347           //  printf("  Class already has an arrow!\n");
       
  1348           //}
       
  1349         }
       
  1350         else 
       
  1351         {
       
  1352           QCString tmp_url="";
       
  1353           if (bClass->isLinkable() && !bClass->isHidden())
       
  1354           {
       
  1355             tmp_url=bClass->getReference()+"$"+bClass->getOutputFileBase();
       
  1356           }
       
  1357           QCString tooltip = bClass->briefDescriptionAsTooltip();
       
  1358           bn = new DotNode(m_curNodeNumber++,
       
  1359               bClass->displayName(),
       
  1360               tooltip,
       
  1361               tmp_url.data()
       
  1362               );
       
  1363           n->addChild(bn,bcd->prot);
       
  1364           bn->addParent(n);
       
  1365           //printf("  Adding node %s to new base node %s (c=%d,p=%d)\n",
       
  1366           //   n->m_label.data(),
       
  1367           //   bn->m_label.data(),
       
  1368           //   bn->m_children ? bn->m_children->count() : 0,
       
  1369           //   bn->m_parents  ? bn->m_parents->count()  : 0
       
  1370           //  );
       
  1371           //printf("  inserting %s (%p)\n",bClass->name().data(),bn);
       
  1372           m_usedNodes->insert(bClass->name(),bn); // add node to the used list
       
  1373         }
       
  1374         if (!bClass->visited && !hideSuper && bClass->subClasses())
       
  1375         {
       
  1376           bool wasVisited=bClass->visited;
       
  1377           bClass->visited=TRUE;
       
  1378           addHierarchy(bn,bClass,wasVisited);
       
  1379         }
       
  1380       }
       
  1381     }
       
  1382   }
       
  1383   //printf("end addHierarchy\n");
       
  1384 }
       
  1385 
       
  1386 void DotGfxHierarchyTable::addClassList(ClassSDict *cl)
       
  1387 {
       
  1388   ClassSDict::Iterator cli(*cl);
       
  1389   ClassDef *cd;
       
  1390   for (cli.toLast();(cd=cli.current());--cli)
       
  1391   {
       
  1392     //printf("Trying %s subClasses=%d\n",cd->name().data(),cd->subClasses()->count());
       
  1393     if (!hasVisibleRoot(cd->baseClasses()) &&
       
  1394         cd->isVisibleInHierarchy()
       
  1395        ) // root node in the forest
       
  1396     {
       
  1397       QCString tmp_url="";
       
  1398       if (cd->isLinkable() && !cd->isHidden()) 
       
  1399       {
       
  1400         tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
       
  1401       }
       
  1402       //printf("Inserting root class %s\n",cd->name().data());
       
  1403       QCString tooltip = cd->briefDescriptionAsTooltip();
       
  1404       DotNode *n = new DotNode(m_curNodeNumber++,
       
  1405           cd->displayName(),
       
  1406           tooltip,
       
  1407           tmp_url.data());
       
  1408 
       
  1409       //m_usedNodes->clear();
       
  1410       m_usedNodes->insert(cd->name(),n);
       
  1411       m_rootNodes->insert(0,n);   
       
  1412       if (!cd->visited && cd->subClasses())
       
  1413       {
       
  1414         addHierarchy(n,cd,cd->visited);
       
  1415         cd->visited=TRUE;
       
  1416       }
       
  1417     }
       
  1418   }
       
  1419 }
       
  1420 
       
  1421 DotGfxHierarchyTable::DotGfxHierarchyTable()
       
  1422 {
       
  1423   m_curNodeNumber=0;
       
  1424   m_rootNodes = new QList<DotNode>;
       
  1425   m_usedNodes = new QDict<DotNode>(1009); 
       
  1426   m_usedNodes->setAutoDelete(TRUE);
       
  1427   m_rootSubgraphs = new DotNodeList;
       
  1428   
       
  1429   // build a graph with each class as a node and the inheritance relations
       
  1430   // as edges
       
  1431   initClassHierarchy(Doxygen::classSDict);
       
  1432   initClassHierarchy(Doxygen::hiddenClasses);
       
  1433   addClassList(Doxygen::classSDict);
       
  1434   addClassList(Doxygen::hiddenClasses);
       
  1435   // m_usedNodes now contains all nodes in the graph
       
  1436  
       
  1437   // color the graph into a set of independent subgraphs
       
  1438   bool done=FALSE; 
       
  1439   int curColor=0;
       
  1440   QListIterator<DotNode> dnli(*m_rootNodes);
       
  1441   while (!done) // there are still nodes to color
       
  1442   {
       
  1443     DotNode *n;
       
  1444     done=TRUE; // we are done unless there are still uncolored nodes
       
  1445     for (dnli.toLast();(n=dnli.current());--dnli)
       
  1446     {
       
  1447       if (n->m_subgraphId==-1) // not yet colored
       
  1448       {
       
  1449         //printf("Starting at node %s (%p): %d\n",n->m_label.data(),n,curColor);
       
  1450         done=FALSE; // still uncolored nodes
       
  1451         n->m_subgraphId=curColor;
       
  1452         n->markAsVisible();
       
  1453         n->colorConnectedNodes(curColor);
       
  1454         curColor++;
       
  1455         const DotNode *dn=n->findDocNode();
       
  1456         if (dn!=0) 
       
  1457           m_rootSubgraphs->inSort(dn);
       
  1458         else
       
  1459           m_rootSubgraphs->inSort(n);
       
  1460       }
       
  1461     }
       
  1462   }
       
  1463   
       
  1464   //printf("Number of independent subgraphs: %d\n",curColor);
       
  1465   //QListIterator<DotNode> dnli2(*m_rootSubgraphs);
       
  1466   //DotNode *n;
       
  1467   //for (dnli2.toFirst();(n=dnli2.current());++dnli2)
       
  1468   //{
       
  1469   //  printf("Node %s color=%d (c=%d,p=%d)\n",
       
  1470   //      n->m_label.data(),n->m_subgraphId,
       
  1471   //      n->m_children?n->m_children->count():0,
       
  1472   //      n->m_parents?n->m_parents->count():0);
       
  1473   //}
       
  1474 }
       
  1475 
       
  1476 DotGfxHierarchyTable::~DotGfxHierarchyTable()
       
  1477 {
       
  1478   //printf("DotGfxHierarchyTable::~DotGfxHierarchyTable\n");
       
  1479 
       
  1480   //QDictIterator<DotNode> di(*m_usedNodes);
       
  1481   //DotNode *n;
       
  1482   //for (;(n=di.current());++di)
       
  1483   //{
       
  1484   //  printf("Node %p: %s\n",n,n->label().data());
       
  1485   //}
       
  1486   
       
  1487   delete m_rootNodes;
       
  1488   delete m_usedNodes;
       
  1489   delete m_rootSubgraphs;
       
  1490 }
       
  1491 
       
  1492 //--------------------------------------------------------------------
       
  1493 
       
  1494 int DotClassGraph::m_curNodeNumber = 0;
       
  1495 
       
  1496 void DotClassGraph::addClass(ClassDef *cd,DotNode *n,int prot,
       
  1497     const char *label,const char *usedName,const char *templSpec,bool base,int distance)
       
  1498 {
       
  1499   if (Config_getBool("HIDE_UNDOC_CLASSES") && !cd->isLinkable()) return;
       
  1500 
       
  1501   int edgeStyle = (label || prot==EdgeInfo::Orange) ? EdgeInfo::Dashed : EdgeInfo::Solid;
       
  1502   QCString className;
       
  1503   if (usedName) // name is a typedef
       
  1504   {
       
  1505     className=usedName;
       
  1506   }
       
  1507   else if (templSpec) // name has a template part
       
  1508   {
       
  1509     className=insertTemplateSpecifierInScope(cd->name(),templSpec);
       
  1510   }
       
  1511   else // just a normal name
       
  1512   {
       
  1513     className=cd->displayName();
       
  1514   }
       
  1515   //printf("DotClassGraph::addClass(class=`%s',parent=%s,prot=%d,label=%s,dist=%d,usedName=%s,templSpec=%s,base=%d)\n",
       
  1516   //                                 className.data(),n->m_label.data(),prot,label,distance,usedName,templSpec,base);
       
  1517   DotNode *bn = m_usedNodes->find(className);
       
  1518   if (bn) // class already inserted
       
  1519   {
       
  1520     if (base)
       
  1521     {
       
  1522       n->addChild(bn,prot,edgeStyle,label);
       
  1523       bn->addParent(n);
       
  1524     }
       
  1525     else
       
  1526     {
       
  1527       bn->addChild(n,prot,edgeStyle,label);
       
  1528       n->addParent(bn);
       
  1529     }
       
  1530     bn->setDistance(distance);
       
  1531     //printf(" add exiting node %s of %s\n",bn->m_label.data(),n->m_label.data());
       
  1532   }
       
  1533   else // new class
       
  1534   {
       
  1535     QCString displayName=className;
       
  1536     if (Config_getBool("HIDE_SCOPE_NAMES")) displayName=stripScope(displayName);
       
  1537     QCString tmp_url;
       
  1538     if (cd->isLinkable() && !cd->isHidden()) 
       
  1539     {
       
  1540       tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
       
  1541     }
       
  1542     QCString tooltip = cd->briefDescriptionAsTooltip();
       
  1543     bn = new DotNode(m_curNodeNumber++,
       
  1544         displayName,
       
  1545         tooltip,
       
  1546         tmp_url.data(),
       
  1547         FALSE,        // rootNode
       
  1548         cd
       
  1549        );
       
  1550     if (base)
       
  1551     {
       
  1552       n->addChild(bn,prot,edgeStyle,label);
       
  1553       bn->addParent(n);
       
  1554     }
       
  1555     else
       
  1556     {
       
  1557       bn->addChild(n,prot,edgeStyle,label);
       
  1558       n->addParent(bn);
       
  1559     }
       
  1560     bn->setDistance(distance);
       
  1561     m_usedNodes->insert(className,bn);
       
  1562     //printf(" add new child node `%s' to %s hidden=%d url=%s\n",
       
  1563     //    className.data(),n->m_label.data(),cd->isHidden(),tmp_url.data());
       
  1564     
       
  1565     buildGraph(cd,bn,base,distance+1);
       
  1566   }
       
  1567 }
       
  1568 
       
  1569 void DotClassGraph::determineTruncatedNodes(QList<DotNode> &queue,bool includeParents)
       
  1570 {
       
  1571   while (queue.count()>0)
       
  1572   {
       
  1573     DotNode *n = queue.take(0);
       
  1574     if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
       
  1575     {
       
  1576       bool truncated = FALSE;
       
  1577       if (n->m_children)
       
  1578       {
       
  1579         QListIterator<DotNode> li(*n->m_children);
       
  1580         DotNode *dn;
       
  1581         for (li.toFirst();(dn=li.current());++li)
       
  1582         {
       
  1583           if (!dn->isVisible()) 
       
  1584             truncated = TRUE;
       
  1585           else 
       
  1586             queue.append(dn);
       
  1587         }
       
  1588       }
       
  1589       if (n->m_parents && includeParents)
       
  1590       {
       
  1591         QListIterator<DotNode> li(*n->m_parents);
       
  1592         DotNode *dn;
       
  1593         for (li.toFirst();(dn=li.current());++li)
       
  1594         {
       
  1595           if (!dn->isVisible()) 
       
  1596             truncated = TRUE;
       
  1597           else 
       
  1598             queue.append(dn);
       
  1599         }
       
  1600       }
       
  1601       n->markAsTruncated(truncated);
       
  1602     }
       
  1603   }
       
  1604 }
       
  1605 
       
  1606 bool DotClassGraph::determineVisibleNodes(DotNode *rootNode,
       
  1607                                           int maxNodes,bool includeParents)
       
  1608 {
       
  1609   QList<DotNode> childQueue;
       
  1610   QList<DotNode> parentQueue;
       
  1611   QArray<int> childTreeWidth;
       
  1612   QArray<int> parentTreeWidth;
       
  1613   childQueue.append(rootNode);
       
  1614   if (includeParents) parentQueue.append(rootNode);
       
  1615   bool firstNode=TRUE; // flag to force reprocessing rootNode in the parent loop 
       
  1616                        // despite being marked visible in the child loop
       
  1617   while ((childQueue.count()>0 || parentQueue.count()>0) && maxNodes>0)
       
  1618   {
       
  1619     static int maxDistance = Config_getInt("MAX_DOT_GRAPH_DEPTH");
       
  1620     if (childQueue.count()>0)
       
  1621     {
       
  1622       DotNode *n = childQueue.take(0);
       
  1623       int distance = n->distance();
       
  1624       if (!n->isVisible() && distance<maxDistance) // not yet processed
       
  1625       {
       
  1626         if (distance>0)
       
  1627         {
       
  1628           int oldSize=(int)childTreeWidth.size();
       
  1629           if (distance>oldSize)
       
  1630           {
       
  1631             childTreeWidth.resize(QMAX(childTreeWidth.size(),(uint)distance));
       
  1632             int i; for (i=oldSize;i<distance;i++) childTreeWidth[i]=0;
       
  1633           }
       
  1634           childTreeWidth[distance-1]+=n->label().length();
       
  1635         }
       
  1636         n->markAsVisible();
       
  1637         maxNodes--;
       
  1638         // add direct children
       
  1639         if (n->m_children)
       
  1640         {
       
  1641           QListIterator<DotNode> li(*n->m_children);
       
  1642           DotNode *dn;
       
  1643           for (li.toFirst();(dn=li.current());++li)
       
  1644           {
       
  1645             childQueue.append(dn);
       
  1646           }
       
  1647         }
       
  1648       }
       
  1649     }
       
  1650     if (includeParents && parentQueue.count()>0)
       
  1651     {
       
  1652       DotNode *n = parentQueue.take(0);
       
  1653       if ((!n->isVisible() || firstNode) && n->distance()<maxDistance) // not yet processed
       
  1654       {
       
  1655         firstNode=FALSE;
       
  1656         int distance = n->distance();
       
  1657         if (distance>0)
       
  1658         {
       
  1659           int oldSize = (int)parentTreeWidth.size();
       
  1660           if (distance>oldSize)
       
  1661           {
       
  1662             parentTreeWidth.resize(QMAX(parentTreeWidth.size(),(uint)distance));
       
  1663             int i; for (i=oldSize;i<distance;i++) parentTreeWidth[i]=0;
       
  1664           }
       
  1665           parentTreeWidth[distance-1]+=n->label().length();
       
  1666         }
       
  1667         n->markAsVisible();
       
  1668         maxNodes--;
       
  1669         // add direct parents
       
  1670         if (n->m_parents)
       
  1671         {
       
  1672           QListIterator<DotNode> li(*n->m_parents);
       
  1673           DotNode *dn;
       
  1674           for (li.toFirst();(dn=li.current());++li)
       
  1675           {
       
  1676             parentQueue.append(dn);
       
  1677           }
       
  1678         }
       
  1679       }
       
  1680     }
       
  1681   }
       
  1682   if (Config_getBool("UML_LOOK")) return FALSE; // UML graph are always top to bottom
       
  1683   int maxWidth=0;
       
  1684   int maxHeight=(int)QMAX(childTreeWidth.size(),parentTreeWidth.size());
       
  1685   uint i;
       
  1686   for (i=0;i<childTreeWidth.size();i++)
       
  1687   {
       
  1688     if (childTreeWidth.at(i)>maxWidth) maxWidth=childTreeWidth.at(i);
       
  1689   }
       
  1690   for (i=0;i<parentTreeWidth.size();i++)
       
  1691   {
       
  1692     if (parentTreeWidth.at(i)>maxWidth) maxWidth=parentTreeWidth.at(i);
       
  1693   }
       
  1694   //printf("max tree width=%d, max tree height=%d\n",maxWidth,maxHeight);
       
  1695   return maxWidth>80 && maxHeight<12; // used metric to decide to render the tree
       
  1696                                       // from left to right instead of top to bottom,
       
  1697                                       // with the idea to render very wide trees in
       
  1698                                       // left to right order.
       
  1699 }
       
  1700 
       
  1701 void DotClassGraph::buildGraph(ClassDef *cd,DotNode *n,bool base,int distance)
       
  1702 {
       
  1703   //printf("DocClassGraph::buildGraph(%s,distance=%d,base=%d)\n",
       
  1704   //    cd->name().data(),distance,base);
       
  1705   // ---- Add inheritance relations
       
  1706 
       
  1707   if (m_graphType == DotNode::Inheritance || m_graphType==DotNode::Collaboration)
       
  1708   {
       
  1709     BaseClassList *bcl = base ? cd->baseClasses() : cd->subClasses();
       
  1710     if (bcl)
       
  1711     {
       
  1712       BaseClassListIterator bcli(*bcl);
       
  1713       BaseClassDef *bcd;
       
  1714       for ( ; (bcd=bcli.current()) ; ++bcli )
       
  1715       {
       
  1716         //printf("-------- inheritance relation %s->%s templ=`%s'\n",
       
  1717         //            cd->name().data(),bcd->classDef->name().data(),bcd->templSpecifiers.data());
       
  1718         addClass(bcd->classDef,n,bcd->prot,0,bcd->usedName,
       
  1719             bcd->templSpecifiers,base,distance); 
       
  1720       }
       
  1721     }
       
  1722   }
       
  1723   if (m_graphType == DotNode::Collaboration)
       
  1724   {
       
  1725     // ---- Add usage relations
       
  1726     
       
  1727     UsesClassDict *dict = 
       
  1728       base ? cd->usedImplementationClasses() : 
       
  1729              cd->usedByImplementationClasses()
       
  1730       ;
       
  1731     if (dict)
       
  1732     {
       
  1733       UsesClassDictIterator ucdi(*dict);
       
  1734       UsesClassDef *ucd;
       
  1735       for (;(ucd=ucdi.current());++ucdi)
       
  1736       {
       
  1737         QCString label;
       
  1738         QDictIterator<void> dvi(*ucd->accessors);
       
  1739         const char *s;
       
  1740         bool first=TRUE;
       
  1741         int count=0;
       
  1742         int maxLabels=10;
       
  1743         for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count)
       
  1744         {
       
  1745           if (first) 
       
  1746           {
       
  1747             label=s;
       
  1748             first=FALSE;
       
  1749           }
       
  1750           else
       
  1751           {
       
  1752             label+=QCString("\n")+s;
       
  1753           }
       
  1754         }
       
  1755         if (count==maxLabels) label+="\n...";
       
  1756         //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data());
       
  1757         addClass(ucd->classDef,n,EdgeInfo::Purple,label,0,
       
  1758             ucd->templSpecifiers,base,distance);
       
  1759       }
       
  1760     }
       
  1761   }
       
  1762 
       
  1763   // ---- Add template instantiation relations
       
  1764 
       
  1765   static bool templateRelations = Config_getBool("TEMPLATE_RELATIONS");
       
  1766   if (templateRelations)
       
  1767   {
       
  1768     if (base) // template relations for base classes
       
  1769     {
       
  1770       ClassDef *templMaster=cd->templateMaster();
       
  1771       if (templMaster)
       
  1772       {
       
  1773         QDictIterator<ClassDef> cli(*templMaster->getTemplateInstances());
       
  1774         ClassDef *templInstance;
       
  1775         for (;(templInstance=cli.current());++cli)
       
  1776         {
       
  1777           if (templInstance==cd)
       
  1778           {
       
  1779             addClass(templMaster,n,EdgeInfo::Orange,cli.currentKey(),0,
       
  1780                 0,TRUE,distance);
       
  1781           }
       
  1782         }
       
  1783       }
       
  1784     }
       
  1785     else // template relations for super classes
       
  1786     {
       
  1787       QDict<ClassDef> *templInstances = cd->getTemplateInstances();
       
  1788       if (templInstances)
       
  1789       {
       
  1790         QDictIterator<ClassDef> cli(*templInstances);
       
  1791         ClassDef *templInstance;
       
  1792         for (;(templInstance=cli.current());++cli)
       
  1793         {
       
  1794           addClass(templInstance,n,EdgeInfo::Orange,cli.currentKey(),0,
       
  1795               0,FALSE,distance);
       
  1796         }
       
  1797       }
       
  1798     }
       
  1799   }
       
  1800 }
       
  1801 
       
  1802 DotClassGraph::DotClassGraph(ClassDef *cd,DotNode::GraphType t)
       
  1803 {
       
  1804   //printf("--------------- DotClassGraph::DotClassGraph `%s'\n",cd->displayName().data());
       
  1805   m_graphType = t;
       
  1806   QCString tmp_url="";
       
  1807   if (cd->isLinkable() && !cd->isHidden()) 
       
  1808   {
       
  1809     tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
       
  1810   }
       
  1811   QCString className = cd->displayName();
       
  1812   QCString tooltip = cd->briefDescriptionAsTooltip();
       
  1813   m_startNode = new DotNode(m_curNodeNumber++,
       
  1814                             className,
       
  1815                             tooltip,
       
  1816                             tmp_url.data(),
       
  1817                             TRUE,                      // is a root node
       
  1818                             cd
       
  1819                            );
       
  1820   m_startNode->setDistance(0);
       
  1821   m_usedNodes = new QDict<DotNode>(1009);
       
  1822   m_usedNodes->insert(className,m_startNode);
       
  1823 
       
  1824   //printf("Root node %s\n",cd->name().data());
       
  1825   //if (m_recDepth>0) 
       
  1826   //{
       
  1827     buildGraph(cd,m_startNode,TRUE,1);
       
  1828     if (t==DotNode::Inheritance) buildGraph(cd,m_startNode,FALSE,1);
       
  1829   //}
       
  1830 
       
  1831   static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES");
       
  1832   //int directChildNodes = 1;
       
  1833   //if (m_startNode->m_children!=0) 
       
  1834   //  directChildNodes+=m_startNode->m_children->count();
       
  1835   //if (t==DotNode::Inheritance && m_startNode->m_parents!=0)
       
  1836   //  directChildNodes+=m_startNode->m_parents->count();
       
  1837   //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
       
  1838   //openNodeQueue.append(m_startNode);
       
  1839   m_lrRank = determineVisibleNodes(m_startNode,maxNodes,t==DotNode::Inheritance);
       
  1840   QList<DotNode> openNodeQueue;
       
  1841   openNodeQueue.append(m_startNode);
       
  1842   determineTruncatedNodes(openNodeQueue,t==DotNode::Inheritance);
       
  1843 
       
  1844   m_diskName = cd->getFileBase().copy();
       
  1845 }
       
  1846 
       
  1847 bool DotClassGraph::isTrivial() const
       
  1848 {
       
  1849   if (m_graphType==DotNode::Inheritance)
       
  1850     return m_startNode->m_children==0 && m_startNode->m_parents==0;
       
  1851   else
       
  1852     return m_startNode->m_children==0;
       
  1853 }
       
  1854 
       
  1855 bool DotClassGraph::isTooBig() const
       
  1856 {
       
  1857   static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES");
       
  1858   int numNodes = 0;
       
  1859   numNodes+= m_startNode->m_children ? m_startNode->m_children->count() : 0;
       
  1860   if (m_graphType==DotNode::Inheritance)
       
  1861   {
       
  1862     numNodes+= m_startNode->m_parents ? m_startNode->m_parents->count() : 0;
       
  1863   }
       
  1864   return numNodes>=maxNodes;
       
  1865 }
       
  1866 
       
  1867 DotClassGraph::~DotClassGraph()
       
  1868 {
       
  1869   deleteNodes(m_startNode);
       
  1870   delete m_usedNodes;
       
  1871 }
       
  1872 
       
  1873 /*! Computes a 16 byte md5 checksum for a given dot graph.
       
  1874  *  The md5 checksum is returned as a 32 character ASCII string.
       
  1875  */
       
  1876 QCString computeMd5Signature(DotNode *root,
       
  1877                    DotNode::GraphType gt,
       
  1878                    GraphOutputFormat format,
       
  1879                    bool lrRank,
       
  1880                    bool renderParents,
       
  1881                    bool backArrows,
       
  1882                    QCString &graphStr
       
  1883                   )
       
  1884 {
       
  1885   bool reNumber=TRUE;
       
  1886     
       
  1887   //printf("computeMd5Signature\n");
       
  1888   QString buf;
       
  1889   QTextStream md5stream(&buf,IO_WriteOnly);
       
  1890   md5stream.setEncoding(md5stream.UnicodeUTF8);
       
  1891   writeGraphHeader(md5stream);
       
  1892   if (lrRank)
       
  1893   {
       
  1894     md5stream << "  rankdir=LR;" << endl;
       
  1895   }
       
  1896   root->clearWriteFlag();
       
  1897   root->write(md5stream, 
       
  1898       gt,
       
  1899       format,
       
  1900       gt!=DotNode::CallGraph && gt!=DotNode::Dependency,
       
  1901       TRUE,
       
  1902       backArrows,
       
  1903       reNumber);
       
  1904   if (renderParents && root->m_parents) 
       
  1905   {
       
  1906     QListIterator<DotNode>  dnli(*root->m_parents);
       
  1907     DotNode *pn;
       
  1908     for (dnli.toFirst();(pn=dnli.current());++dnli)
       
  1909     {
       
  1910       if (pn->isVisible()) 
       
  1911       {
       
  1912         root->writeArrow(md5stream,                              // stream
       
  1913             gt,                                                  // graph type
       
  1914             format,                                              // output format
       
  1915             pn,                                                  // child node
       
  1916             pn->m_edgeInfo->at(pn->m_children->findRef(root)),   // edge info
       
  1917             FALSE,                                               // topDown?
       
  1918             backArrows,                                          // point back?
       
  1919             reNumber                                             // renumber nodes
       
  1920             );
       
  1921       }
       
  1922       pn->write(md5stream,      // stream
       
  1923                 gt,             // graph type
       
  1924                 format,         // output format
       
  1925                 TRUE,           // topDown?
       
  1926                 FALSE,          // toChildren?
       
  1927                 backArrows,     // backward pointing arrows?
       
  1928                 reNumber        // renumber nodes?
       
  1929                );
       
  1930     }
       
  1931   }
       
  1932   writeGraphFooter(md5stream);
       
  1933   uchar md5_sig[16];
       
  1934   QCString sigStr(33);
       
  1935   MD5Buffer((const unsigned char *)buf.ascii(),buf.length(),md5_sig);
       
  1936   MD5SigToString(md5_sig,sigStr.data(),33);
       
  1937   if (reNumber)
       
  1938   {
       
  1939     resetReNumbering();
       
  1940   }
       
  1941   graphStr=buf.ascii();
       
  1942   //printf("md5: %s | file: %s\n",sigStr,baseName.data());
       
  1943   return sigStr;
       
  1944 }
       
  1945 
       
  1946 static bool updateDotGraph(DotNode *root,
       
  1947                            DotNode::GraphType gt,
       
  1948                            const QCString &baseName,
       
  1949                            GraphOutputFormat format,
       
  1950                            bool lrRank,
       
  1951                            bool renderParents,
       
  1952                            bool backArrows
       
  1953                           )
       
  1954 {
       
  1955   QCString theGraph;
       
  1956   // TODO: write graph to theGraph, then compute md5 checksum
       
  1957   QCString md5 = computeMd5Signature(
       
  1958                    root,gt,format,lrRank,renderParents,backArrows,theGraph);
       
  1959   if (checkAndUpdateMd5Signature(baseName,md5)) // graph needs to be regenerated
       
  1960   {
       
  1961     QFile f;
       
  1962     f.setName(baseName+".dot");
       
  1963     if (f.open(IO_WriteOnly))
       
  1964     {
       
  1965       QTextStream t(&f);
       
  1966       t.setEncoding(t.UnicodeUTF8);
       
  1967       t << theGraph;
       
  1968     }
       
  1969     return TRUE;
       
  1970   }
       
  1971   return FALSE;
       
  1972 }
       
  1973 
       
  1974 QCString DotClassGraph::diskName() const
       
  1975 {
       
  1976   QCString result=m_diskName.copy();
       
  1977   switch (m_graphType)
       
  1978   {
       
  1979     case DotNode::Collaboration:
       
  1980       result+="_coll_graph";
       
  1981       break;
       
  1982     //case Interface:
       
  1983     //  result+="_intf_graph";
       
  1984     //  break;
       
  1985     case DotNode::Inheritance:
       
  1986       result+="_inherit_graph";
       
  1987       break;
       
  1988     default:
       
  1989       ASSERT(0);
       
  1990       break;
       
  1991   }
       
  1992   return result;
       
  1993 }
       
  1994 
       
  1995 QCString DotClassGraph::writeGraph(QTextStream &out,
       
  1996                                GraphOutputFormat format,
       
  1997                                const char *path,
       
  1998                                const char *relPath,
       
  1999                                bool /*isTBRank*/,
       
  2000                                bool generateImageMap) const
       
  2001 {
       
  2002   QDir d(path);
       
  2003   // store the original directory
       
  2004   if (!d.exists())
       
  2005   {
       
  2006     err("Error: Output dir %s does not exist!\n",path); exit(1);
       
  2007   }
       
  2008   setDotFontPath(d.absPath());
       
  2009 
       
  2010   QCString baseName;
       
  2011   QCString mapName;
       
  2012   switch (m_graphType)
       
  2013   {
       
  2014     case DotNode::Collaboration:
       
  2015       mapName="coll_map";
       
  2016       break;
       
  2017     //case Interface:
       
  2018     //  mapName="intf_map";
       
  2019     //  break;
       
  2020     case DotNode::Inheritance:
       
  2021       mapName="inherit_map";
       
  2022       break;
       
  2023     default:
       
  2024       ASSERT(0);
       
  2025       break;
       
  2026   }
       
  2027   baseName = convertNameToFile(diskName());
       
  2028   QCString absBaseName = QCString(d.absPath().data())+"/"+baseName;
       
  2029 
       
  2030   QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
       
  2031 
       
  2032   if (updateDotGraph(m_startNode,
       
  2033                  m_graphType,
       
  2034                  absBaseName,
       
  2035                  format,
       
  2036                  m_lrRank,
       
  2037                  m_graphType==DotNode::Inheritance,
       
  2038                  TRUE
       
  2039                 )
       
  2040       )
       
  2041   {
       
  2042     if (format==BITMAP) // run dot to create a bitmap image
       
  2043     {
       
  2044       QCString dotArgs(maxCmdLine);
       
  2045       QCString absImgName = absBaseName+"."+imgExt;
       
  2046 
       
  2047       DotRunner dotRun(absBaseName+".dot");
       
  2048       dotRun.addJob(imgExt,absImgName);
       
  2049       if (generateImageMap) dotRun.addJob(MAP_CMD,absBaseName+".map");
       
  2050       if (!dotRun.run())
       
  2051       {
       
  2052         unsetDotFontPath();
       
  2053         return baseName;
       
  2054       }
       
  2055       checkDotResult(absImgName);
       
  2056     }
       
  2057     else if (format==EPS) // run dot to create a .eps image
       
  2058     {
       
  2059       DotRunner dotRun(absBaseName+".dot");
       
  2060       dotRun.addJob("ps",absBaseName+".eps");
       
  2061 
       
  2062       if (Config_getBool("USE_PDFLATEX"))
       
  2063       {
       
  2064         QCString epstopdfArgs(maxCmdLine);
       
  2065         epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
       
  2066             absBaseName.data(),absBaseName.data());
       
  2067         dotRun.addPostProcessing("epstopdf",epstopdfArgs);
       
  2068       }
       
  2069 
       
  2070       if (!dotRun.run())
       
  2071       {
       
  2072         unsetDotFontPath();
       
  2073         return baseName;
       
  2074       }
       
  2075     }
       
  2076     if (Config_getBool("DOT_CLEANUP")) d.remove(baseName+".dot");
       
  2077   }
       
  2078   Doxygen::indexList.addImageFile(baseName+"."+imgExt);
       
  2079 
       
  2080   if (format==BITMAP && generateImageMap) // produce HTML to include the image
       
  2081   {
       
  2082     QCString mapLabel = escapeCharsInString(m_startNode->m_label,FALSE)+"_"+
       
  2083                         escapeCharsInString(mapName,FALSE);
       
  2084     out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." 
       
  2085       << imgExt << "\" border=\"0\" usemap=\"#"
       
  2086       << mapLabel << "\" alt=\"";
       
  2087     switch (m_graphType)
       
  2088     {
       
  2089       case DotNode::Collaboration:
       
  2090         out << "Collaboration graph";
       
  2091         break;
       
  2092       case DotNode::Inheritance:
       
  2093         out << "Inheritance graph";
       
  2094         break;
       
  2095       default:
       
  2096         ASSERT(0);
       
  2097         break;
       
  2098     }
       
  2099     out << "\"/></div>" << endl;
       
  2100     QString tmpstr;
       
  2101     QTextOStream tmpout(&tmpstr);
       
  2102     tmpout.setEncoding(tmpout.UnicodeUTF8);
       
  2103     convertMapFile(tmpout,absBaseName+".map",relPath);
       
  2104     if (!tmpstr.isEmpty())
       
  2105     {
       
  2106       out << "<map name=\"" << mapLabel << "\" id=\"" << mapLabel << "\">" << endl;
       
  2107       out << tmpstr;
       
  2108       out << "</map>" << endl;
       
  2109     }
       
  2110   }
       
  2111   else if (format==EPS) // produce tex to include the .eps image
       
  2112   {
       
  2113       int width=420,height=600;
       
  2114       if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height))
       
  2115       {
       
  2116         err("Error: Could not extract bounding box from .eps!\n");
       
  2117         unsetDotFontPath();
       
  2118         return baseName;
       
  2119       }
       
  2120       //printf("Got EPS size %d,%d\n",width,height);
       
  2121       int maxWidth  = 400;  /* approx. page width in points, excl. margins */
       
  2122       int maxHeight = 400;  /* approx. page height in points, excl. margins */ 
       
  2123       out << "\\nopagebreak\n"
       
  2124         "\\begin{figure}[H]\n"
       
  2125         "\\begin{center}\n"
       
  2126         "\\leavevmode\n";
       
  2127       if (width>maxWidth)
       
  2128       {
       
  2129         out << "\\includegraphics[width=" << maxWidth << "pt]";
       
  2130       }
       
  2131       else if (height>maxHeight)
       
  2132       {
       
  2133         out << "\\includegraphics[height=" << maxHeight << "pt]";
       
  2134       }
       
  2135       else
       
  2136       {
       
  2137         out << "\\includegraphics[width=" << width << "pt]";
       
  2138       }
       
  2139       out << "{" << baseName << "}\n"
       
  2140         "\\end{center}\n"
       
  2141         "\\end{figure}\n";
       
  2142   }
       
  2143   unsetDotFontPath();
       
  2144 
       
  2145   return baseName;
       
  2146 }
       
  2147 
       
  2148 //--------------------------------------------------------------------
       
  2149 
       
  2150 void DotClassGraph::writeXML(QTextStream &t)
       
  2151 {
       
  2152   QDictIterator<DotNode> dni(*m_usedNodes);
       
  2153   DotNode *node;
       
  2154   for (;(node=dni.current());++dni)
       
  2155   {
       
  2156     node->writeXML(t,TRUE);
       
  2157   }
       
  2158 }
       
  2159 
       
  2160 void DotClassGraph::writeXML(XmlStream &t)
       
  2161 {
       
  2162   QDictIterator<DotNode> dni(*m_usedNodes);
       
  2163   DotNode *node;
       
  2164   for (;(node=dni.current());++dni)
       
  2165   {
       
  2166     node->writeXML(t,TRUE);
       
  2167   }
       
  2168 }
       
  2169 
       
  2170 void DotClassGraph::writeXMLDITA(XmlStream &t)
       
  2171 {
       
  2172   QDictIterator<DotNode> dni(*m_usedNodes);
       
  2173   DotNode *node;
       
  2174   for (;(node=dni.current());++dni)
       
  2175   {
       
  2176     node->writeXMLDITA(t,TRUE);
       
  2177   }
       
  2178 }
       
  2179 
       
  2180 void DotClassGraph::writeDEF(QTextStream &t)
       
  2181 {
       
  2182   QDictIterator<DotNode> dni(*m_usedNodes);
       
  2183   DotNode *node;
       
  2184   for (;(node=dni.current());++dni)
       
  2185   {
       
  2186     node->writeDEF(t);
       
  2187   }
       
  2188 }
       
  2189 
       
  2190 //--------------------------------------------------------------------
       
  2191 
       
  2192 int DotInclDepGraph::m_curNodeNumber = 0;
       
  2193 
       
  2194 void DotInclDepGraph::buildGraph(DotNode *n,FileDef *fd,int distance)
       
  2195 {
       
  2196   QList<IncludeInfo> *includeFiles = 
       
  2197      m_inverse ? fd->includedByFileList() : fd->includeFileList();
       
  2198   if (includeFiles)
       
  2199   {
       
  2200     QListIterator<IncludeInfo> ili(*includeFiles);
       
  2201     IncludeInfo *ii;
       
  2202     for (;(ii=ili.current());++ili)
       
  2203     {
       
  2204       FileDef *bfd = ii->fileDef;
       
  2205       QCString in  = ii->includeName;
       
  2206       //printf(">>>> in=`%s' bfd=%p\n",ii->includeName.data(),bfd);
       
  2207       bool doc=TRUE,src=FALSE;
       
  2208       if (bfd)
       
  2209       {
       
  2210         in  = bfd->absFilePath();  
       
  2211         doc = bfd->isLinkable() && !bfd->isHidden();
       
  2212         src = bfd->generateSourceFile();
       
  2213       }
       
  2214       if (doc || src || !Config_getBool("HIDE_UNDOC_RELATIONS"))
       
  2215       {
       
  2216         QCString url="";
       
  2217         if (bfd) url=bfd->getOutputFileBase().copy();
       
  2218         if (!doc && src)
       
  2219         {
       
  2220           url=bfd->getSourceFileBase();
       
  2221         }
       
  2222         DotNode *bn  = m_usedNodes->find(in);
       
  2223         if (bn) // file is already a node in the graph
       
  2224         {
       
  2225           n->addChild(bn,0,0,0);
       
  2226           bn->addParent(n);
       
  2227           bn->setDistance(distance);
       
  2228         }
       
  2229         else
       
  2230         {
       
  2231           QCString tmp_url;
       
  2232           QCString tooltip;
       
  2233           if (bfd) 
       
  2234           {
       
  2235             tmp_url=doc || src ? bfd->getReference()+"$"+url : QCString();
       
  2236             tooltip = bfd->briefDescriptionAsTooltip();
       
  2237           }
       
  2238           bn = new DotNode(
       
  2239               m_curNodeNumber++, // n
       
  2240               ii->includeName,   // label
       
  2241               tooltip,           // tip
       
  2242               tmp_url,           // url
       
  2243               FALSE,             // rootNode
       
  2244               0                  // cd
       
  2245               );
       
  2246           n->addChild(bn,0,0,0);
       
  2247           bn->addParent(n);
       
  2248           m_usedNodes->insert(in,bn);
       
  2249           bn->setDistance(distance);
       
  2250 
       
  2251           if (bfd) buildGraph(bn,bfd,distance+1);
       
  2252         }
       
  2253       }
       
  2254     }
       
  2255   }
       
  2256 }
       
  2257 
       
  2258 void DotInclDepGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes)
       
  2259 {
       
  2260   while (queue.count()>0 && maxNodes>0)
       
  2261   {
       
  2262     static int maxDistance = Config_getInt("MAX_DOT_GRAPH_DEPTH");
       
  2263     DotNode *n = queue.take(0);
       
  2264     if (!n->isVisible() && n->distance()<maxDistance) // not yet processed
       
  2265     {
       
  2266       n->markAsVisible();
       
  2267       maxNodes--;
       
  2268       // add direct children
       
  2269       if (n->m_children)
       
  2270       {
       
  2271         QListIterator<DotNode> li(*n->m_children);
       
  2272         DotNode *dn;
       
  2273         for (li.toFirst();(dn=li.current());++li)
       
  2274         {
       
  2275           queue.append(dn);
       
  2276         }
       
  2277       }
       
  2278     }
       
  2279   }
       
  2280 }
       
  2281 
       
  2282 void DotInclDepGraph::determineTruncatedNodes(QList<DotNode> &queue)
       
  2283 {
       
  2284   while (queue.count()>0)
       
  2285   {
       
  2286     DotNode *n = queue.take(0);
       
  2287     if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
       
  2288     {
       
  2289       bool truncated = FALSE;
       
  2290       if (n->m_children)
       
  2291       {
       
  2292         QListIterator<DotNode> li(*n->m_children);
       
  2293         DotNode *dn;
       
  2294         for (li.toFirst();(dn=li.current());++li)
       
  2295         {
       
  2296           if (!dn->isVisible()) 
       
  2297             truncated = TRUE;
       
  2298           else 
       
  2299             queue.append(dn);
       
  2300         }
       
  2301       }
       
  2302       n->markAsTruncated(truncated);
       
  2303     }
       
  2304   }
       
  2305 }
       
  2306 
       
  2307 
       
  2308 DotInclDepGraph::DotInclDepGraph(FileDef *fd,bool inverse)
       
  2309 {
       
  2310   m_maxDistance = 0;
       
  2311   m_inverse = inverse;
       
  2312   ASSERT(fd!=0);
       
  2313   m_diskName  = fd->getFileBase().copy();
       
  2314   QCString tmp_url=fd->getReference()+"$"+fd->getFileBase();
       
  2315   m_startNode = new DotNode(m_curNodeNumber++,
       
  2316                             fd->docName(),
       
  2317                             "",
       
  2318                             tmp_url.data(),
       
  2319                             TRUE     // root node
       
  2320                            );
       
  2321   m_startNode->setDistance(0);
       
  2322   m_usedNodes = new QDict<DotNode>(1009);
       
  2323   m_usedNodes->insert(fd->absFilePath(),m_startNode);
       
  2324   buildGraph(m_startNode,fd,1);
       
  2325 
       
  2326   static int nodes = Config_getInt("DOT_GRAPH_MAX_NODES");
       
  2327   int maxNodes = nodes;
       
  2328   //int directChildNodes = 1;
       
  2329   //if (m_startNode->m_children!=0) 
       
  2330   //  directChildNodes+=m_startNode->m_children->count();
       
  2331   //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
       
  2332   QList<DotNode> openNodeQueue;
       
  2333   openNodeQueue.append(m_startNode);
       
  2334   determineVisibleNodes(openNodeQueue,maxNodes);
       
  2335   openNodeQueue.clear();
       
  2336   openNodeQueue.append(m_startNode);
       
  2337   determineTruncatedNodes(openNodeQueue);
       
  2338 }
       
  2339 
       
  2340 DotInclDepGraph::~DotInclDepGraph()
       
  2341 {
       
  2342   deleteNodes(m_startNode);
       
  2343   delete m_usedNodes;
       
  2344 }
       
  2345 
       
  2346 QCString DotInclDepGraph::diskName() const
       
  2347 {
       
  2348   QCString result=m_diskName.copy();
       
  2349   if (m_inverse) result+="_dep";
       
  2350   result+="_incl";
       
  2351   return convertNameToFile(result); 
       
  2352 }
       
  2353 
       
  2354 QCString DotInclDepGraph::writeGraph(QTextStream &out,
       
  2355                                  GraphOutputFormat format,
       
  2356                                  const char *path,
       
  2357                                  const char *relPath,
       
  2358                                  bool generateImageMap
       
  2359                                 ) const
       
  2360 {
       
  2361   QDir d(path);
       
  2362   // store the original directory
       
  2363   if (!d.exists())
       
  2364   {
       
  2365     err("Error: Output dir %s does not exist!\n",path); exit(1);
       
  2366   }
       
  2367   setDotFontPath(d.absPath());
       
  2368 
       
  2369   QCString baseName=m_diskName;
       
  2370   if (m_inverse) baseName+="_dep";
       
  2371   baseName+="_incl";
       
  2372   baseName=convertNameToFile(baseName);
       
  2373   QCString mapName=escapeCharsInString(m_startNode->m_label,FALSE);
       
  2374   if (m_inverse) mapName+="dep";
       
  2375   QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
       
  2376 
       
  2377   QCString absBaseName = QCString(d.absPath())+"/"+baseName;
       
  2378   QCString absMapName  = QCString(d.absPath())+"/"+mapName;
       
  2379 
       
  2380   if (updateDotGraph(m_startNode,
       
  2381                  DotNode::Dependency,
       
  2382                  absBaseName,
       
  2383                  format,
       
  2384                  FALSE,        // lrRank
       
  2385                  FALSE,        // renderParents
       
  2386                  m_inverse     // backArrows
       
  2387                 )
       
  2388       )
       
  2389   {
       
  2390     if (format==BITMAP)
       
  2391     {
       
  2392       // run dot to create a bitmap image
       
  2393       QCString dotArgs(maxCmdLine);
       
  2394       QCString absImgName=absBaseName+"."+imgExt;
       
  2395       DotRunner dotRun(absBaseName+".dot");
       
  2396       dotRun.addJob(imgExt,absImgName);
       
  2397       if (generateImageMap) dotRun.addJob(MAP_CMD,absBaseName+".map");
       
  2398       if (!dotRun.run())
       
  2399       {
       
  2400         unsetDotFontPath();
       
  2401         return baseName;
       
  2402       }
       
  2403       checkDotResult(absImgName);
       
  2404     }
       
  2405     else if (format==EPS)
       
  2406     {
       
  2407       // run dot to create a .eps image
       
  2408       DotRunner dotRun(absBaseName+".dot");
       
  2409       dotRun.addJob("ps",absBaseName+".eps");
       
  2410       if (Config_getBool("USE_PDFLATEX"))
       
  2411       {
       
  2412         QCString epstopdfArgs(maxCmdLine);
       
  2413         epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
       
  2414             absBaseName.data(),absBaseName.data());
       
  2415         dotRun.addPostProcessing("epstopdf",epstopdfArgs);
       
  2416       }
       
  2417       if (!dotRun.run())
       
  2418       {
       
  2419         unsetDotFontPath();
       
  2420         return baseName;
       
  2421       }
       
  2422     }
       
  2423   }
       
  2424   Doxygen::indexList.addImageFile(baseName+"."+imgExt);
       
  2425 
       
  2426   if (format==BITMAP && generateImageMap)
       
  2427   {
       
  2428     out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." 
       
  2429         << imgExt << "\" border=\"0\" usemap=\"#"
       
  2430         << mapName << "_map\" alt=\"\"/>";
       
  2431     out << "</div>" << endl;
       
  2432     QString tmpstr;
       
  2433     QTextOStream tmpout(&tmpstr);
       
  2434     tmpout.setEncoding(tmpout.UnicodeUTF8);
       
  2435     convertMapFile(tmpout,absBaseName+".map",relPath);
       
  2436     if (!tmpstr.isEmpty())
       
  2437     {
       
  2438       out << "<map name=\"" << mapName << "_map\" id=\"" << mapName << "\">" << endl;
       
  2439       out << tmpstr;
       
  2440       out << "</map>" << endl;
       
  2441     }
       
  2442   }
       
  2443   else if (format==EPS)
       
  2444   {
       
  2445     int width,height;
       
  2446     if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height))
       
  2447     {
       
  2448       err("Error: Could not extract bounding box from .eps!\n");
       
  2449       unsetDotFontPath();
       
  2450       return baseName;
       
  2451     }
       
  2452     int maxWidth = 420; /* approx. page width in points */
       
  2453 
       
  2454     out << "\\nopagebreak\n"
       
  2455            "\\begin{figure}[H]\n"
       
  2456            "\\begin{center}\n"
       
  2457            "\\leavevmode\n"
       
  2458            "\\includegraphics[width=" << QMIN(width/2,maxWidth) 
       
  2459                                       << "pt]{" << baseName << "}\n"
       
  2460            "\\end{center}\n"
       
  2461            "\\end{figure}\n";
       
  2462   }
       
  2463 
       
  2464   if (Config_getBool("DOT_CLEANUP")) d.remove(baseName+".dot");
       
  2465 
       
  2466   unsetDotFontPath();
       
  2467   return baseName;
       
  2468 }
       
  2469 
       
  2470 bool DotInclDepGraph::isTrivial() const
       
  2471 {
       
  2472   return m_startNode->m_children==0;
       
  2473 }
       
  2474 
       
  2475 bool DotInclDepGraph::isTooBig() const
       
  2476 {
       
  2477   static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES");
       
  2478   int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0;
       
  2479   return numNodes>=maxNodes;
       
  2480 }
       
  2481 
       
  2482 void DotInclDepGraph::writeXML(QTextStream &t)
       
  2483 {
       
  2484   QDictIterator<DotNode> dni(*m_usedNodes);
       
  2485   DotNode *node;
       
  2486   for (;(node=dni.current());++dni)
       
  2487   {
       
  2488     node->writeXML(t,FALSE);
       
  2489   }
       
  2490 }
       
  2491 
       
  2492 void DotInclDepGraph::writeXML(XmlStream &xt)
       
  2493 {
       
  2494   QDictIterator<DotNode> dni(*m_usedNodes);
       
  2495   DotNode *node;
       
  2496   for (;(node=dni.current());++dni)
       
  2497   {
       
  2498     node->writeXML(xt,FALSE);
       
  2499   }
       
  2500 }
       
  2501 
       
  2502 //-------------------------------------------------------------
       
  2503 
       
  2504 int DotCallGraph::m_curNodeNumber = 0;
       
  2505 
       
  2506 void DotCallGraph::buildGraph(DotNode *n,MemberDef *md,int distance)
       
  2507 {
       
  2508   LockingPtr<MemberSDict> refs = m_inverse ? md->getReferencedByMembers() : md->getReferencesMembers();
       
  2509   if (!refs.isNull())
       
  2510   {
       
  2511     MemberSDict::Iterator mri(*refs);
       
  2512     MemberDef *rmd;
       
  2513     for (;(rmd=mri.current());++mri)
       
  2514     {
       
  2515       if (rmd->isFunction() || rmd->isSlot() || rmd->isSignal())
       
  2516       {
       
  2517         QCString uniqueId;
       
  2518         uniqueId=rmd->getReference()+"$"+
       
  2519                  rmd->getOutputFileBase()+"#"+rmd->anchor();
       
  2520         DotNode *bn  = m_usedNodes->find(uniqueId);
       
  2521         if (bn) // file is already a node in the graph
       
  2522         {
       
  2523           n->addChild(bn,0,0,0);
       
  2524           bn->addParent(n);
       
  2525           bn->setDistance(distance);
       
  2526         }
       
  2527         else
       
  2528         {
       
  2529           QCString name;
       
  2530           if (Config_getBool("HIDE_SCOPE_NAMES"))
       
  2531           {
       
  2532             name  = rmd->getOuterScope()==m_scope ? 
       
  2533                     rmd->name() : rmd->qualifiedName();
       
  2534           }
       
  2535           else
       
  2536           {
       
  2537             name = rmd->qualifiedName();
       
  2538           }
       
  2539           QCString tooltip = rmd->briefDescriptionAsTooltip();
       
  2540           bn = new DotNode(
       
  2541               m_curNodeNumber++,
       
  2542               linkToText(name,FALSE),
       
  2543               tooltip,
       
  2544               uniqueId,
       
  2545               0 //distance
       
  2546               );
       
  2547           n->addChild(bn,0,0,0);
       
  2548           bn->addParent(n);
       
  2549           bn->setDistance(distance);
       
  2550           m_usedNodes->insert(uniqueId,bn);
       
  2551 
       
  2552           buildGraph(bn,rmd,distance+1);
       
  2553         }
       
  2554       }
       
  2555     }
       
  2556   }
       
  2557 }
       
  2558 
       
  2559 void DotCallGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes)
       
  2560 {
       
  2561   while (queue.count()>0 && maxNodes>0)
       
  2562   {
       
  2563     static int maxDistance = Config_getInt("MAX_DOT_GRAPH_DEPTH");
       
  2564     DotNode *n = queue.take(0);
       
  2565     if (!n->isVisible() && n->distance()<maxDistance) // not yet processed
       
  2566     {
       
  2567       n->markAsVisible();
       
  2568       maxNodes--;
       
  2569       // add direct children
       
  2570       if (n->m_children)
       
  2571       {
       
  2572         QListIterator<DotNode> li(*n->m_children);
       
  2573         DotNode *dn;
       
  2574         for (li.toFirst();(dn=li.current());++li)
       
  2575         {
       
  2576           queue.append(dn);
       
  2577         }
       
  2578       }
       
  2579     }
       
  2580   }
       
  2581 }
       
  2582 
       
  2583 void DotCallGraph::determineTruncatedNodes(QList<DotNode> &queue)
       
  2584 {
       
  2585   while (queue.count()>0)
       
  2586   {
       
  2587     DotNode *n = queue.take(0);
       
  2588     if (n->isVisible() && n->isTruncated()==DotNode::Unknown)
       
  2589     {
       
  2590       bool truncated = FALSE;
       
  2591       if (n->m_children)
       
  2592       {
       
  2593         QListIterator<DotNode> li(*n->m_children);
       
  2594         DotNode *dn;
       
  2595         for (li.toFirst();(dn=li.current());++li)
       
  2596         {
       
  2597           if (!dn->isVisible()) 
       
  2598             truncated = TRUE;
       
  2599           else 
       
  2600             queue.append(dn);
       
  2601         }
       
  2602       }
       
  2603       n->markAsTruncated(truncated);
       
  2604     }
       
  2605   }
       
  2606 }
       
  2607 
       
  2608 
       
  2609 
       
  2610 DotCallGraph::DotCallGraph(MemberDef *md,bool inverse)
       
  2611 {
       
  2612   m_maxDistance = 0;
       
  2613   m_inverse = inverse;
       
  2614   m_diskName = md->getOutputFileBase()+"_"+md->anchor();
       
  2615   m_scope    = md->getOuterScope();
       
  2616   QCString uniqueId;
       
  2617   uniqueId = md->getReference()+"$"+
       
  2618              md->getOutputFileBase()+"#"+md->anchor();
       
  2619   QCString name;
       
  2620   if (Config_getBool("HIDE_SCOPE_NAMES"))
       
  2621   {
       
  2622     name = md->name();
       
  2623   }
       
  2624   else
       
  2625   {
       
  2626     name = md->qualifiedName();
       
  2627   }
       
  2628   m_startNode = new DotNode(m_curNodeNumber++,
       
  2629                             linkToText(name,FALSE),
       
  2630                             "",
       
  2631                             uniqueId.data(),
       
  2632                             TRUE     // root node
       
  2633                            );
       
  2634   m_startNode->setDistance(0);
       
  2635   m_usedNodes = new QDict<DotNode>(1009);
       
  2636   m_usedNodes->insert(uniqueId,m_startNode);
       
  2637   buildGraph(m_startNode,md,1);
       
  2638 
       
  2639   static int nodes = Config_getInt("DOT_GRAPH_MAX_NODES");
       
  2640   int maxNodes = nodes;
       
  2641   //int directChildNodes = 1;
       
  2642   //if (m_startNode->m_children!=0) 
       
  2643   //  directChildNodes+=m_startNode->m_children->count();
       
  2644   //if (directChildNodes>maxNodes) maxNodes=directChildNodes;
       
  2645   QList<DotNode> openNodeQueue;
       
  2646   openNodeQueue.append(m_startNode);
       
  2647   determineVisibleNodes(openNodeQueue,maxNodes);
       
  2648   openNodeQueue.clear();
       
  2649   openNodeQueue.append(m_startNode);
       
  2650   determineTruncatedNodes(openNodeQueue);
       
  2651 }
       
  2652 
       
  2653 DotCallGraph::~DotCallGraph()
       
  2654 {
       
  2655   deleteNodes(m_startNode);
       
  2656   delete m_usedNodes;
       
  2657 }
       
  2658 
       
  2659 QCString DotCallGraph::writeGraph(QTextStream &out, GraphOutputFormat format,
       
  2660                         const char *path,const char *relPath,bool generateImageMap) const
       
  2661 {
       
  2662   QDir d(path);
       
  2663   // store the original directory
       
  2664   if (!d.exists())
       
  2665   {
       
  2666     err("Error: Output dir %s does not exist!\n",path); exit(1);
       
  2667   }
       
  2668   setDotFontPath(d.absPath());
       
  2669 
       
  2670   QCString baseName = m_diskName + (m_inverse ? "_icgraph" : "_cgraph");
       
  2671   QCString mapName=baseName;
       
  2672   QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
       
  2673 
       
  2674   QCString absBaseName = QCString(d.absPath())+"/"+baseName;
       
  2675 
       
  2676   if (updateDotGraph(m_startNode,
       
  2677                  DotNode::CallGraph,
       
  2678                  absBaseName,
       
  2679                  format,
       
  2680                  TRUE,         // lrRank
       
  2681                  FALSE,        // renderParents
       
  2682                  m_inverse     // backArrows
       
  2683                 )
       
  2684       )
       
  2685   {
       
  2686     if (format==BITMAP)
       
  2687     {
       
  2688       // run dot to create a bitmap image
       
  2689       QCString dotArgs(maxCmdLine);
       
  2690       QCString absImgName=absBaseName+"."+imgExt;
       
  2691       DotRunner dotRun(absBaseName+".dot");
       
  2692       dotRun.addJob(imgExt,absImgName);
       
  2693       if (generateImageMap) dotRun.addJob(MAP_CMD,absBaseName+".map");
       
  2694       if (!dotRun.run())
       
  2695       {
       
  2696         unsetDotFontPath();
       
  2697         return baseName;
       
  2698       }
       
  2699       checkDotResult(absImgName);
       
  2700     }
       
  2701     else if (format==EPS)
       
  2702     {
       
  2703       // run dot to create a .eps image
       
  2704       DotRunner dotRun(absBaseName+".dot");
       
  2705       dotRun.addJob("ps",absBaseName+".eps");
       
  2706       if (Config_getBool("USE_PDFLATEX"))
       
  2707       {
       
  2708         QCString epstopdfArgs(maxCmdLine);
       
  2709         epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
       
  2710             absBaseName.data(),absBaseName.data());
       
  2711         dotRun.addPostProcessing("epstopdf",epstopdfArgs);
       
  2712       }
       
  2713       if (!dotRun.run())
       
  2714       {
       
  2715         unsetDotFontPath();
       
  2716         return baseName;
       
  2717       }
       
  2718     }
       
  2719   }
       
  2720   Doxygen::indexList.addImageFile(baseName+"."+imgExt);
       
  2721 
       
  2722   if (format==BITMAP && generateImageMap)
       
  2723   {
       
  2724     out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." 
       
  2725         << imgExt << "\" border=\"0\" usemap=\"#"
       
  2726         << mapName << "_map\" alt=\"";
       
  2727     out << "\">";
       
  2728     out << "</div>" << endl;
       
  2729     QString tmpstr;
       
  2730     QTextOStream tmpout(&tmpstr);
       
  2731     tmpout.setEncoding(tmpout.UnicodeUTF8);
       
  2732     convertMapFile(tmpout,absBaseName+".map",relPath);
       
  2733     if (!tmpstr.isEmpty())
       
  2734     {
       
  2735       out << "<map name=\"" << mapName << "_map\" id=\"" << mapName << "\">" << endl;
       
  2736       out << tmpstr;
       
  2737       out << "</map>" << endl;
       
  2738     }
       
  2739   }
       
  2740   else if (format==EPS)
       
  2741   {
       
  2742     int width,height;
       
  2743     if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height))
       
  2744     {
       
  2745       err("Error: Could not extract bounding box from .eps!\n");
       
  2746       unsetDotFontPath();
       
  2747       return baseName;
       
  2748     }
       
  2749     int maxWidth = 420; /* approx. page width in points */
       
  2750 
       
  2751     out << "\\nopagebreak\n"
       
  2752            "\\begin{figure}[H]\n"
       
  2753            "\\begin{center}\n"
       
  2754            "\\leavevmode\n"
       
  2755            "\\includegraphics[width=" << QMIN(width/2,maxWidth) 
       
  2756                                       << "pt]{" << baseName << "}\n"
       
  2757            "\\end{center}\n"
       
  2758            "\\end{figure}\n";
       
  2759   }
       
  2760 
       
  2761   if (Config_getBool("DOT_CLEANUP")) d.remove(baseName+".dot");
       
  2762 
       
  2763   unsetDotFontPath();
       
  2764   return baseName;
       
  2765 }
       
  2766 
       
  2767 bool DotCallGraph::isTrivial() const
       
  2768 {
       
  2769   return m_startNode->m_children==0;
       
  2770 }
       
  2771 
       
  2772 bool DotCallGraph::isTooBig() const
       
  2773 {
       
  2774   static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES");
       
  2775   int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0;
       
  2776   return numNodes>=maxNodes;
       
  2777 }
       
  2778 
       
  2779 //-------------------------------------------------------------
       
  2780 
       
  2781 DotDirDeps::DotDirDeps(DirDef *dir) : m_dir(dir)
       
  2782 {
       
  2783 }
       
  2784 
       
  2785 DotDirDeps::~DotDirDeps()
       
  2786 {
       
  2787 }
       
  2788 
       
  2789 QCString DotDirDeps::writeGraph(QTextStream &out,
       
  2790                             GraphOutputFormat format,
       
  2791                             const char *path,
       
  2792                             const char *relPath,
       
  2793                             bool generateImageMap) const
       
  2794 {
       
  2795   QDir d(path);
       
  2796   // store the original directory
       
  2797   if (!d.exists())
       
  2798   {
       
  2799     err("Error: Output dir %s does not exist!\n",path); exit(1);
       
  2800   }
       
  2801   setDotFontPath(d.absPath());
       
  2802 
       
  2803   QCString baseName=m_dir->getOutputFileBase()+"_dep";
       
  2804   QCString mapName=escapeCharsInString(baseName,FALSE);
       
  2805   QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
       
  2806 
       
  2807   QCString absBaseName = QCString(d.absPath())+"/"+baseName;
       
  2808 
       
  2809   // TODO: create check, update md5 checksum
       
  2810   {
       
  2811     QFile f(absBaseName+".dot");
       
  2812     if (!f.open(IO_WriteOnly))
       
  2813     {
       
  2814       err("Cannot create file %s.dot for writing!\n",baseName.data());
       
  2815     }
       
  2816     QTextStream t(&f);
       
  2817     t.setEncoding(t.UnicodeUTF8);
       
  2818     m_dir->writeDepGraph(t);
       
  2819     f.close();
       
  2820 
       
  2821     if (format==BITMAP)
       
  2822     {
       
  2823       // run dot to create a bitmap image
       
  2824       QCString dotArgs(maxCmdLine);
       
  2825       QCString absImgName=absBaseName+"."+imgExt;
       
  2826       DotRunner dotRun(absBaseName+".dot");
       
  2827       dotRun.addJob(imgExt,absImgName);
       
  2828       if (generateImageMap) dotRun.addJob(MAP_CMD,absBaseName+".map");
       
  2829       if (!dotRun.run())
       
  2830       {
       
  2831         unsetDotFontPath();
       
  2832         return baseName;
       
  2833       }
       
  2834       checkDotResult(absImgName);
       
  2835     }
       
  2836     else if (format==EPS)
       
  2837     {
       
  2838       // run dot to create a .eps image
       
  2839       DotRunner dotRun(absBaseName+".dot");
       
  2840       dotRun.addJob("ps",absBaseName+".eps");
       
  2841       if (Config_getBool("USE_PDFLATEX"))
       
  2842       {
       
  2843         QCString epstopdfArgs(maxCmdLine);
       
  2844         epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
       
  2845             absBaseName.data(),absBaseName.data());
       
  2846         dotRun.addPostProcessing("epstopdf",epstopdfArgs);
       
  2847       }
       
  2848       if (!dotRun.run())
       
  2849       {
       
  2850         unsetDotFontPath();
       
  2851         return baseName;
       
  2852       }
       
  2853     }
       
  2854   }
       
  2855   Doxygen::indexList.addImageFile(baseName+"."+imgExt);
       
  2856 
       
  2857   if (format==BITMAP && generateImageMap)
       
  2858   {
       
  2859     out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." 
       
  2860         << imgExt << "\" border=\"0\" usemap=\"#"
       
  2861         << mapName << "_map\" alt=\"";
       
  2862     out << convertToXML(m_dir->displayName());
       
  2863     out << "\"/>";
       
  2864     out << "</div>" << endl;
       
  2865     QString tmpstr;
       
  2866     QTextOStream tmpout(&tmpstr);
       
  2867     tmpout.setEncoding(tmpout.UnicodeUTF8);
       
  2868     convertMapFile(tmpout,absBaseName+".map",relPath,TRUE);
       
  2869     if (!tmpstr.isEmpty())
       
  2870     {
       
  2871       out << "<map name=\"" << mapName << "_map\" id=\"" << mapName << "\">" << endl;
       
  2872       out << tmpstr;
       
  2873       out << "</map>" << endl;
       
  2874     }
       
  2875     else
       
  2876     {
       
  2877       //printf("Map is empty!\n");
       
  2878     }
       
  2879     //thisDir.remove(baseName+".map");
       
  2880   }
       
  2881   else if (format==EPS)
       
  2882   {
       
  2883     int width,height;
       
  2884     if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height))
       
  2885     {
       
  2886       err("Error: Could not extract bounding box from .eps!\n");
       
  2887       unsetDotFontPath();
       
  2888       return baseName;
       
  2889     }
       
  2890     int maxWidth = 420; /* approx. page width in points */
       
  2891 
       
  2892     out << "\\nopagebreak\n"
       
  2893            "\\begin{figure}[H]\n"
       
  2894            "\\begin{center}\n"
       
  2895            "\\leavevmode\n"
       
  2896            "\\includegraphics[width=" << QMIN(width/2,maxWidth) 
       
  2897                                       << "pt]{" << baseName << "}\n"
       
  2898            "\\end{center}\n"
       
  2899            "\\end{figure}\n";
       
  2900   }
       
  2901 
       
  2902   if (Config_getBool("DOT_CLEANUP")) d.remove(baseName+".dot");
       
  2903 
       
  2904   unsetDotFontPath();
       
  2905   return baseName;
       
  2906 }
       
  2907 
       
  2908 bool DotDirDeps::isTrivial() const
       
  2909 {
       
  2910   return m_dir->depGraphIsTrivial();
       
  2911 }
       
  2912 
       
  2913 //-------------------------------------------------------------
       
  2914 
       
  2915 void generateGraphLegend(const char *path)
       
  2916 {
       
  2917   QFile dotFile((QCString)path+"/graph_legend.dot");
       
  2918   if (!dotFile.open(IO_WriteOnly))
       
  2919   {
       
  2920     err("Could not open file %s for writing\n",
       
  2921         convertToQCString(dotFile.name()).data());
       
  2922     return;
       
  2923   }
       
  2924   QTextStream dotText(&dotFile); 
       
  2925   writeGraphHeader(dotText);
       
  2926   dotText << "  Node9 [shape=\"box\",label=\"Inherited\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",fillcolor=\"grey75\",style=\"filled\" fontcolor=\"black\"];\n";
       
  2927   dotText << "  Node10 -> Node9 [dir=back,color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
       
  2928   dotText << "  Node10 [shape=\"box\",label=\"PublicBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPublicBase" << Doxygen::htmlFileExtension << "\"];\n";
       
  2929   dotText << "  Node11 -> Node10 [dir=back,color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
       
  2930   dotText << "  Node11 [shape=\"box\",label=\"Truncated\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"red\",URL=\"$classTruncated" << Doxygen::htmlFileExtension << "\"];\n";
       
  2931   dotText << "  Node13 -> Node9 [dir=back,color=\"darkgreen\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
       
  2932   dotText << "  Node13 [shape=\"box\",label=\"ProtectedBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classProtectedBase" << Doxygen::htmlFileExtension << "\"];\n";
       
  2933   dotText << "  Node14 -> Node9 [dir=back,color=\"firebrick4\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
       
  2934   dotText << "  Node14 [shape=\"box\",label=\"PrivateBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPrivateBase" << Doxygen::htmlFileExtension << "\"];\n";
       
  2935   dotText << "  Node15 -> Node9 [dir=back,color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
       
  2936   dotText << "  Node15 [shape=\"box\",label=\"Undocumented\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"grey75\"];\n";
       
  2937   dotText << "  Node16 -> Node9 [dir=back,color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n";
       
  2938   dotText << "  Node16 [shape=\"box\",label=\"Templ< int >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
       
  2939   dotText << "  Node17 -> Node16 [dir=back,color=\"orange\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"< int >\",fontname=\"" << FONTNAME << "\"];\n";
       
  2940   dotText << "  Node17 [shape=\"box\",label=\"Templ< T >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n";
       
  2941   dotText << "  Node18 -> Node9 [dir=back,color=\"darkorchid3\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"m_usedClass\",fontname=\"" << FONTNAME << "\"];\n";
       
  2942   dotText << "  Node18 [shape=\"box\",label=\"Used\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classUsed" << Doxygen::htmlFileExtension << "\"];\n";
       
  2943   writeGraphFooter(dotText);
       
  2944   dotFile.close();
       
  2945 
       
  2946   QDir d(path);
       
  2947   // store the original directory
       
  2948   if (!d.exists())
       
  2949   {
       
  2950     err("Error: Output dir %s does not exist!\n",path); exit(1);
       
  2951   }
       
  2952   setDotFontPath(d.absPath());
       
  2953 
       
  2954   // run dot to generate the a bitmap image from the graph
       
  2955   QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
       
  2956   QCString imgName = "graph_legend."+imgExt;
       
  2957   QCString absImgName = QCString(d.absPath())+"/"+ imgName;
       
  2958 
       
  2959   DotRunner dotRun(d.absPath()+"/graph_legend.dot");
       
  2960   dotRun.addJob(imgExt,absImgName);
       
  2961   if (!dotRun.run())
       
  2962   {
       
  2963     unsetDotFontPath();
       
  2964     return;
       
  2965   }
       
  2966   checkDotResult(absImgName);
       
  2967   Doxygen::indexList.addImageFile(imgName);
       
  2968   unsetDotFontPath();
       
  2969 }
       
  2970 
       
  2971 void writeDotGraphFromFile(const char *inFile,const char *outDir,
       
  2972                            const char *outFile,GraphOutputFormat format)
       
  2973 {
       
  2974   QDir d(outDir);
       
  2975   if (!d.exists())
       
  2976   {
       
  2977     err("Error: Output dir %s does not exist!\n",outDir); exit(1);
       
  2978   }
       
  2979   setDotFontPath(0);
       
  2980 
       
  2981   QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
       
  2982   QCString imgName = (QCString)outFile+"."+imgExt;
       
  2983   QCString absImgName = QCString(d.absPath())+"/"+imgName;
       
  2984   QCString absOutFile = QCString(d.absPath())+"/"+outFile;
       
  2985 
       
  2986   DotRunner dotRun(inFile);
       
  2987   if (format==BITMAP)
       
  2988     dotRun.addJob(imgExt,absImgName);
       
  2989   else // format==EPS
       
  2990     dotRun.addJob("ps",absOutFile+".eps");
       
  2991 
       
  2992   if ( (format==EPS) && (Config_getBool("USE_PDFLATEX")) )
       
  2993   {
       
  2994     QCString epstopdfArgs(maxCmdLine);
       
  2995     epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
       
  2996                          absOutFile.data(),absOutFile.data());
       
  2997     dotRun.addPostProcessing("epstopdf",epstopdfArgs);
       
  2998   }
       
  2999 
       
  3000   if (!dotRun.run())
       
  3001   {
       
  3002      unsetDotFontPath();
       
  3003      return;
       
  3004   }
       
  3005 
       
  3006   if (format==BITMAP) checkDotResult(absImgName);
       
  3007   Doxygen::indexList.addImageFile(imgName);
       
  3008 
       
  3009   unsetDotFontPath();
       
  3010 }
       
  3011 
       
  3012  
       
  3013 /*! Marco Dalla Gasperina [marcodg@attbi.com] added this to allow
       
  3014  *  dotfiles to generate image maps.
       
  3015  *  \param inFile just the basename part of the filename
       
  3016  *  \param outDir output directory
       
  3017  *  \param relPath relative path the to root of the output dir
       
  3018  *  \param context the scope in which this graph is found (for resolving links)
       
  3019  *  \returns a string which is the HTML image map (without the \<map\>\</map\>)
       
  3020  */
       
  3021 QString getDotImageMapFromFile(const QString& inFile, const QString& outDir,
       
  3022     const QCString &relPath,const QString &context)
       
  3023 {
       
  3024   QString outFile = inFile + ".map";
       
  3025 
       
  3026   QDir d(outDir);
       
  3027   if (!d.exists())
       
  3028   {
       
  3029     err("Error: Output dir %s does not exist!\n",outDir.data()); exit(1);
       
  3030   }
       
  3031   setDotFontPath(d.absPath());
       
  3032 
       
  3033   QCString absOutFile = QCString(d.absPath())+"/"+outFile.data();
       
  3034 
       
  3035   DotRunner dotRun(inFile);
       
  3036   dotRun.addJob(MAP_CMD,absOutFile);
       
  3037   if (!dotRun.run())
       
  3038   {
       
  3039     unsetDotFontPath();
       
  3040     return "";
       
  3041   }
       
  3042 
       
  3043   QString result;
       
  3044   QTextOStream tmpout(&result);
       
  3045   tmpout.setEncoding(tmpout.UnicodeUTF8);
       
  3046   convertMapFile(tmpout, absOutFile, relPath ,TRUE, context);
       
  3047   d.remove(outFile);
       
  3048 
       
  3049   unsetDotFontPath();
       
  3050   return result;
       
  3051 }
       
  3052 // end MDG mods
       
  3053 
       
  3054 //-------------------------------------------------------------
       
  3055 
       
  3056 DotGroupCollaboration::DotGroupCollaboration(GroupDef* gd)
       
  3057 {
       
  3058     m_curNodeId = 0;
       
  3059     QCString tmp_url = gd->getReference()+"$"+gd->getOutputFileBase();
       
  3060     m_usedNodes = new QDict<DotNode>(1009);
       
  3061     m_rootNode = new DotNode(m_curNodeId++, gd->groupTitle(), "", tmp_url, TRUE );
       
  3062     m_rootNode->markAsVisible();
       
  3063     m_usedNodes->insert(gd->name(), m_rootNode );
       
  3064     m_edges.setAutoDelete(TRUE);
       
  3065 
       
  3066     m_diskName = gd->getOutputFileBase();
       
  3067 
       
  3068     buildGraph( gd );
       
  3069 }
       
  3070 
       
  3071 DotGroupCollaboration::~DotGroupCollaboration()
       
  3072 {
       
  3073   delete m_usedNodes;
       
  3074 }
       
  3075 
       
  3076 void DotGroupCollaboration::buildGraph(GroupDef* gd)
       
  3077 {
       
  3078   QCString tmp_url;
       
  3079   //===========================
       
  3080   // hierarchy.
       
  3081 
       
  3082   // Write parents
       
  3083   LockingPtr<GroupList> groups = gd->partOfGroups();
       
  3084   if ( groups!=0 )
       
  3085   {
       
  3086     GroupListIterator gli(*groups);
       
  3087     GroupDef *d;
       
  3088     for (gli.toFirst();(d=gli.current());++gli)
       
  3089     {
       
  3090       DotNode* nnode = m_usedNodes->find(d->name());
       
  3091       if ( !nnode )
       
  3092       { // add node
       
  3093         tmp_url = d->getReference()+"$"+d->getOutputFileBase();
       
  3094         QCString tooltip = d->briefDescriptionAsTooltip();
       
  3095         nnode = new DotNode(m_curNodeId++, d->groupTitle(), tooltip, tmp_url );
       
  3096         nnode->markAsVisible();
       
  3097         m_usedNodes->insert(d->name(), nnode );
       
  3098       }
       
  3099       tmp_url = "";
       
  3100       addEdge( nnode, m_rootNode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
       
  3101     }
       
  3102   }
       
  3103 
       
  3104   // Add subgroups
       
  3105   if ( gd->getSubGroups() && gd->getSubGroups()->count() )
       
  3106   {
       
  3107     QListIterator<GroupDef> defli(*gd->getSubGroups());
       
  3108     GroupDef *def;
       
  3109     for (;(def=defli.current());++defli)
       
  3110     {
       
  3111       DotNode* nnode = m_usedNodes->find(def->name());
       
  3112       if ( !nnode )
       
  3113       { // add node
       
  3114         tmp_url = def->getReference()+"$"+def->getOutputFileBase();
       
  3115         QCString tooltip = def->briefDescriptionAsTooltip();
       
  3116         nnode = new DotNode(m_curNodeId++, def->groupTitle(), tooltip, tmp_url );
       
  3117         nnode->markAsVisible();
       
  3118         m_usedNodes->insert(def->name(), nnode );
       
  3119       }
       
  3120       tmp_url = "";
       
  3121       addEdge( m_rootNode, nnode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url );
       
  3122     }
       
  3123   }
       
  3124 
       
  3125   //=======================
       
  3126   // Write collaboration
       
  3127 
       
  3128   // Add members
       
  3129   addMemberList( gd->getMemberList(MemberList::allMembersList) );
       
  3130 
       
  3131   // Add classes
       
  3132   if ( gd->getClasses() && gd->getClasses()->count() )
       
  3133   {
       
  3134     ClassSDict::Iterator defli(*gd->getClasses());
       
  3135     ClassDef *def;
       
  3136     for (;(def=defli.current());++defli)
       
  3137     {
       
  3138       tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
       
  3139       addCollaborationMember( def, tmp_url, DotGroupCollaboration::tclass );          
       
  3140     }
       
  3141   }
       
  3142 
       
  3143   // Add namespaces
       
  3144   if ( gd->getNamespaces() && gd->getNamespaces()->count() )
       
  3145   {
       
  3146     NamespaceSDict::Iterator defli(*gd->getNamespaces());
       
  3147     NamespaceDef *def;
       
  3148     for (;(def=defli.current());++defli)
       
  3149     {
       
  3150       tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
       
  3151       addCollaborationMember( def, tmp_url, DotGroupCollaboration::tnamespace );          
       
  3152     }
       
  3153   }
       
  3154 
       
  3155   // Add files
       
  3156   if ( gd->getFiles() && gd->getFiles()->count() )
       
  3157   {
       
  3158     QListIterator<FileDef> defli(*gd->getFiles());
       
  3159     FileDef *def;
       
  3160     for (;(def=defli.current());++defli)
       
  3161     {
       
  3162       tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
       
  3163       addCollaborationMember( def, tmp_url, DotGroupCollaboration::tfile );          
       
  3164     }
       
  3165   }
       
  3166 
       
  3167   // Add pages
       
  3168   if ( gd->getPages() && gd->getPages()->count() )
       
  3169   {
       
  3170     PageSDict::Iterator defli(*gd->getPages());
       
  3171     PageDef *def;
       
  3172     for (;(def=defli.current());++defli)
       
  3173     {
       
  3174       tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
       
  3175       addCollaborationMember( def, tmp_url, DotGroupCollaboration::tpages );          
       
  3176     }
       
  3177   }
       
  3178 
       
  3179   // Add directories
       
  3180   if ( gd->getDirs() && gd->getDirs()->count() )
       
  3181   {
       
  3182     QListIterator<DirDef> defli(*gd->getDirs());
       
  3183     DirDef *def;
       
  3184     for (;(def=defli.current());++defli)
       
  3185     {
       
  3186       tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension;
       
  3187       addCollaborationMember( def, tmp_url, DotGroupCollaboration::tdir );          
       
  3188     }
       
  3189   }
       
  3190 }
       
  3191 
       
  3192 void DotGroupCollaboration::addMemberList( MemberList* ml )
       
  3193 {
       
  3194   if ( !( ml && ml->count()) ) return;
       
  3195   MemberListIterator defli(*ml);
       
  3196   MemberDef *def;
       
  3197   for (;(def=defli.current());++defli)
       
  3198   {
       
  3199     QCString tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension
       
  3200       +"#"+def->anchor();
       
  3201     addCollaborationMember( def, tmp_url, DotGroupCollaboration::tmember );
       
  3202   }
       
  3203 }
       
  3204 
       
  3205 DotGroupCollaboration::Edge* DotGroupCollaboration::addEdge( 
       
  3206     DotNode* _pNStart, DotNode* _pNEnd, EdgeType _eType,
       
  3207     const QCString& _label, const QCString& _url )
       
  3208 {
       
  3209   // search a existing link.
       
  3210   QListIterator<Edge> lli(m_edges);
       
  3211   Edge* newEdge = 0;
       
  3212   for ( lli.toFirst(); (newEdge=lli.current()); ++lli)
       
  3213   {
       
  3214     if ( newEdge->pNStart==_pNStart && 
       
  3215          newEdge->pNEnd==_pNEnd &&
       
  3216          newEdge->eType==_eType 
       
  3217        )
       
  3218     { // edge already found
       
  3219       break;
       
  3220     }
       
  3221   }
       
  3222   if ( newEdge==0 ) // new link
       
  3223   {
       
  3224     newEdge = new Edge(_pNStart,_pNEnd,_eType);
       
  3225     m_edges.append( newEdge );
       
  3226   } 
       
  3227 
       
  3228   if (!_label.isEmpty())
       
  3229   {
       
  3230     newEdge->links.append(new Link(_label,_url));
       
  3231   }
       
  3232 
       
  3233   return newEdge;
       
  3234 }
       
  3235 
       
  3236 void DotGroupCollaboration::addCollaborationMember( 
       
  3237     Definition* def, QCString& url, EdgeType eType )
       
  3238 {
       
  3239   // Create group nodes
       
  3240   if ( !def->partOfGroups() )
       
  3241     return;
       
  3242   GroupListIterator gli(*def->partOfGroups());
       
  3243   GroupDef *d;
       
  3244   QCString tmp_str;
       
  3245   for (;(d=gli.current());++gli)
       
  3246   {
       
  3247     DotNode* nnode = m_usedNodes->find(d->name());
       
  3248     if ( nnode != m_rootNode )
       
  3249     {
       
  3250       if ( nnode==0 )
       
  3251       { // add node
       
  3252         tmp_str = d->getReference()+"$"+d->getOutputFileBase();
       
  3253         QCString tooltip = d->briefDescriptionAsTooltip();
       
  3254         nnode = new DotNode(m_curNodeId++, d->groupTitle(), tooltip, tmp_str );
       
  3255         nnode->markAsVisible();
       
  3256         m_usedNodes->insert(d->name(), nnode );
       
  3257       }
       
  3258       tmp_str = def->qualifiedName();
       
  3259       addEdge( m_rootNode, nnode, eType, tmp_str, url );
       
  3260     }
       
  3261   }
       
  3262 }
       
  3263 
       
  3264 
       
  3265 QCString DotGroupCollaboration::writeGraph( QTextStream &t, GraphOutputFormat format,
       
  3266     const char *path, const char *relPath,
       
  3267     bool writeImageMap) const
       
  3268 {
       
  3269   QDir d(path);
       
  3270   // store the original directory
       
  3271   if (!d.exists())
       
  3272   {
       
  3273     err("Error: Output dir %s does not exist!\n",path); exit(1);
       
  3274   }
       
  3275   setDotFontPath(d.absPath());
       
  3276 
       
  3277   QCString baseName = m_diskName;
       
  3278   QCString absBaseName = QCString(d.absPath())+"/"+baseName;
       
  3279 
       
  3280   QFile dotfile(absBaseName+".dot");
       
  3281   if (dotfile.open(IO_WriteOnly))
       
  3282   {
       
  3283     QTextStream tdot(&dotfile);
       
  3284     tdot.setEncoding(tdot.UnicodeUTF8);
       
  3285     writeGraphHeader(tdot);
       
  3286 
       
  3287     // clean write flags
       
  3288     QDictIterator<DotNode> dni(*m_usedNodes);
       
  3289     DotNode *pn;
       
  3290     for (dni.toFirst();(pn=dni.current());++dni)
       
  3291       pn->clearWriteFlag();
       
  3292 
       
  3293     // write other nodes.
       
  3294     for (dni.toFirst();(pn=dni.current());++dni)
       
  3295     {
       
  3296       pn->write(tdot,DotNode::Inheritance,format,TRUE,FALSE,FALSE,FALSE);
       
  3297     }
       
  3298 
       
  3299     // write edges
       
  3300     QListIterator<Edge> eli(m_edges);
       
  3301     Edge* edge;
       
  3302     for (eli.toFirst();(edge=eli.current());++eli)
       
  3303     {
       
  3304       edge->write( tdot );
       
  3305     }
       
  3306 
       
  3307     writeGraphFooter(tdot);
       
  3308     dotfile.close();
       
  3309   }
       
  3310 
       
  3311   QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT");
       
  3312   if (format==BITMAP) // run dot to create a bitmap image
       
  3313   {
       
  3314     QCString dotArgs(maxCmdLine);
       
  3315     QCString imgName = baseName+"."+imgExt;
       
  3316     QCString mapName=baseName+".map";
       
  3317 
       
  3318     QCString absImgName = QCString(d.absPath())+"/"+imgName;
       
  3319     QCString absMapName = QCString(d.absPath())+"/"+mapName;
       
  3320 
       
  3321     DotRunner dotRun(absBaseName+".dot");
       
  3322     dotRun.addJob(imgExt,absImgName);
       
  3323     if (writeImageMap) dotRun.addJob(MAP_CMD,absMapName);
       
  3324     if (!dotRun.run())
       
  3325     {
       
  3326       unsetDotFontPath();
       
  3327       return baseName;
       
  3328     }
       
  3329 
       
  3330     if (writeImageMap)
       
  3331     {
       
  3332       QCString mapLabel = escapeCharsInString(baseName,FALSE);
       
  3333       t << "<center><table><tr><td><img src=\"" << relPath << imgName
       
  3334         << "\" border=\"0\" alt=\"\" usemap=\"#" 
       
  3335         << mapLabel << "_map\"/>" << endl;
       
  3336       t << "<map name=\"" << mapLabel << "_map\" id=\"" << mapLabel << "\">" << endl;
       
  3337       convertMapFile(t,absMapName,relPath);
       
  3338       t << "</map></td></tr></table></center>" << endl;
       
  3339     }
       
  3340   }
       
  3341   else if (format==EPS)
       
  3342   {
       
  3343     DotRunner dotRun(absBaseName+".dot");
       
  3344     dotRun.addJob("ps",absBaseName+".eps");
       
  3345     if (Config_getBool("USE_PDFLATEX"))
       
  3346     {
       
  3347       QCString epstopdfArgs(maxCmdLine);
       
  3348       epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
       
  3349           absBaseName.data(),absBaseName.data());
       
  3350       dotRun.addPostProcessing("epstopdf",epstopdfArgs);
       
  3351     }
       
  3352 
       
  3353     if (!dotRun.run())
       
  3354     {
       
  3355       unsetDotFontPath();
       
  3356       return baseName;
       
  3357     }
       
  3358 
       
  3359     int width,height;
       
  3360     if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height))
       
  3361     {
       
  3362       err("Error: Could not extract bounding box from .eps!\n");
       
  3363       unsetDotFontPath();
       
  3364       return baseName;
       
  3365     }
       
  3366     int maxWidth = 420; /* approx. page width in points */
       
  3367     t << "\\nopagebreak\n"
       
  3368          "\\begin{figure}[H]\n"
       
  3369          "\\begin{center}\n"
       
  3370          "\\leavevmode\n"
       
  3371          "\\includegraphics[width=" << QMIN(width/2,maxWidth) 
       
  3372           << "pt]{" << baseName << "}\n"
       
  3373          "\\end{center}\n"
       
  3374          "\\end{figure}\n";
       
  3375   }
       
  3376   if (Config_getBool("DOT_CLEANUP"))
       
  3377   {
       
  3378     d.remove(baseName+".dot");
       
  3379   }
       
  3380 
       
  3381   unsetDotFontPath();
       
  3382   return baseName;
       
  3383 }
       
  3384 
       
  3385 void DotGroupCollaboration::Edge::write( QTextStream &t ) const
       
  3386 {
       
  3387   const char* linkTypeColor[] = {
       
  3388     "darkorchid3"
       
  3389       ,"orange"
       
  3390       ,"blueviolet"
       
  3391       ,"darkgreen"   
       
  3392       ,"firebrick4"  
       
  3393       ,"grey75"
       
  3394       ,"midnightblue"
       
  3395   };
       
  3396   QCString arrowStyle = "dir=\"none\", style=\"dashed\"";
       
  3397   t << "  Node" << pNStart->number();
       
  3398   t << "->";
       
  3399   t << "Node" << pNEnd->number();
       
  3400 
       
  3401   t << " [shape=plaintext";
       
  3402   if (links.count()>0) // there are links
       
  3403   {
       
  3404     t << ", ";
       
  3405     // HTML-like edge labels crash on my Mac with Graphviz 2.0! and
       
  3406     // are not supported by older version of dot.
       
  3407     //
       
  3408     //t << label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">";
       
  3409     //QListIterator<Link> lli(links);
       
  3410     //Link *link;
       
  3411     //for( lli.toFirst(); (link=lli.current()); ++lli)
       
  3412     //{
       
  3413     //  t << "<TR><TD";
       
  3414     //  if ( !link->url.isEmpty() )
       
  3415     //    t << " HREF=\"" << link->url << "\"";
       
  3416     //  t << ">" << link->label << "</TD></TR>";
       
  3417     //}
       
  3418     //t << "</TABLE>>";
       
  3419 
       
  3420     t << "label=\"";
       
  3421     QListIterator<Link> lli(links);
       
  3422     Link *link;
       
  3423     bool first=TRUE;
       
  3424     int count=0;
       
  3425     const int maxLabels = 10;
       
  3426     for( lli.toFirst(); (link=lli.current()) && count<maxLabels; ++lli,++count)
       
  3427     {
       
  3428       if (first) first=FALSE; else t << "\\n"; 
       
  3429       t << convertLabel(link->label);
       
  3430     }
       
  3431     if (count==maxLabels) t << "\\n...";
       
  3432     t << "\"";
       
  3433 
       
  3434   }
       
  3435   switch( eType )
       
  3436   {
       
  3437     case thierarchy :
       
  3438       arrowStyle = "dir=\"back\", style=\"solid\"";
       
  3439     default :
       
  3440       t << ", color=\"" << linkTypeColor[(int)eType] << "\"";
       
  3441       break;
       
  3442   }
       
  3443   t << ", " << arrowStyle;
       
  3444   t << "];" << endl;
       
  3445 }
       
  3446 
       
  3447 bool DotGroupCollaboration::isTrivial() const
       
  3448 {
       
  3449   return m_usedNodes->count() <= 1;
       
  3450 }
       
  3451 
       
  3452 void DotGroupCollaboration::writeGraphHeader(QTextStream &t) const
       
  3453 {
       
  3454   t << "digraph structs" << endl;
       
  3455   t << "{" << endl;
       
  3456   if (Config_getBool("DOT_TRANSPARENT"))
       
  3457   {
       
  3458     t << "  bgcolor=\"transparent\";" << endl;
       
  3459   }
       
  3460   t << "  edge [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\","
       
  3461     "labelfontname=\"" << FONTNAME << "\",labelfontsize=\"" << FONTSIZE << "\"];\n";
       
  3462   t << "  node [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\",shape=record];\n";
       
  3463   t << "  rankdir=LR;\n";
       
  3464 }
       
  3465 
       
  3466 void writeDotDirDepGraph(QTextStream &t,DirDef *dd)
       
  3467 {
       
  3468     t << "digraph G {\n";
       
  3469     if (Config_getBool("DOT_TRANSPARENT"))
       
  3470     {
       
  3471       t << "  bgcolor=transparent;\n";
       
  3472     }
       
  3473     t << "  compound=true\n";
       
  3474     t << "  node [ fontsize=\"" << FONTSIZE << "\", fontname=\"" << FONTNAME << "\"];\n";
       
  3475     t << "  edge [ labelfontsize=\"" << FONTSIZE << "\", labelfontname=\"" << FONTNAME << "\"];\n";
       
  3476 
       
  3477     QDict<DirDef> dirsInGraph(257);
       
  3478     
       
  3479     dirsInGraph.insert(dd->getOutputFileBase(),dd);
       
  3480     if (dd->parent())
       
  3481     {
       
  3482       t << "  subgraph cluster" << dd->parent()->getOutputFileBase() << " {\n";
       
  3483       t << "    graph [ bgcolor=\"#ddddee\", pencolor=\"black\", label=\"" 
       
  3484         << dd->parent()->shortName() 
       
  3485         << "\" fontname=\"" << FONTNAME << "\", fontsize=\"" << FONTSIZE << "\", URL=\"";
       
  3486       t << dd->parent()->getOutputFileBase() << Doxygen::htmlFileExtension;
       
  3487       t << "\"]\n";
       
  3488     }
       
  3489     if (dd->isCluster())
       
  3490     {
       
  3491       t << "  subgraph cluster" << dd->getOutputFileBase() << " {\n";
       
  3492       t << "    graph [ bgcolor=\"#eeeeff\", pencolor=\"black\", label=\"\""
       
  3493         << " URL=\"" << dd->getOutputFileBase() << Doxygen::htmlFileExtension 
       
  3494         << "\"];\n";
       
  3495       t << "    " << dd->getOutputFileBase() << " [shape=plaintext label=\"" 
       
  3496         << dd->shortName() << "\"];\n";
       
  3497 
       
  3498       // add nodes for sub directories
       
  3499       QListIterator<DirDef> sdi(dd->subDirs());
       
  3500       DirDef *sdir;
       
  3501       for (sdi.toFirst();(sdir=sdi.current());++sdi)
       
  3502       {
       
  3503         t << "    " << sdir->getOutputFileBase() << " [shape=box label=\"" 
       
  3504           << sdir->shortName() << "\"";
       
  3505         if (sdir->isCluster())
       
  3506         {
       
  3507           t << " color=\"red\"";
       
  3508         }
       
  3509         else
       
  3510         {
       
  3511           t << " color=\"black\"";
       
  3512         }
       
  3513         t << " fillcolor=\"white\" style=\"filled\"";
       
  3514         t << " URL=\"" << sdir->getOutputFileBase() 
       
  3515           << Doxygen::htmlFileExtension << "\"";
       
  3516         t << "];\n";
       
  3517         dirsInGraph.insert(sdir->getOutputFileBase(),sdir);
       
  3518       }
       
  3519       t << "  }\n";
       
  3520     }
       
  3521     else
       
  3522     {
       
  3523       t << "  " << dd->getOutputFileBase() << " [shape=box, label=\"" 
       
  3524         << dd->shortName() << "\", style=\"filled\", fillcolor=\"#eeeeff\","
       
  3525         << " pencolor=\"black\", URL=\"" << dd->getOutputFileBase() 
       
  3526         << Doxygen::htmlFileExtension << "\"];\n";
       
  3527     }
       
  3528     if (dd->parent())
       
  3529     {
       
  3530       t << "  }\n";
       
  3531     }
       
  3532 
       
  3533     // add nodes for other used directories
       
  3534     QDictIterator<UsedDir> udi(*dd->usedDirs());
       
  3535     UsedDir *udir;
       
  3536     //printf("*** For dir %s\n",shortName().data());
       
  3537     for (udi.toFirst();(udir=udi.current());++udi) 
       
  3538       // for each used dir (=directly used or a parent of a directly used dir)
       
  3539     {
       
  3540       const DirDef *usedDir=udir->dir();
       
  3541       DirDef *dir=dd;
       
  3542       while (dir)
       
  3543       {
       
  3544         //printf("*** check relation %s->%s same_parent=%d !%s->isParentOf(%s)=%d\n",
       
  3545         //    dir->shortName().data(),usedDir->shortName().data(),
       
  3546         //    dir->parent()==usedDir->parent(),
       
  3547         //    usedDir->shortName().data(),
       
  3548         //    shortName().data(),
       
  3549         //    !usedDir->isParentOf(this)
       
  3550         //    );
       
  3551         if (dir!=usedDir && dir->parent()==usedDir->parent() && 
       
  3552             !usedDir->isParentOf(dd))
       
  3553           // include if both have the same parent (or no parent)
       
  3554         {
       
  3555           t << "  " << usedDir->getOutputFileBase() << " [shape=box label=\"" 
       
  3556             << usedDir->shortName() << "\"";
       
  3557           if (usedDir->isCluster())
       
  3558           {
       
  3559             if (!Config_getBool("DOT_TRANSPARENT"))
       
  3560             {
       
  3561               t << " fillcolor=\"white\" style=\"filled\"";
       
  3562             }
       
  3563             t << " color=\"red\"";
       
  3564           }
       
  3565           t << " URL=\"" << usedDir->getOutputFileBase() 
       
  3566             << Doxygen::htmlFileExtension << "\"];\n";
       
  3567           dirsInGraph.insert(usedDir->getOutputFileBase(),usedDir);
       
  3568           break;
       
  3569         }
       
  3570         dir=dir->parent();
       
  3571       }
       
  3572     }
       
  3573 
       
  3574     // add relations between all selected directories
       
  3575     DirDef *dir;
       
  3576     QDictIterator<DirDef> di(dirsInGraph);
       
  3577     for (di.toFirst();(dir=di.current());++di) // foreach dir in the graph
       
  3578     {
       
  3579       QDictIterator<UsedDir> udi(*dir->usedDirs());
       
  3580       UsedDir *udir;
       
  3581       for (udi.toFirst();(udir=udi.current());++udi) // foreach used dir
       
  3582       {
       
  3583         const DirDef *usedDir=udir->dir();
       
  3584         if ((dir!=dd || !udir->inherited()) &&     // only show direct dependendies for this dir
       
  3585             (usedDir!=dd || !udir->inherited()) && // only show direct dependendies for this dir
       
  3586             !usedDir->isParentOf(dir) &&             // don't point to own parent
       
  3587             dirsInGraph.find(usedDir->getOutputFileBase())) // only point to nodes that are in the graph
       
  3588         {
       
  3589           QCString relationName;
       
  3590           relationName.sprintf("dir_%06d_%06d",dir->dirCount(),usedDir->dirCount());
       
  3591           if (Doxygen::dirRelations.find(relationName)==0)
       
  3592           {
       
  3593             // new relation
       
  3594             Doxygen::dirRelations.append(relationName,
       
  3595                 new DirRelation(relationName,dir,udir));
       
  3596           }
       
  3597           int nrefs = udir->filePairs().count();
       
  3598           t << "  " << dir->getOutputFileBase() << "->" 
       
  3599                     << usedDir->getOutputFileBase();
       
  3600           t << " [headlabel=\"" << nrefs << "\", labeldistance=1.5";
       
  3601           t << " headhref=\"" << relationName << Doxygen::htmlFileExtension 
       
  3602             << "\"];\n";
       
  3603         }
       
  3604       }
       
  3605     }
       
  3606 
       
  3607     t << "}\n";
       
  3608 }