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