diagram.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  *
00003  * $Id: diagram.cpp,v 1.30 2001/03/19 19:27:40 root Exp $
00004  *
00005  *
00006  * Copyright (C) 1997-2008 by Dimitri van Heesch.
00007  *
00008  * Permission to use, copy, modify, and distribute this software and its
00009  * documentation under the terms of the GNU General Public License is hereby 
00010  * granted. No representations are made about the suitability of this software 
00011  * for any purpose. It is provided "as is" without express or implied warranty.
00012  * See the GNU General Public License for more details.
00013  *
00014  * Documents produced by Doxygen are derivative works derived from the
00015  * input used in their production; they are not affected by this license.
00016  *
00017  */
00018 
00019 #include "qtbc.h"
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <qlist.h>
00023 #include <qarray.h>
00024 #include <qtextstream.h>
00025 #include <qfile.h>
00026 
00027 #include "diagram.h"
00028 #include "image.h"
00029 #include "classdef.h"
00030 #include "config.h"
00031 #include "message.h"
00032 #include "util.h"
00033 //#include "latexgen.h"
00034 //#include "htmlgen.h"
00035 #include "doxygen.h"
00036 #include "portable.h"
00037 
00038 //-----------------------------------------------------------------------------
00039 
00040 const uint maxTreeWidth = 8;
00041 const int gridWidth  = 100;
00042 const int gridHeight = 100;
00043 
00044 const uint labelHorSpacing  = 10;  // horizontal distance between labels
00045 const uint labelVertSpacing = 32;  // vertical distance between labels
00046 const uint labelHorMargin   = 6;   // horiz. spacing between label and box
00047 const uint fontHeight       = 12;  // height of a character
00048 
00049 //static QCString escapeLatex(const char *s)
00050 //{
00051 //  QCString result;
00052 //  char c;
00053 //  while ((c=*s++))
00054 //  {
00055 //    if (c=='_') result+="\\_";
00056 //           else result+=c;
00057 //  }
00058 //  return result;
00059 //}
00060 
00061 static uint protToMask(Protection p)
00062 {
00063   switch(p)
00064   {
00065     case Public:    return 0xffffffff;
00066     case Package: // package is not possible!
00067     case Protected: return 0xcccccccc;
00068     case Private:   return 0xaaaaaaaa;
00069   }
00070   return 0;
00071 }
00072 
00073 static uint protToColor(Protection p)
00074 {
00075   switch(p)
00076   {
00077     case Public:    return 6;
00078     case Package: // package is not possible!
00079     case Protected: return 5;
00080     case Private:   return 4;
00081   }
00082   return 0;
00083 }
00084 
00085 static QCString protToString(Protection p)
00086 {
00087   switch(p)
00088   {
00089     case Public:    return "solid";
00090     case Package: // package is not possible!
00091     case Protected: return "dashed";
00092     case Private:   return "dotted";
00093   }
00094   return 0;
00095 }
00096 
00097 static uint virtToMask(Specifier p)
00098 {
00099   switch(p)
00100   {
00101     case Normal:    return 0xffffffff;
00102     case Virtual:   return 0xf0f0f0f0;
00103     default:        return 0;
00104   }
00105   return 0;
00106 }
00107 
00108 // pre: dil is not empty
00109 static Protection getMinProtectionLevel(DiagramItemList *dil)
00110 {
00111   DiagramItem *di=dil->first();
00112   Protection result=di->protection();
00113   di=dil->next();
00114   while (di)
00115   {
00116     Protection p=di->protection();
00117     if (p!=result) 
00118     {
00119       if (result==Protected && p==Public) result=p;
00120       else if (result==Private) result=p;
00121     } 
00122     di=dil->next();
00123   }
00124   return result;
00125 }
00126 
00127 static void writeBitmapBox(DiagramItem *di,Image *image,
00128                            int x,int y,int w,int h,bool firstRow,
00129                            bool hasDocs,bool children=FALSE)
00130 {
00131   int colFill = hasDocs ? (firstRow ? 0 : 2) : 7;
00132   int colBorder = (firstRow || !hasDocs) ? 1 : 3;
00133   int l = Image::stringLength(di->label());
00134   uint mask=virtToMask(di->virtualness());
00135   image->fillRect(x+1,y+1,w-2,h-2,colFill,mask);
00136   image->drawRect(x,y,w,h,colBorder,mask);
00137   image->writeString(x+(w-l)/2, y+(h-fontHeight)/2, di->label(),1);
00138   if (children)
00139   {
00140     int i;
00141     for (i=0;i<5;i++)
00142       image->drawHorzLine(y+h+i-6,x+w-2-i,x+w-2,firstRow?1:3,0xffffffff);
00143   }
00144 }
00145 
00146 static void writeVectorBox(QTextStream &t,DiagramItem *di,
00147                            float x,float y,bool children=FALSE)
00148 {
00149   if (di->virtualness()==Virtual) t << "dashed\n";
00150   t << " (" << di->label() << ") " << x << " " << y << " box\n";
00151   if (children) t << x << " " << y << " mark\n";
00152   if (di->virtualness()==Virtual) t << "solid\n";
00153 }
00154 
00155 static void writeMapArea(QTextStream &t,ClassDef *cd,QCString relPath,
00156                          int x,int y,int w,int h)
00157 {
00158   if (cd->isLinkable())
00159   {
00160     QCString *dest;
00161     QCString ref=cd->getReference();
00162     t << "<area ";
00163     if (!ref.isEmpty()) 
00164     {
00165       t << "doxygen=\"" << ref << ":";
00166       if ((dest=Doxygen::tagDestinationDict[ref])) t << *dest << "/";
00167       t << "\" ";
00168     }
00169     t << "href=\"";
00170     if (!ref.isEmpty())
00171     {
00172       if ((dest=Doxygen::tagDestinationDict[ref])) t << *dest << "/";
00173     }
00174     else
00175     {
00176       t << relPath;
00177     }
00178     t << cd->getOutputFileBase() << Doxygen::htmlFileExtension << "\" ";
00179     t << "alt=\"" << cd->displayName(); 
00180     t << "\" shape=\"rect\" coords=\"" << x << "," << y << ",";
00181     t << (x+w) << "," << (y+h) << "\">" << endl;
00182   }
00183 }
00184 //-----------------------------------------------------------------------------
00185 
00186 DiagramItem::DiagramItem(DiagramItem *p,int number,ClassDef *cd,
00187                          Protection pr,Specifier vi,const char *ts) 
00188 { 
00189   parent=p; 
00190   x=y=0; 
00191   //name=n;
00192   num=number;
00193   children = new DiagramItemList;
00194   prot=pr;
00195   virt=vi;
00196   inList=FALSE;
00197   classDef=cd;
00198   templSpec=ts;
00199 }
00200  
00201 DiagramItem::~DiagramItem() 
00202 { 
00203   delete children;
00204 }
00205 
00206 QCString DiagramItem::label() const
00207 {
00208   QCString result;
00209   if (!templSpec.isEmpty())
00210   {
00211     result=insertTemplateSpecifierInScope(classDef->displayName(),templSpec);
00212   }
00213   else
00214   {
00215     result=classDef->displayName();
00216   }
00217   if (Config_getBool("HIDE_SCOPE_NAMES")) result=stripScope(result);
00218   return result;
00219 }
00220 
00221 QCString DiagramItem::fileName() const
00222 {
00223   return classDef->getOutputFileBase();
00224 }
00225 
00226 int DiagramItem::avgChildPos() const
00227 {
00228   DiagramItem *di;
00229   int c=children->count();
00230   if (c==0) // no children -> don't move
00231     return xPos();
00232   if ((di=children->getFirst())->isInList()) // children should be in a list
00233     return di->xPos();
00234   if (c&1) // odd number of children -> get pos of middle child
00235     return children->at(c/2)->xPos();
00236   else // even number of children -> get middle of most middle children
00237     return (children->at(c/2-1)->xPos()+children->at(c/2)->xPos())/2;
00238 }
00239 
00240 int DiagramItem::numChildren() const
00241 {
00242   return children->count();
00243 }
00244 
00245 void DiagramItem::addChild(DiagramItem *di)
00246 {
00247   children->append(di);
00248 }
00249 
00250 void DiagramRow::insertClass(DiagramItem *parent,ClassDef *cd,bool doBases,
00251                              Protection prot,Specifier virt,const char *ts)
00252 {
00253   //if (cd->visited) return; // the visit check does not work in case of
00254                              // multiple inheritance of the same class!
00255   DiagramItem *di=new DiagramItem(parent, diagram->at(level)->count(), 
00256                                   cd,prot,virt,ts);
00257   //cd->visited=TRUE;
00258   if (parent) parent->addChild(di);
00259   di->move(count()*gridWidth,level*gridHeight);
00260   append(di);
00261   BaseClassList *bcl=doBases ? cd->baseClasses() : cd->subClasses();
00262   int count=0;
00263   if (bcl)
00264   {
00265     /* there are base/sub classes */
00266     BaseClassDef *bcd=bcl->first();
00267     while (bcd)
00268     {
00269       ClassDef *ccd=bcd->classDef;
00270       if (ccd && ccd->isVisibleInHierarchy() /*&& !ccd->visited*/) count++;
00271       bcd=bcl->next();
00272     }
00273   }
00274   if (count>0 && (prot!=Private || !doBases))
00275   {
00276     DiagramRow *row=0;
00277     if (diagram->count()<=level+1) /* add new row */
00278     {
00279       row = new DiagramRow(diagram,level+1);
00280       diagram->append(row);
00281     }
00282     else /* get next row */
00283     {
00284       row=diagram->at(level+1);
00285     }
00286     /* insert base classes in the next row */
00287     BaseClassDef *bcd=bcl->first();
00288     while (bcd)
00289     {
00290       ClassDef *ccd=bcd->classDef;
00291       if (ccd && ccd->isVisibleInHierarchy() /*&& !ccd->visited*/)
00292       {
00293         row->insertClass(di,ccd,doBases,bcd->prot,
00294             doBases?bcd->virt:Normal,
00295             doBases?bcd->templSpecifiers.data():"");
00296       }
00297       bcd=bcl->next();
00298     }
00299   }
00300 }
00301 
00302 TreeDiagram::TreeDiagram(ClassDef *root,bool doBases)
00303 {
00304   setAutoDelete(TRUE); 
00305   DiagramRow *row=new DiagramRow(this,0);
00306   append(row);
00307   row->insertClass(0,root,doBases,Public,Normal,0);
00308 }
00309 
00310 TreeDiagram::~TreeDiagram()
00311 {
00312 }
00313 
00314 
00315 void TreeDiagram::moveChildren(DiagramItem *root,int dx)
00316 {
00317   DiagramItemList *dil=root->getChildren();
00318   DiagramItem *di=dil->first();
00319   while (di)
00320   {
00321     di->move(dx,0);
00322     moveChildren(di,dx);
00323     di=dil->next();
00324   }
00325 }
00326 
00327 bool TreeDiagram::layoutTree(DiagramItem *root,int r)
00328 {
00329   bool moved=FALSE;
00330   //printf("layoutTree(%s,%d)\n",root->label().data(),r);
00331 
00332   DiagramItemList *dil=root->getChildren(); 
00333   if (dil->count()>0)
00334   {
00335     uint k;
00336     int pPos=root->xPos();
00337     int cPos=root->avgChildPos();
00338     if (pPos>cPos) // move children
00339     {
00340       DiagramRow *row=at(r+1);
00341       //printf("Moving children %d-%d in row %d\n",
00342       //    dil->getFirst()->number(),row->count()-1,r+1);
00343       for (k=dil->getFirst()->number();k<row->count();k++)
00344         row->at(k)->move(pPos-cPos,0);
00345       moved=TRUE;
00346     }
00347     else if (pPos<cPos) // move parent
00348     {
00349       DiagramRow *row=at(r);
00350       //printf("Moving parents %d-%d in row %d\n",
00351       //    root->number(),row->count()-1,r);
00352       for (k=root->number();k<row->count();k++)
00353         row->at(k)->move(cPos-pPos,0);
00354       moved=TRUE;
00355     }
00356 
00357     // recurse to children
00358     DiagramItem *di=dil->first();
00359     while (di && !moved && !di->isInList())
00360     {
00361       moved = moved || layoutTree(di,r+1);
00362       di=dil->next();
00363     }
00364   }
00365   return moved;
00366 }
00367 
00368 void TreeDiagram::computeLayout()
00369 {
00370   DiagramRow *row=first();
00371   while (row && row->count()<maxTreeWidth) row=next();
00372   if (row)
00373   {
00374     //printf("computeLayout() list row at %d\n",row->number());
00375     DiagramItem *di=row->first();
00376     DiagramItem *opi=0;
00377     int delta=0;
00378     bool first=TRUE;
00379     while (di)
00380     {
00381       DiagramItem *pi=di->parentItem();
00382       if (pi==opi && !first) { delta-=gridWidth; }
00383       first = pi!=opi;
00384       opi=pi;
00385       di->move(delta,0); // collapse all items in the same 
00386                          // list (except the first)
00387       di->putInList();
00388       di=row->next();
00389     }
00390   }
00391 
00392   // re-organize the diagram items
00393   DiagramItem *root=getFirst()->getFirst();
00394   while (layoutTree(root,0));
00395   
00396   // move first items of the lists
00397   if (row)
00398   {
00399     DiagramItem *di=row->first();
00400     while (di)
00401     {
00402       DiagramItem *pi=di->parentItem();
00403       if (pi->getChildren()->count()>1)
00404       {
00405         di->move(gridWidth,0);
00406         while (di && di->parentItem()==pi) di=row->next();
00407       }
00408       else
00409       {
00410         di=row->next();
00411       }
00412     }
00413   }
00414 }
00415 
00416 uint TreeDiagram::computeRows()
00417 {
00418   //printf("TreeDiagram::computeRows()=%d\n",count());
00419   int count=0;
00420   DiagramRow *row=first();
00421   while (row && !row->getFirst()->isInList())
00422   {
00423     count++;
00424     row=next();
00425   }
00426   //printf("count=%d row=%p\n",count,row);
00427   if (row)
00428   {
00429     int maxListLen=0;
00430     int curListLen=0;
00431     DiagramItem *di=row->first(),*opi=0;
00432     while (di)
00433     {
00434       if (di->parentItem()!=opi) curListLen=1; else curListLen++; 
00435       if (curListLen>maxListLen) maxListLen=curListLen;
00436       opi=di->parentItem();
00437       di=row->next();
00438     }
00439     //printf("maxListLen=%d\n",maxListLen);
00440     count+=maxListLen;
00441   }
00442   return count;
00443 }
00444 
00445 #if 0
00446 uint TreeDiagram::computeCols()
00447 {
00448   uint count=0;
00449   DiagramRow *row=first();
00450   while (row && !row->getFirst()->isInList())
00451   {
00452     if (row->count()>count) count=row->count();
00453     row=next();
00454   }
00455   if (row)
00456   {
00457     row=prev();
00458     uint cols=row->count();
00459     if (row->getLast()->getChildren()->count()>1) cols++;
00460     if (cols>count) count=cols;
00461   }
00462   return count;
00463 };
00464 #endif
00465 
00466 void TreeDiagram::computeExtremes(uint *maxLabelLen,uint *maxXPos)
00467 {
00468   uint ml=0,mx=0;
00469   DiagramRow *dr=first();
00470   bool done=FALSE;
00471   while (dr && !done)
00472   {
00473     DiagramItem *di=dr->first();
00474     while (di)
00475     {
00476       if (di->isInList()) done=TRUE;
00477       if (maxXPos) mx=QMAX(mx,(uint)di->xPos());
00478       if (maxLabelLen) ml=QMAX(ml,Image::stringLength(di->label()));
00479       di=dr->next();
00480     }
00481     dr=next();
00482   }
00483   if (maxLabelLen) *maxLabelLen=ml;
00484   if (maxXPos)     *maxXPos=mx;
00485 }
00486 
00487 void TreeDiagram::drawBoxes(QTextStream &t,Image *image, 
00488                             bool doBase,bool bitmap,
00489                             uint baseRows,uint superRows,
00490                             uint cellWidth,uint cellHeight,
00491                             QCString relPath,
00492                             bool generateMap)
00493 {
00494   DiagramRow *dr=first();
00495   if (!doBase) dr=next();
00496   bool done=FALSE;
00497   bool firstRow = doBase;
00498   while (dr && !done)
00499   {
00500     int x=0,y=0;
00501     float xf=0.0,yf=0.0;
00502     DiagramItem *di=dr->first();
00503     if (di->isInList()) // put boxes in a list
00504     {
00505       DiagramItem *opi=0;
00506       if (doBase) di=dr->last();
00507       while (di) 
00508       {
00509         if (di->parentItem()==opi)
00510         {
00511           if (bitmap)
00512           {
00513             if (doBase) y -= cellHeight+labelVertSpacing;
00514             else        y += cellHeight+labelVertSpacing;
00515           }
00516           else
00517           {
00518             if (doBase) yf += 1.0;
00519             else        yf -= 1.0;
00520           }
00521         }
00522         else
00523         {
00524           if (bitmap)
00525           {
00526             x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth;
00527             if (doBase)
00528             {
00529               y = image->getHeight()-
00530                 superRows*cellHeight-
00531                 (superRows-1)*labelVertSpacing-
00532                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00533             }
00534             else
00535             {
00536               y = (baseRows-1)*(cellHeight+labelVertSpacing)+
00537                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00538             }
00539           }
00540           else
00541           {
00542             xf = di->xPos()/(float)gridWidth;
00543             if (doBase)
00544             {
00545               yf = di->yPos()/(float)gridHeight+superRows-1;
00546             }
00547             else
00548             {
00549               yf = superRows-1-di->yPos()/(float)gridHeight;
00550             }
00551           }
00552         }
00553         opi=di->parentItem();
00554         
00555         if (bitmap)
00556         {
00557           bool hasDocs=di->getClassDef()->isLinkable();
00558           writeBitmapBox(di,image,x,y,cellWidth,cellHeight,firstRow,
00559               hasDocs,di->getChildren()->count()>0); 
00560           if (!firstRow && generateMap) 
00561             writeMapArea(t,di->getClassDef(),relPath,x,y,cellWidth,cellHeight);
00562         }
00563         else
00564         {
00565           writeVectorBox(t,di,xf,yf,di->getChildren()->count()>0);
00566         }
00567         
00568         if (doBase) di=dr->prev(); else di=dr->next();
00569       }
00570       done=TRUE;
00571     }
00572     else // draw a tree of boxes
00573     {
00574       while (di)
00575       {
00576         if (bitmap)
00577         {
00578           x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth;
00579           if (doBase)
00580           {
00581             y = image->getHeight()-
00582               superRows*cellHeight-
00583               (superRows-1)*labelVertSpacing-
00584               di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00585           }
00586           else
00587           {
00588             y = (baseRows-1)*(cellHeight+labelVertSpacing)+
00589               di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00590           }
00591           bool hasDocs=di->getClassDef()->isLinkable();
00592           writeBitmapBox(di,image,x,y,cellWidth,cellHeight,firstRow,hasDocs); 
00593           if (!firstRow && generateMap) 
00594             writeMapArea(t,di->getClassDef(),relPath,x,y,cellWidth,cellHeight);
00595         }
00596         else
00597         {
00598           xf=di->xPos()/(float)gridWidth;
00599           if (doBase)
00600           {
00601             yf = di->yPos()/(float)gridHeight+superRows-1;
00602           }
00603           else
00604           {
00605             yf = superRows-1-di->yPos()/(float)gridHeight;
00606           }
00607           writeVectorBox(t,di,xf,yf);
00608         }
00609 
00610         di=dr->next();
00611       }
00612     }
00613     dr=next();
00614     firstRow=FALSE;
00615   }
00616 }
00617 
00618 void TreeDiagram::drawConnectors(QTextStream &t,Image *image,
00619                                  bool doBase,bool bitmap,
00620                                  uint baseRows,uint superRows,
00621                                  uint cellWidth,uint cellHeight)
00622 {
00623   DiagramRow *dr=first();
00624   bool done=FALSE;
00625   while (dr && !done) // for each row
00626   {
00627     DiagramItem *di=dr->first();
00628     if (di->isInList()) // row consists of list connectors
00629     {
00630       int x=0,y=0,ys=0;
00631       float xf=0.0,yf=0.0,ysf=0.0;
00632       while (di)
00633       {
00634         DiagramItem *pi=di->parentItem();
00635         DiagramItemList *dil=pi->getChildren();
00636         DiagramItem *last=dil->getLast();
00637         if (di==last) // single child
00638         {
00639           if (bitmap) // draw pixels
00640           {
00641             x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2;
00642             if (doBase) // base classes
00643             {
00644               y = image->getHeight()-
00645                 (superRows-1)*(cellHeight+labelVertSpacing)-
00646                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00647               image->drawVertArrow(x,y,y+labelVertSpacing/2,
00648                                    protToColor(di->protection()),
00649                                    protToMask(di->protection()));
00650             }
00651             else // super classes
00652             {
00653               y = (baseRows-1)*(cellHeight+labelVertSpacing)-
00654                 labelVertSpacing/2+
00655                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00656               image->drawVertLine(x,y,y+labelVertSpacing/2,
00657                                   protToColor(di->protection()),
00658                                   protToMask(di->protection()));
00659             }
00660           }
00661           else // draw vectors
00662           {
00663             t << protToString(di->protection()) << endl;
00664             if (doBase)
00665             {
00666               t << "1 " << (di->xPos()/(float)gridWidth) << " " 
00667                 << (di->yPos()/(float)gridHeight+superRows-1) << " in\n";
00668             }
00669             else
00670             {
00671               t << "0 " << (di->xPos()/(float)gridWidth) << " " 
00672                 << ((float)superRows-0.25-di->yPos()/(float)gridHeight)
00673                 << " in\n";
00674             }
00675           }
00676         }
00677         else // multiple children, put them in a vertical list
00678         {
00679           if (bitmap)
00680           {
00681             x = di->parentItem()->xPos()*
00682               (cellWidth+labelHorSpacing)/gridWidth+cellWidth/2;
00683             if (doBase) // base classes
00684             {
00685               ys = image->getHeight()-
00686                 (superRows-1)*(cellHeight+labelVertSpacing)-
00687                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00688               y = ys - cellHeight/2;
00689             }
00690             else // super classes
00691             {
00692               ys = (baseRows-1)*(cellHeight+labelVertSpacing)+
00693                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00694               y = ys + cellHeight/2;
00695             }
00696           }
00697           else
00698           {
00699             xf = di->parentItem()->xPos()/(float)gridWidth;
00700             if (doBase)
00701             {
00702               ysf = di->yPos()/(float)gridHeight+superRows-1;
00703               yf = ysf + 0.5;
00704             }
00705             else
00706             {
00707               ysf = (float)superRows-0.25-di->yPos()/(float)gridHeight;
00708               yf = ysf - 0.25;
00709             }
00710           }
00711           while (di!=last) // more children to add
00712           {
00713             if (bitmap)
00714             {
00715               if (doBase) // base classes
00716               {
00717                 image->drawHorzArrow(y,x,x+cellWidth/2+labelHorSpacing,
00718                     protToColor(di->protection()),
00719                     protToMask(di->protection()));
00720                 y -= cellHeight+labelVertSpacing;
00721               }
00722               else // super classes
00723               {
00724                 image->drawHorzLine(y,x,x+cellWidth/2+labelHorSpacing,
00725                     protToColor(di->protection()),
00726                     protToMask(di->protection()));
00727                 y += cellHeight+labelVertSpacing;
00728               }
00729             }
00730             else
00731             {
00732               t << protToString(di->protection()) << endl;
00733               if (doBase)
00734               {
00735                 t << "1 " << xf << " " << yf << " hedge\n";
00736                 yf += 1.0;
00737               }
00738               else
00739               {
00740                 t << "0 " << xf << " " << yf << " hedge\n";
00741                 yf -= 1.0;
00742               }
00743             }
00744             di=dr->next();
00745           }
00746           // add last horizonal line and a vertical connection line
00747           if (bitmap)
00748           {
00749             if (doBase) // base classes
00750             {
00751               image->drawHorzArrow(y,x,x+cellWidth/2+labelHorSpacing,
00752                   protToColor(di->protection()),
00753                   protToMask(di->protection()));
00754               image->drawVertLine(x,y,ys+labelVertSpacing/2,
00755                   protToColor(getMinProtectionLevel(dil)),
00756                   protToMask(getMinProtectionLevel(dil)));
00757             }
00758             else // super classes
00759             {
00760               image->drawHorzLine(y,x,x+cellWidth/2+labelHorSpacing,
00761                   protToColor(di->protection()),
00762                   protToMask(di->protection()));
00763               image->drawVertLine(x,ys-labelVertSpacing/2,y,
00764                   protToColor(getMinProtectionLevel(dil)),
00765                   protToMask(getMinProtectionLevel(dil)));
00766             }
00767           }
00768           else
00769           {
00770             t << protToString(di->protection()) << endl;
00771             if (doBase)
00772             {
00773               t << "1 " << xf << " " << yf << " hedge\n";
00774             }
00775             else
00776             {
00777               t << "0 " << xf << " " << yf << " hedge\n";
00778             }
00779             t << protToString(getMinProtectionLevel(dil)) << endl;
00780             if (doBase)
00781             {
00782               t << xf << " " << ysf << " " << yf << " vedge\n";
00783             }
00784             else
00785             {
00786               t << xf << " " << (ysf + 0.25) << " " << yf << " vedge\n";
00787             }
00788           }
00789         }
00790         di=dr->next();
00791       }
00792       done=TRUE; // the tree is drawn now
00793     }
00794     else // normal tree connector
00795     {
00796       while (di)
00797       {
00798         int x=0,y=0;
00799         DiagramItemList *dil = di->getChildren();
00800         DiagramItem *parent  = di->parentItem();
00801         if (parent) // item has a parent -> connect to it
00802         {
00803           if (bitmap) // draw pixels
00804           {
00805             x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2;
00806             if (doBase) // base classes
00807             {
00808               y = image->getHeight()-
00809                 (superRows-1)*(cellHeight+labelVertSpacing)-
00810                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00811               /* write input line */
00812               image->drawVertArrow(x,y,y+labelVertSpacing/2,
00813                   protToColor(di->protection()),
00814                   protToMask(di->protection()));
00815             }
00816             else // super classes
00817             {
00818               y = (baseRows-1)*(cellHeight+labelVertSpacing)-
00819                 labelVertSpacing/2+
00820                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00821               /* write output line */
00822               image->drawVertLine(x,y,y+labelVertSpacing/2,
00823                   protToColor(di->protection()),
00824                   protToMask(di->protection()));
00825             }
00826           }
00827           else // draw pixels
00828           {
00829             t << protToString(di->protection()) << endl;
00830             if (doBase)
00831             {
00832               t << "1 " << di->xPos()/(float)gridWidth << " " 
00833                 << (di->yPos()/(float)gridHeight+superRows-1) << " in\n";
00834             }
00835             else
00836             {
00837               t << "0 " << di->xPos()/(float)gridWidth << " " 
00838                 << ((float)superRows-0.25-di->yPos()/(float)gridHeight)
00839                 << " in\n";
00840             }
00841           }
00842         }
00843         if (dil->count()>0)
00844         {
00845           Protection p=getMinProtectionLevel(dil);
00846           uint mask=protToMask(p);
00847           uint col=protToColor(p);
00848           if (bitmap)
00849           {
00850             x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2;
00851             if (doBase) // base classes
00852             {
00853               y = image->getHeight()-
00854                 (superRows-1)*(cellHeight+labelVertSpacing)-
00855                 cellHeight-labelVertSpacing/2-
00856                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00857               image->drawVertLine(x,y,y+labelVertSpacing/2-1,col,mask);
00858             }
00859             else // super classes
00860             {
00861               y = (baseRows-1)*(cellHeight+labelVertSpacing)+
00862                 cellHeight+
00863                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
00864               image->drawVertArrow(x,y,y+labelVertSpacing/2-1,col,mask);
00865             }
00866           }
00867           else
00868           {
00869             t << protToString(p) << endl;
00870             if (doBase)
00871             {
00872               t << "0 " << di->xPos()/(float)gridWidth  << " " 
00873                 << (di->yPos()/(float)gridHeight+superRows-1) << " out\n";
00874             }
00875             else
00876             {
00877               t << "1 " << di->xPos()/(float)gridWidth  << " " 
00878                 << ((float)superRows-1.75-di->yPos()/(float)gridHeight)
00879                 << " out\n";
00880             }
00881           }
00882           /* write input line */
00883           DiagramItem *first = dil->first();
00884           DiagramItem *last  = dil->last();
00885           if (first!=last && !first->isInList()) /* connect with all base classes */
00886           {
00887             if (bitmap)
00888             {
00889               int xs = first->xPos()*(cellWidth+labelHorSpacing)/gridWidth
00890                 + cellWidth/2;
00891               int xe = last->xPos()*(cellWidth+labelHorSpacing)/gridWidth
00892                 + cellWidth/2; 
00893               if (doBase) // base classes
00894               {
00895                 image->drawHorzLine(y,xs,xe,col,mask); 
00896               }
00897               else // super classes
00898               {
00899                 image->drawHorzLine(y+labelVertSpacing/2,xs,xe,col,mask); 
00900               }
00901             }
00902             else
00903             {
00904               t << protToString(p) << endl;
00905               if (doBase)
00906               {
00907                 t << first->xPos()/(float)gridWidth << " " 
00908                   << last->xPos()/(float)gridWidth << " "
00909                   << (first->yPos()/(float)gridHeight+superRows-1) 
00910                   << " conn\n";
00911               }
00912               else
00913               {
00914                 t << first->xPos()/(float)gridWidth << " " 
00915                   << last->xPos()/(float)gridWidth << " "
00916                   << ((float)superRows-first->yPos()/(float)gridHeight)
00917                   << " conn\n";
00918               }
00919             }
00920           }
00921         }
00922         di=dr->next();
00923       }
00924       dr=next();
00925     }
00926   }
00927 }
00928 
00929 
00930 void clearVisitFlags()
00931 {
00932   ClassSDict::Iterator cli(*Doxygen::classSDict);
00933   ClassDef *cd;
00934   for (;(cd=cli.current());++cli)
00935   {
00936     cd->visited=FALSE;
00937   }
00938 }
00939 
00940 ClassDiagram::ClassDiagram(ClassDef *root)
00941 {
00942   clearVisitFlags();
00943   base  = new TreeDiagram(root,TRUE);
00944   base->computeLayout();
00945   clearVisitFlags();
00946   super = new TreeDiagram(root,FALSE);
00947   super->computeLayout();
00948   DiagramItem *baseItem  = base->first()->first();
00949   DiagramItem *superItem = super->first()->first();
00950   int xbase  = baseItem->xPos();
00951   int xsuper = superItem->xPos();
00952   if (xbase>xsuper)
00953   {
00954     superItem->move(xbase-xsuper,0);
00955     super->moveChildren(superItem,xbase-xsuper);
00956   }
00957   else if (xbase<xsuper)
00958   {
00959     baseItem->move(xsuper-xbase,0);
00960     base->moveChildren(baseItem,xsuper-xbase);
00961   }
00962 }
00963 
00964 ClassDiagram::~ClassDiagram()
00965 {
00966   delete base;
00967   delete super;
00968 }
00969 
00970 void ClassDiagram::writeFigure(QTextStream &output,const char *path,
00971                                const char *fileName) const
00972 {
00973   uint baseRows=base->computeRows();
00974   uint superRows=super->computeRows();
00975   uint baseMaxX, baseMaxLabelWidth, superMaxX, superMaxLabelWidth;
00976   base->computeExtremes(&baseMaxLabelWidth,&baseMaxX);
00977   super->computeExtremes(&superMaxLabelWidth,&superMaxX);
00978 
00979   uint rows=baseRows+superRows-1;
00980   uint cols=(QMAX(baseMaxX,superMaxX)+gridWidth*2-1)/gridWidth;
00981   
00982   // Estimate the image aspect width and height in pixels.
00983   uint estHeight = rows*40;
00984   uint estWidth  = cols*(20+QMAX(baseMaxLabelWidth,superMaxLabelWidth));
00985   //printf("Estimated size %d x %d\n",estWidth,estHeight);
00986   
00987   const float pageWidth = 14.0; // estimated page width in cm.
00988                                 // Somewhat lower to deal with estimation
00989                                 // errors. 
00990   
00991   // compute the image height in centimeters based on the estimates
00992   float realHeight = QMIN(rows,12); // real height in cm
00993   float realWidth  = realHeight * estWidth/(float)estHeight;
00994   if (realWidth>pageWidth) // assume that the page width is about 15 cm
00995   {
00996     realHeight*=pageWidth/realWidth; 
00997     realWidth=pageWidth;
00998   }
00999 
01000   //output << "}\n";
01001   output << ":\\begin{figure}[H]\n"
01002             "\\begin{center}\n"
01003             "\\leavevmode\n";
01004   output << "\\includegraphics[height=" << realHeight << "cm]{" 
01005                                         << fileName << "}" << endl;
01006   output << "\\end{center}\n"
01007             "\\end{figure}\n";
01008   
01009   //printf("writeFigure rows=%d cols=%d\n",rows,cols);
01010 
01011   QCString epsBaseName=(QCString)path+"/"+fileName;
01012   QCString epsName=epsBaseName+".eps";
01013   QFile f1;
01014   f1.setName(epsName.data());
01015   if (!f1.open(IO_WriteOnly))
01016   {
01017     err("Could not open file %s for writing\n",convertToQCString(f1.name()).data());
01018     exit(1);
01019   }
01020   QTextStream t(&f1);
01021   
01022   //printf("writeEPS() rows=%d cols=%d\n",rows,cols);
01023   
01024   // generate EPS header and postscript variables and procedures
01025   
01026   t << "%!PS-Adobe-2.0 EPSF-2.0\n";
01027   t << "%%Title: ClassName\n";
01028   t << "%%Creator: Doxygen\n";
01029   t << "%%CreationDate: Time\n";
01030   t << "%%For: \n";
01031   t << "%Magnification: 1.00\n";
01032   t << "%%Orientation: Portrait\n";
01033   t << "%%BoundingBox: 0 0 500 " << estHeight*500.0/(float)estWidth << "\n";
01034   t << "%%Pages: 0\n";
01035   t << "%%BeginSetup\n";
01036   t << "%%EndSetup\n";
01037   t << "%%EndComments\n";
01038   t << "\n";
01039   t << "% ----- variables -----\n";
01040   t << "\n";
01041   t << "/boxwidth 0 def\n";
01042   t << "/boxheight 40 def\n";
01043   t << "/fontheight 24 def\n";
01044   t << "/marginwidth 10 def\n";
01045   t << "/distx 20 def\n";
01046   t << "/disty 40 def\n";
01047   t << "/boundaspect " << estWidth/(float)estHeight << " def  % aspect ratio of the BoundingBox (width/height)\n";
01048   t << "/boundx 500 def\n";
01049   t << "/boundy boundx boundaspect div def\n";
01050   t << "/xspacing 0 def\n";
01051   t << "/yspacing 0 def\n";
01052   t << "/rows " << rows << " def\n";
01053   t << "/cols " << cols << " def\n";
01054   t << "/scalefactor 0 def\n";
01055   t << "/boxfont /Times-Roman findfont fontheight scalefont def\n";
01056   t << "\n";
01057   t << "% ----- procedures -----\n";
01058   t << "\n";
01059   t << "/dotted { [1 4] 0 setdash } def\n";
01060   t << "/dashed { [5] 0 setdash } def\n";
01061   t << "/solid  { [] 0 setdash } def\n";
01062   t << "\n";
01063   t << "/max % result = MAX(arg1,arg2)\n";
01064   t << "{\n";
01065   t << "  /a exch def\n";
01066   t << "  /b exch def\n";
01067   t << "  a b gt {a} {b} ifelse\n";
01068   t << "} def\n";
01069   t << "\n";
01070   t << "/xoffset % result = MAX(0,(scalefactor-(boxwidth*cols+distx*(cols-1)))/2)\n";
01071   t << "{\n";
01072   t << "  0 scalefactor boxwidth cols mul distx cols 1 sub mul add sub 2 div max\n";
01073   t << "} def\n";
01074   t << "\n";
01075   t << "/cw % boxwidth = MAX(boxwidth, stringwidth(arg1))\n";
01076   t << "{\n";
01077   t << "  /str exch def\n";
01078   t << "  /boxwidth boxwidth str stringwidth pop max def\n";
01079   t << "} def\n";
01080   t << "\n";
01081   t << "/box % draws a box with text `arg1' at grid pos (arg2,arg3)\n";
01082   t << "{ gsave\n";
01083   t << "  2 setlinewidth\n";
01084   t << "  newpath\n";
01085   t << "  exch xspacing mul xoffset add\n";
01086   t << "  exch yspacing mul\n";
01087   t << "  moveto\n";
01088   t << "  boxwidth 0 rlineto \n";
01089   t << "  0 boxheight rlineto \n";
01090   t << "  boxwidth neg 0 rlineto \n";
01091   t << "  0 boxheight neg rlineto \n";
01092   t << "  closepath\n";
01093   t << "  dup stringwidth pop neg boxwidth add 2 div\n";
01094   t << "  boxheight fontheight 2 div sub 2 div\n";
01095   t << "  rmoveto show stroke\n";
01096   t << "  grestore\n";
01097   t << "} def  \n";
01098   t << "\n";
01099   t << "/mark\n";
01100   t << "{ newpath\n";
01101   t << "  exch xspacing mul xoffset add boxwidth add\n";
01102   t << "  exch yspacing mul\n";
01103   t << "  moveto\n";
01104   t << "  0 boxheight 4 div rlineto\n";
01105   t << "  boxheight neg 4 div boxheight neg 4 div rlineto\n";
01106   t << "  closepath\n";
01107   t << "  eofill\n";
01108   t << "  stroke\n";
01109   t << "} def\n";
01110   t << "\n";
01111   t << "/arrow\n";
01112   t << "{ newpath\n";
01113   t << "  moveto\n";
01114   t << "  3 -8 rlineto\n";
01115   t << "  -6 0 rlineto\n";
01116   t << "  3 8 rlineto\n";
01117   t << "  closepath\n";
01118   t << "  eofill\n";
01119   t << "  stroke\n";
01120   t << "} def\n";
01121   t << "\n";
01122   t << "/out % draws an output connector for the block at (arg1,arg2)\n";
01123   t << "{\n";
01124   t << "  newpath\n";
01125   t << "  exch xspacing mul xoffset add boxwidth 2 div add\n";
01126   t << "  exch yspacing mul boxheight add\n";
01127   t << "  /y exch def\n";
01128   t << "  /x exch def\n";
01129   t << "  x y moveto\n";
01130   t << "  0 disty 2 div rlineto \n";
01131   t << "  stroke\n";
01132   t << "  1 eq { x y disty 2 div add arrow } if\n";
01133   t << "} def\n";
01134   t << "\n";
01135   t << "/in % draws an input connector for the block at (arg1,arg2)\n";
01136   t << "{\n";
01137   t << "  newpath\n";
01138   t << "  exch xspacing mul xoffset add boxwidth 2 div add\n";
01139   t << "  exch yspacing mul disty 2 div sub\n";
01140   t << "  /y exch def\n";
01141   t << "  /x exch def\n";
01142   t << "  x y moveto\n";
01143   t << "  0 disty 2 div rlineto\n";
01144   t << "  stroke\n";
01145   t << "  1 eq { x y disty 2 div add arrow } if\n";
01146   t << "} def\n";
01147   t << "\n";
01148   t << "/hedge\n";
01149   t << "{\n";
01150   t << "  exch xspacing mul xoffset add boxwidth 2 div add\n";
01151   t << "  exch yspacing mul boxheight 2 div sub\n";
01152   t << "  /y exch def\n";
01153   t << "  /x exch def\n";
01154   t << "  newpath\n";
01155   t << "  x y moveto\n";
01156   t << "  boxwidth 2 div distx add 0 rlineto\n";
01157   t << "  stroke\n";
01158   t << "  1 eq\n";
01159   t << "  { newpath x boxwidth 2 div distx add add y moveto\n";
01160   t << "    -8 3 rlineto\n";
01161   t << "    0 -6 rlineto\n";
01162   t << "    8 3 rlineto\n";
01163   t << "    closepath\n";
01164   t << "    eofill\n";
01165   t << "    stroke\n";
01166   t << "  } if\n";
01167   t << "} def\n";
01168   t << "\n";
01169   t << "/vedge\n";
01170   t << "{\n";
01171   t << "  /ye exch def\n";
01172   t << "  /ys exch def\n";
01173   t << "  /xs exch def\n";
01174   t << "  newpath\n";
01175   t << "  xs xspacing mul xoffset add boxwidth 2 div add dup\n";
01176   t << "  ys yspacing mul boxheight 2 div sub\n";
01177   t << "  moveto\n";
01178   t << "  ye yspacing mul boxheight 2 div sub\n";
01179   t << "  lineto\n";
01180   t << "  stroke\n";
01181   t << "} def\n";
01182   t << "\n";
01183   t << "/conn % connections the blocks from col `arg1' to `arg2' of row `arg3'\n";
01184   t << "{\n";
01185   t << "  /ys exch def\n";
01186   t << "  /xe exch def\n";
01187   t << "  /xs exch def\n";
01188   t << "  newpath\n";
01189   t << "  xs xspacing mul xoffset add boxwidth 2 div add\n";
01190   t << "  ys yspacing mul disty 2 div sub\n";
01191   t << "  moveto\n";
01192   t << "  xspacing xe xs sub mul 0\n";
01193   t << "  rlineto\n";
01194   t << "  stroke\n";
01195   t << "} def\n";
01196   t << "\n";
01197   t << "% ----- main ------\n";
01198   t << "\n";
01199   t << "boxfont setfont\n";
01200   t << "1 boundaspect scale\n";
01201 
01202   
01203   bool done=FALSE;
01204   DiagramRow *dr=base->first();
01205   while (dr && !done)
01206   {
01207     DiagramItem *di=dr->first();
01208     while (di)
01209     {
01210       done=di->isInList();
01211       t << "(" << di->label() << ") cw\n";
01212       di=dr->next();
01213     }
01214     dr=base->next();
01215   }
01216   dr=super->first();
01217   dr=super->next();
01218   done=FALSE;
01219   while (dr && !done)
01220   {
01221     DiagramItem *di=dr->first();
01222     while (di)
01223     {
01224       done=di->isInList();
01225       t << "(" << di->label() << ") cw\n";
01226       di=dr->next();
01227     }
01228     dr=super->next();
01229   }
01230   
01231   t << "/boxwidth boxwidth marginwidth 2 mul add def\n"
01232     << "/xspacing boxwidth distx add def\n"
01233     << "/yspacing boxheight disty add def\n"
01234     << "/scalefactor \n"
01235     << "  boxwidth cols mul distx cols 1 sub mul add\n"
01236     << "  boxheight rows mul disty rows 1 sub mul add boundaspect mul \n"
01237     << "  max def\n"
01238     << "boundx scalefactor div boundy scalefactor div scale\n";
01239   
01240   t << "\n% ----- classes -----\n\n";
01241   base->drawBoxes(t,0,TRUE,FALSE,baseRows,superRows,0,0);
01242   super->drawBoxes(t,0,FALSE,FALSE,baseRows,superRows,0,0);
01243   
01244   t << "\n% ----- relations -----\n\n";
01245   base->drawConnectors(t,0,TRUE,FALSE,baseRows,superRows,0,0);
01246   super->drawConnectors(t,0,FALSE,FALSE,baseRows,superRows,0,0);
01247 
01248   f1.close();
01249   if (Config_getBool("USE_PDFLATEX"))
01250   {
01251     QCString epstopdfArgs(4096);
01252     epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
01253                    epsBaseName.data(),epsBaseName.data());
01254     //printf("Converting eps using `%s'\n",epstopdfCmd.data());
01255     if (portable_system("epstopdf",epstopdfArgs)!=0)
01256     {
01257        err("Error: Problems running epstopdf. Check your TeX installation!\n");
01258        return;
01259     }
01260   }
01261 }
01262 
01263 
01264 void ClassDiagram::writeImage(QTextStream &t,const char *path,
01265                               const char *relPath,const char *fileName, 
01266                               bool generateMap) const
01267 {
01268   uint baseRows=base->computeRows();
01269   uint superRows=super->computeRows();
01270   uint rows=baseRows+superRows-1;
01271 
01272   uint lb,ls,xb,xs;
01273   base->computeExtremes(&lb,&xb);
01274   super->computeExtremes(&ls,&xs);
01275  
01276   uint cellWidth  = QMAX(lb,ls)+labelHorMargin*2;
01277   uint maxXPos    = QMAX(xb,xs);
01278   uint labelVertMargin = 6; //QMAX(6,(cellWidth-fontHeight)/6); // aspect at least 1:3
01279   uint cellHeight = labelVertMargin*2+fontHeight;
01280   uint imageWidth = (maxXPos+gridWidth)*cellWidth/gridWidth+
01281                     (maxXPos*labelHorSpacing)/gridWidth;
01282   uint imageHeight = rows*cellHeight+(rows-1)*labelVertSpacing;
01283 
01284   Image image(imageWidth,imageHeight);
01285 
01286   base->drawBoxes(t,&image,TRUE,TRUE,baseRows,superRows,cellWidth,cellHeight,relPath,generateMap);
01287   super->drawBoxes(t,&image,FALSE,TRUE,baseRows,superRows,cellWidth,cellHeight,relPath,generateMap);
01288   base->drawConnectors(t,&image,TRUE,TRUE,baseRows,superRows,cellWidth,cellHeight);
01289   super->drawConnectors(t,&image,FALSE,TRUE,baseRows,superRows,cellWidth,cellHeight);
01290 
01291   image.save((QCString)path+"/"+fileName+".png");
01292   
01293   if (generateMap) t << "</map>" << endl;
01294 }
01295 



Generated on Mon Mar 31 10:58:34 2008 by  doxygen 1.5.1