diff options
Diffstat (limited to 'src/diagram.cpp')
-rw-r--r-- | src/diagram.cpp | 1236 |
1 files changed, 1236 insertions, 0 deletions
diff --git a/src/diagram.cpp b/src/diagram.cpp new file mode 100644 index 0000000..0fd30a5 --- /dev/null +++ b/src/diagram.cpp @@ -0,0 +1,1236 @@ +/****************************************************************************** + * + * $Id$ + * + * + * Copyright (C) 1997-1999 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * All output generated with Doxygen is not covered by this license. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <qlist.h> +#include <qstring.h> +#include <qarray.h> +#include <qtstream.h> +#include <qfile.h> + +#include "diagram.h" +#include "image.h" +#include "classdef.h" +#include "config.h" +#include "message.h" +#include "util.h" +#include "latexgen.h" +#include "htmlgen.h" + +//----------------------------------------------------------------------------- + +const uint maxTreeWidth = 8; +const int gridWidth = 100; +const int gridHeight = 100; + +const uint labelHorSpacing = 10; // horizontal distance between labels +const uint labelVertSpacing = 32; // vertical distance between labels +const uint labelHorMargin = 6; // horiz. spacing between label and box +const uint fontHeight = 12; // height of a character + +//static QString escapeLatex(const char *s) +//{ +// QString result; +// char c; +// while ((c=*s++)) +// { +// if (c=='_') result+="\\_"; +// else result+=c; +// } +// return result; +//} + +static uint protToMask(Protection p) +{ + switch(p) + { + case Public: return 0xffffffff; + case Protected: return 0xcccccccc; + case Private: return 0xaaaaaaaa; + } + return 0; +} + +static uint protToColor(Protection p) +{ + switch(p) + { + case Public: return 6; + case Protected: return 5; + case Private: return 4; + } + return 0; +} + +static QString protToString(Protection p) +{ + switch(p) + { + case Public: return "solid"; + case Protected: return "dashed"; + case Private: return "dotted"; + } + return 0; +} + +static uint virtToMask(Specifier p) +{ + switch(p) + { + case Normal: return 0xffffffff; + case Virtual: return 0xf0f0f0f0; + default: return 0; + } + return 0; +} + +// pre: dil is not empty +static Protection getMinProtectionLevel(DiagramItemList *dil) +{ + DiagramItem *di=dil->first(); + Protection result=di->protection(); + di=dil->next(); + while (di) + { + Protection p=di->protection(); + if (p!=result) + { + if (result==Protected && p==Public) result=p; + else if (result==Private) result=p; + } + di=dil->next(); + } + return result; +} + +static void writeBitmapBox(DiagramItem *di,Image *image, + int x,int y,int w,int h,bool firstRow, + bool hasDocs,bool children=FALSE) +{ + int colFill = hasDocs ? (firstRow ? 0 : 2) : 7; + int colBorder = (firstRow || !hasDocs) ? 1 : 3; + int l = stringLength(di->label()); + uint mask=virtToMask(di->virtualness()); + image->fillRect(x+1,y+1,w-2,h-2,colFill,mask); + image->drawRect(x,y,w,h,colBorder,mask); + image->writeString(x+(w-l)/2, y+(h-fontHeight)/2, di->label(),1); + if (children) + { + int i; + for (i=0;i<5;i++) + image->drawHorzLine(y+h+i-6,x+w-2-i,x+w-2,firstRow?1:3,0xffffffff); + } +} + +static void writeVectorBox(QTextStream &t,DiagramItem *di, + float x,float y,bool children=FALSE) +{ + if (di->virtualness()==Virtual) t << "dashed\n"; + t << " (" << di->label() << ") " << x << " " << y << " box\n"; + if (children) t << x << " " << y << " mark\n"; + if (di->virtualness()==Virtual) t << "solid\n"; +} + +static void writeMapArea(QTextStream &t,ClassDef *cd,int x,int y,int w,int h) +{ + if (cd->hasDocumentation() || cd->isReference()) + { + t << "<area "; + if (cd->getReference()) t << "doxygen=\"" << cd->getReference() << ":\" "; + t << "href=\"" << cd->classFile() << ".html\" "; + t << "ALT=\"" << cd->name(); + t << "\" shape=\"rect\" coords=\"" << x << "," << y << ","; + t << x+w << "," << y+h << "\">" << endl; + } +} +//----------------------------------------------------------------------------- + +DiagramItem::DiagramItem(DiagramItem *p,int number,ClassDef *cd, + Protection pr,Specifier vi,const char *ts) +{ + parent=p; + x=y=0; + //name=n; + num=number; + children = new DiagramItemList; + prot=pr; + virt=vi; + inList=FALSE; + classDef=cd; + templSpec=ts; +} + +DiagramItem::~DiagramItem() +{ + delete children; +} + +QString DiagramItem::label() const +{ + return classDef->name()+templSpec; +} + +QString DiagramItem::fileName() const +{ + return classDef->classFile(); +} + +int DiagramItem::avgChildPos() const +{ + DiagramItem *di; + int c=children->count(); + if (c==0) // no children -> don't move + return xPos(); + if ((di=children->getFirst())->isInList()) // children should be in a list + return di->xPos(); + if (c&1) // odd number of children -> get pos of middle child + return children->at(c/2)->xPos(); + else // even number of children -> get middle of most middle children + return (children->at(c/2-1)->xPos()+children->at(c/2)->xPos())/2; +} + +int DiagramItem::numChildren() const +{ + return children->count(); +} + +void DiagramItem::addChild(DiagramItem *di) +{ + children->append(di); +} + +void DiagramRow::insertClass(DiagramItem *parent,ClassDef *cd,bool doBases, + Protection prot,Specifier virt,const char *ts) +{ + DiagramItem *di=new DiagramItem(parent, diagram->at(level)->count(), + cd,prot,virt,ts); + if (parent) parent->addChild(di); + di->move(count()*gridWidth,level*gridHeight); + append(di); + BaseClassList *bcl=doBases ? cd->baseClasses() : cd->superClasses(); + /* there are base/super classes */ + int count=0; + BaseClassDef *bcd=bcl->first(); + while (bcd) + { + ClassDef *ccd=bcd->classDef; + if (ccd && ccd->isVisibleExt() + //(ccd->protection()!=Private || extractPrivateFlag) && + //(ccd->hasDocumentation() || !hideClassFlag) + ) count++; + bcd=bcl->next(); + } + if (count>0 && (prot!=Private || !doBases)) + { + DiagramRow *row=0; + if (diagram->count()<=level+1) /* add new row */ + { + row = new DiagramRow(diagram,level+1); + diagram->append(row); + } + else /* get next row */ + { + row=diagram->at(level+1); + } + /* insert base classes in the next row */ + BaseClassDef *bcd=bcl->last(); + while (bcd) + { + ClassDef *ccd=bcd->classDef; + if (ccd && ccd->isVisibleExt() + //(ccd->protection()!=Private || extractPrivateFlag) && + //(ccd->hasDocumentation() || !hideClassFlag) + ) + { + row->insertClass(di,ccd,doBases,bcd->prot, + doBases?bcd->virt:Normal, + doBases?bcd->templSpecifiers.data():""); + } + bcd=bcl->prev(); + } + } +} + +TreeDiagram::TreeDiagram(ClassDef *root,bool doBases) +{ + setAutoDelete(TRUE); + DiagramRow *row=new DiagramRow(this,0); + append(row); + row->insertClass(0,root,doBases,Public,Normal,0); +} + +TreeDiagram::~TreeDiagram() +{ +} + + +void TreeDiagram::moveChildren(DiagramItem *root,int dx) +{ + DiagramItemList *dil=root->getChildren(); + DiagramItem *di=dil->first(); + while (di) + { + di->move(dx,0); + moveChildren(di,dx); + di=dil->next(); + } +} + +bool TreeDiagram::layoutTree(DiagramItem *root,int r) +{ + bool moved=FALSE; + //printf("layoutTree(%s,%d)\n",root->label().data(),r); + + DiagramItemList *dil=root->getChildren(); + if (dil->count()>0) + { + uint k; + int pPos=root->xPos(); + int cPos=root->avgChildPos(); + if (pPos>cPos) // move children + { + DiagramRow *row=at(r+1); + //printf("Moving children %d-%d in row %d\n", + // dil->getFirst()->number(),row->count()-1,r+1); + for (k=dil->getFirst()->number();k<row->count();k++) + row->at(k)->move(pPos-cPos,0); + moved=TRUE; + } + else if (pPos<cPos) // move parent + { + DiagramRow *row=at(r); + //printf("Moving parents %d-%d in row %d\n", + // root->number(),row->count()-1,r); + for (k=root->number();k<row->count();k++) + row->at(k)->move(cPos-pPos,0); + moved=TRUE; + } + + // recurse to children + DiagramItem *di=dil->first(); + while (di && !moved && !di->isInList()) + { + moved = moved || layoutTree(di,r+1); + di=dil->next(); + } + } + return moved; +} + +void TreeDiagram::computeLayout() +{ + DiagramRow *row=first(); + while (row && row->count()<maxTreeWidth) row=next(); + if (row) + { + //printf("computeLayout() list row at %d\n",row->number()); + DiagramItem *di=row->first(); + DiagramItem *opi=0; + int delta=0; + bool first=TRUE; + while (di) + { + DiagramItem *pi=di->parentItem(); + if (pi==opi && !first) { delta-=gridWidth; } + first = pi!=opi; + opi=pi; + di->move(delta,0); // collapse all items in the same + // list (except the first) + di->putInList(); + di=row->next(); + } + } + + // re-organize the diagram items + DiagramItem *root=getFirst()->getFirst(); + while (layoutTree(root,0)); + + // move first items of the lists + if (row) + { + DiagramItem *di=row->first(); + while (di) + { + DiagramItem *pi=di->parentItem(); + if (pi->getChildren()->count()>1) + { + di->move(gridWidth,0); + while (di && di->parentItem()==pi) di=row->next(); + } + else + { + di=row->next(); + } + } + } +} + +uint TreeDiagram::computeRows() +{ + //printf("TreeDiagram::computeRows()=%d\n",count()); + int count=0; + DiagramRow *row=first(); + while (row && !row->getFirst()->isInList()) + { + count++; + row=next(); + } + //printf("count=%d row=%p\n",count,row); + if (row) + { + int maxListLen=0; + int curListLen=0; + DiagramItem *di=row->first(),*opi=0; + while (di) + { + if (di->parentItem()!=opi) curListLen=1; else curListLen++; + if (curListLen>maxListLen) maxListLen=curListLen; + opi=di->parentItem(); + di=row->next(); + } + //printf("maxListLen=%d\n",maxListLen); + count+=maxListLen; + } + return count; +} + +#if 0 +uint TreeDiagram::computeCols() +{ + uint count=0; + DiagramRow *row=first(); + while (row && !row->getFirst()->isInList()) + { + if (row->count()>count) count=row->count(); + row=next(); + } + if (row) + { + row=prev(); + uint cols=row->count(); + if (row->getLast()->getChildren()->count()>1) cols++; + if (cols>count) count=cols; + } + return count; +}; +#endif + +void TreeDiagram::computeExtremes(uint *maxLabelLen,uint *maxXPos) +{ + uint ml=0,mx=0; + DiagramRow *dr=first(); + bool done=FALSE; + while (dr && !done) + { + DiagramItem *di=dr->first(); + while (di) + { + if (di->isInList()) done=TRUE; + if (maxXPos) mx=QMAX(mx,(uint)di->xPos()); + if (maxLabelLen) ml=QMAX(ml,stringLength(di->label())); + di=dr->next(); + } + dr=next(); + } + if (maxLabelLen) *maxLabelLen=ml; + if (maxXPos) *maxXPos=mx; +} + +void TreeDiagram::drawBoxes(QTextStream &t,Image *image, + bool doBase,bool bitmap, + uint baseRows,uint superRows, + uint cellWidth,uint cellHeight) +{ + DiagramRow *dr=first(); + if (!doBase) dr=next(); + bool done=FALSE; + bool firstRow = doBase; + while (dr && !done) + { + int x=0,y=0; + float xf=0.0,yf=0.0; + DiagramItem *di=dr->first(); + if (di->isInList()) // put boxes in a list + { + DiagramItem *opi=0; + if (doBase) di=dr->last(); + while (di) + { + if (di->parentItem()==opi) + { + if (bitmap) + { + if (doBase) y -= cellHeight+labelVertSpacing; + else y += cellHeight+labelVertSpacing; + } + else + { + if (doBase) yf += 1.0; + else yf -= 1.0; + } + } + else + { + if (bitmap) + { + x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth; + if (doBase) + { + y = image->getHeight()- + superRows*cellHeight- + (superRows-1)*labelVertSpacing- + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + } + else + { + y = (baseRows-1)*(cellHeight+labelVertSpacing)+ + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + } + } + else + { + xf = di->xPos()/(float)gridWidth; + if (doBase) + { + yf = di->yPos()/(float)gridHeight+superRows-1; + } + else + { + yf = superRows-1-di->yPos()/(float)gridHeight; + } + } + } + opi=di->parentItem(); + + if (bitmap) + { + bool hasDocs=di->getClassDef()->hasDocumentation() || + di->getClassDef()->isReference(); + writeBitmapBox(di,image,x,y,cellWidth,cellHeight,firstRow, + hasDocs,di->getChildren()->count()>0); + if (!firstRow) writeMapArea(t,di->getClassDef(),x,y,cellWidth,cellHeight); + } + else + { + writeVectorBox(t,di,xf,yf,di->getChildren()->count()>0); + } + + if (doBase) di=dr->prev(); else di=dr->next(); + } + done=TRUE; + } + else // draw a tree of boxes + { + while (di) + { + if (bitmap) + { + x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth; + if (doBase) + { + y = image->getHeight()- + superRows*cellHeight- + (superRows-1)*labelVertSpacing- + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + } + else + { + y = (baseRows-1)*(cellHeight+labelVertSpacing)+ + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + } + bool hasDocs=di->getClassDef()->hasDocumentation() || + di->getClassDef()->isReference(); + writeBitmapBox(di,image,x,y,cellWidth,cellHeight,firstRow,hasDocs); + if (!firstRow) writeMapArea(t,di->getClassDef(),x,y,cellWidth,cellHeight); + } + else + { + xf=di->xPos()/(float)gridWidth; + if (doBase) + { + yf = di->yPos()/(float)gridHeight+superRows-1; + } + else + { + yf = superRows-1-di->yPos()/(float)gridHeight; + } + writeVectorBox(t,di,xf,yf); + } + + di=dr->next(); + } + } + dr=next(); + firstRow=FALSE; + } +} + +void TreeDiagram::drawConnectors(QTextStream &t,Image *image, + bool doBase,bool bitmap, + uint baseRows,uint superRows, + uint cellWidth,uint cellHeight) +{ + DiagramRow *dr=first(); + bool done=FALSE; + while (dr && !done) // for each row + { + DiagramItem *di=dr->first(); + if (di->isInList()) // row consists of list connectors + { + int x=0,y=0,ys=0; + float xf=0.0,yf=0.0,ysf=0.0; + while (di) + { + DiagramItem *pi=di->parentItem(); + DiagramItemList *dil=pi->getChildren(); + DiagramItem *last=dil->getLast(); + if (di==last) // single child + { + if (bitmap) // draw pixels + { + x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2; + if (doBase) // base classes + { + y = image->getHeight()- + (superRows-1)*(cellHeight+labelVertSpacing)- + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + image->drawVertArrow(x,y,y+labelVertSpacing/2, + protToColor(di->protection()), + protToMask(di->protection())); + } + else // super classes + { + y = (baseRows-1)*(cellHeight+labelVertSpacing)- + labelVertSpacing/2+ + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + image->drawVertLine(x,y,y+labelVertSpacing/2, + protToColor(di->protection()), + protToMask(di->protection())); + } + } + else // draw vectors + { + t << protToString(di->protection()) << endl; + if (doBase) + { + t << "1 " << di->xPos()/(float)gridWidth << " " + << di->yPos()/(float)gridHeight+superRows-1 << " in\n"; + } + else + { + t << "0 " << di->xPos()/(float)gridWidth << " " + << (float)superRows-0.25-di->yPos()/(float)gridHeight + << " in\n"; + } + } + } + else // multiple children, put them in a vertical list + { + if (bitmap) + { + x = di->parentItem()->xPos()* + (cellWidth+labelHorSpacing)/gridWidth+cellWidth/2; + if (doBase) // base classes + { + ys = image->getHeight()- + (superRows-1)*(cellHeight+labelVertSpacing)- + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + y = ys - cellHeight/2; + } + else // super classes + { + ys = (baseRows-1)*(cellHeight+labelVertSpacing)+ + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + y = ys + cellHeight/2; + } + } + else + { + xf = di->parentItem()->xPos()/(float)gridWidth; + if (doBase) + { + ysf = di->yPos()/(float)gridHeight+superRows-1; + yf = ysf + 0.5; + } + else + { + ysf = (float)superRows-0.25-di->yPos()/(float)gridHeight; + yf = ysf - 0.25; + } + } + while (di!=last) // more children to add + { + if (bitmap) + { + if (doBase) // base classes + { + image->drawHorzArrow(y,x,x+cellWidth/2+labelHorSpacing, + protToColor(di->protection()), + protToMask(di->protection())); + y -= cellHeight+labelVertSpacing; + } + else // super classes + { + image->drawHorzLine(y,x,x+cellWidth/2+labelHorSpacing, + protToColor(di->protection()), + protToMask(di->protection())); + y += cellHeight+labelVertSpacing; + } + } + else + { + t << protToString(di->protection()) << endl; + if (doBase) + { + t << "1 " << xf << " " << yf << " hedge\n"; + yf += 1.0; + } + else + { + t << "0 " << xf << " " << yf << " hedge\n"; + yf -= 1.0; + } + } + di=dr->next(); + } + // add last horizonal line and a vertical connection line + if (bitmap) + { + if (doBase) // base classes + { + image->drawHorzArrow(y,x,x+cellWidth/2+labelHorSpacing, + protToColor(di->protection()), + protToMask(di->protection())); + image->drawVertLine(x,y,ys+labelVertSpacing/2, + protToColor(getMinProtectionLevel(dil)), + protToMask(getMinProtectionLevel(dil))); + } + else // super classes + { + image->drawHorzLine(y,x,x+cellWidth/2+labelHorSpacing, + protToColor(di->protection()), + protToMask(di->protection())); + image->drawVertLine(x,ys-labelVertSpacing/2,y, + protToColor(getMinProtectionLevel(dil)), + protToMask(getMinProtectionLevel(dil))); + } + } + else + { + t << protToString(di->protection()) << endl; + if (doBase) + { + t << "1 " << xf << " " << yf << " hedge\n"; + } + else + { + t << "0 " << xf << " " << yf << " hedge\n"; + } + t << protToString(getMinProtectionLevel(dil)) << endl; + if (doBase) + { + t << xf << " " << ysf << " " << yf << " vedge\n"; + } + else + { + t << xf << " " << ysf + 0.25 << " " << yf << " vedge\n"; + } + } + } + di=dr->next(); + } + done=TRUE; // the tree is drawn now + } + else // normal tree connector + { + while (di) + { + int x=0,y=0; + DiagramItemList *dil = di->getChildren(); + DiagramItem *parent = di->parentItem(); + if (parent) // item has a parent -> connect to it + { + if (bitmap) // draw pixels + { + x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2; + if (doBase) // base classes + { + y = image->getHeight()- + (superRows-1)*(cellHeight+labelVertSpacing)- + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + /* write input line */ + image->drawVertArrow(x,y,y+labelVertSpacing/2, + protToColor(di->protection()), + protToMask(di->protection())); + } + else // super classes + { + y = (baseRows-1)*(cellHeight+labelVertSpacing)- + labelVertSpacing/2+ + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + /* write output line */ + image->drawVertLine(x,y,y+labelVertSpacing/2, + protToColor(di->protection()), + protToMask(di->protection())); + } + } + else // draw pixels + { + t << protToString(di->protection()) << endl; + if (doBase) + { + t << "1 " << di->xPos()/(float)gridWidth << " " + << di->yPos()/(float)gridHeight+superRows-1 << " in\n"; + } + else + { + t << "0 " << di->xPos()/(float)gridWidth << " " + << (float)superRows-0.25-di->yPos()/(float)gridHeight + << " in\n"; + } + } + } + if (dil->count()>0) + { + Protection p=getMinProtectionLevel(dil); + uint mask=protToMask(p); + uint col=protToColor(p); + if (bitmap) + { + x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2; + if (doBase) // base classes + { + y = image->getHeight()- + (superRows-1)*(cellHeight+labelVertSpacing)- + cellHeight-labelVertSpacing/2- + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + image->drawVertLine(x,y,y+labelVertSpacing/2-1,col,mask); + } + else // super classes + { + y = (baseRows-1)*(cellHeight+labelVertSpacing)+ + cellHeight+ + di->yPos()*(cellHeight+labelVertSpacing)/gridHeight; + image->drawVertArrow(x,y,y+labelVertSpacing/2-1,col,mask); + } + } + else + { + t << protToString(p) << endl; + if (doBase) + { + t << "0 " << di->xPos()/(float)gridWidth << " " + << di->yPos()/(float)gridHeight+superRows-1 << " out\n"; + } + else + { + t << "1 " << di->xPos()/(float)gridWidth << " " + << (float)superRows-1.75-di->yPos()/(float)gridHeight + << " out\n"; + } + } + /* write input line */ + DiagramItem *first = dil->first(); + DiagramItem *last = dil->last(); + if (first!=last && !first->isInList()) /* connect with all base classes */ + { + if (bitmap) + { + int xs = first->xPos()*(cellWidth+labelHorSpacing)/gridWidth + + cellWidth/2; + int xe = last->xPos()*(cellWidth+labelHorSpacing)/gridWidth + + cellWidth/2; + if (doBase) // base classes + { + image->drawHorzLine(y,xs,xe,col,mask); + } + else // super classes + { + image->drawHorzLine(y+labelVertSpacing/2,xs,xe,col,mask); + } + } + else + { + t << protToString(p) << endl; + if (doBase) + { + t << first->xPos()/(float)gridWidth << " " + << last->xPos()/(float)gridWidth << " " + << first->yPos()/(float)gridHeight+superRows-1 + << " conn\n"; + } + else + { + t << first->xPos()/(float)gridWidth << " " + << last->xPos()/(float)gridWidth << " " + << (float)superRows-first->yPos()/(float)gridHeight + << " conn\n"; + } + } + } + } + di=dr->next(); + } + dr=next(); + } + } +} + +ClassDiagram::ClassDiagram(ClassDef *root) +{ + base = new TreeDiagram(root,TRUE); + base->computeLayout(); + super = new TreeDiagram(root,FALSE); + super->computeLayout(); + DiagramItem *baseItem = base->first()->first(); + DiagramItem *superItem = super->first()->first(); + int xbase = baseItem->xPos(); + int xsuper = superItem->xPos(); + if (xbase>xsuper) + { + superItem->move(xbase-xsuper,0); + super->moveChildren(superItem,xbase-xsuper); + } + else if (xbase<xsuper) + { + baseItem->move(xsuper-xbase,0); + base->moveChildren(baseItem,xsuper-xbase); + } +} + +ClassDiagram::~ClassDiagram() +{ + delete base; + delete super; +} + +void ClassDiagram::writeFigure(QTextStream &output,const char *path, + const char *fileName) +{ + uint baseRows=base->computeRows(); + uint superRows=super->computeRows(); + uint baseMaxX, baseMaxLabelWidth, superMaxX, superMaxLabelWidth; + base->computeExtremes(&baseMaxLabelWidth,&baseMaxX); + super->computeExtremes(&superMaxLabelWidth,&superMaxX); + + uint rows=baseRows+superRows-1; + uint cols=(QMAX(baseMaxX,superMaxX)+gridWidth*2-1)/gridWidth; + + // Estimate the image aspect width and height in pixels. + uint estHeight = rows*40; + uint estWidth = cols*(20+QMAX(baseMaxLabelWidth,superMaxLabelWidth)); + //printf("Estimated size %d x %d\n",estWidth,estHeight); + + const float pageWidth = 14.0; // estimated page width in cm. + // Somewhat lower to deal with estimation + // errors. + + // compute the image height in centimeters based on the estimates + float realHeight = QMIN(rows,12); // real height in cm + float realWidth = realHeight * estWidth/(float)estHeight; + if (realWidth>pageWidth) // assume that the page width is about 15 cm + { + realHeight*=pageWidth/realWidth; + realWidth=pageWidth; + } + + output << "}\n" + "\\begin{figure}[H]\n" + "\\begin{center}\n" + "\\leavevmode\n"; + output << "\\setlength{\\epsfysize}{" << realHeight << "cm}\n"; + output << "\\epsfbox{" << fileName << ".eps}\n" + "\\end{center}\n" + "\\end{figure}\n"; + + //printf("writeFigure rows=%d cols=%d\n",rows,cols); + + QFile f1((QString)path+"/"+fileName+".eps"); + if (!f1.open(IO_WriteOnly)) + { + err("Could not open file %s for writing\n",f1.name()); + exit(1); + } + QTextStream t(&f1); + + //printf("writeEPS() rows=%d cols=%d\n",rows,cols); + + // generate EPS header and postscipt variables and procedures + + t << "%!PS-Adobe-2.0 EPSF-2.0\n"; + t << "%%Title: ClassName\n"; + t << "%%Creator: Doxygen\n"; + t << "%%CreationDate: Time\n"; + t << "%%For: \n"; + t << "%Magnification: 1.00\n"; + t << "%%Orientation: Portrait\n"; + t << "%%BoundingBox: 0 0 500 " << estHeight*500.0/(float)estWidth << "\n"; + t << "%%Pages: 0\n"; + t << "%%BeginSetup\n"; + t << "%%EndSetup\n"; + t << "%%EndComments\n"; + t << "\n"; + t << "% ----- variables -----\n"; + t << "\n"; + t << "/boxwidth 0 def\n"; + t << "/boxheight 40 def\n"; + t << "/fontheight 24 def\n"; + t << "/marginwidth 10 def\n"; + t << "/distx 20 def\n"; + t << "/disty 40 def\n"; + t << "/boundaspect " << estWidth/(float)estHeight << " def % aspect ratio of the BoundingBox (width/height)\n"; + t << "/boundx 500 def\n"; + t << "/boundy boundx boundaspect div def\n"; + t << "/xspacing 0 def\n"; + t << "/yspacing 0 def\n"; + t << "/rows " << rows << " def\n"; + t << "/cols " << cols << " def\n"; + t << "/scalefactor 0 def\n"; + t << "/boxfont /Times-Roman findfont fontheight scalefont def\n"; + t << "\n"; + t << "% ----- procedures -----\n"; + t << "\n"; + t << "/dotted { [1 4] 0 setdash } def\n"; + t << "/dashed { [5] 0 setdash } def\n"; + t << "/solid { [] 0 setdash } def\n"; + t << "\n"; + t << "/max % result = MAX(arg1,arg2)\n"; + t << "{\n"; + t << " /a exch def\n"; + t << " /b exch def\n"; + t << " a b gt {a} {b} ifelse\n"; + t << "} def\n"; + t << "\n"; + t << "/xoffset % result = MAX(0,(scalefactor-(boxwidth*cols+distx*(cols-1)))/2)\n"; + t << "{\n"; + t << " 0 scalefactor boxwidth cols mul distx cols 1 sub mul add sub 2 div max\n"; + t << "} def\n"; + t << "\n"; + t << "/cw % boxwidth = MAX(boxwidth, stringwidth(arg1))\n"; + t << "{\n"; + t << " /str exch def\n"; + t << " /boxwidth boxwidth str stringwidth pop max def\n"; + t << "} def\n"; + t << "\n"; + t << "/box % draws a box with text `arg1' at grid pos (arg2,arg3)\n"; + t << "{ gsave\n"; + t << " 2 setlinewidth\n"; + t << " newpath\n"; + t << " exch xspacing mul xoffset add\n"; + t << " exch yspacing mul\n"; + t << " moveto\n"; + t << " boxwidth 0 rlineto \n"; + t << " 0 boxheight rlineto \n"; + t << " boxwidth neg 0 rlineto \n"; + t << " 0 boxheight neg rlineto \n"; + t << " closepath\n"; + t << " dup stringwidth pop neg boxwidth add 2 div\n"; + t << " boxheight fontheight 2 div sub 2 div\n"; + t << " rmoveto show stroke\n"; + t << " grestore\n"; + t << "} def \n"; + t << "\n"; + t << "/mark\n"; + t << "{ newpath\n"; + t << " exch xspacing mul xoffset add boxwidth add\n"; + t << " exch yspacing mul\n"; + t << " moveto\n"; + t << " 0 boxheight 4 div rlineto\n"; + t << " boxheight neg 4 div boxheight neg 4 div rlineto\n"; + t << " closepath\n"; + t << " eofill\n"; + t << " stroke\n"; + t << "} def\n"; + t << "\n"; + t << "/arrow\n"; + t << "{ newpath\n"; + t << " moveto\n"; + t << " 3 -8 rlineto\n"; + t << " -6 0 rlineto\n"; + t << " 3 8 rlineto\n"; + t << " closepath\n"; + t << " eofill\n"; + t << " stroke\n"; + t << "} def\n"; + t << "\n"; + t << "/out % draws an output connector for the block at (arg1,arg2)\n"; + t << "{\n"; + t << " newpath\n"; + t << " exch xspacing mul xoffset add boxwidth 2 div add\n"; + t << " exch yspacing mul boxheight add\n"; + t << " /y exch def\n"; + t << " /x exch def\n"; + t << " x y moveto\n"; + t << " 0 disty 2 div rlineto \n"; + t << " stroke\n"; + t << " 1 eq { x y disty 2 div add arrow } if\n"; + t << "} def\n"; + t << "\n"; + t << "/in % draws an input connector for the block at (arg1,arg2)\n"; + t << "{\n"; + t << " newpath\n"; + t << " exch xspacing mul xoffset add boxwidth 2 div add\n"; + t << " exch yspacing mul disty 2 div sub\n"; + t << " /y exch def\n"; + t << " /x exch def\n"; + t << " x y moveto\n"; + t << " 0 disty 2 div rlineto\n"; + t << " stroke\n"; + t << " 1 eq { x y disty 2 div add arrow } if\n"; + t << "} def\n"; + t << "\n"; + t << "/hedge\n"; + t << "{\n"; + t << " exch xspacing mul xoffset add boxwidth 2 div add\n"; + t << " exch yspacing mul boxheight 2 div sub\n"; + t << " /y exch def\n"; + t << " /x exch def\n"; + t << " newpath\n"; + t << " x y moveto\n"; + t << " boxwidth 2 div distx add 0 rlineto\n"; + t << " stroke\n"; + t << " 1 eq\n"; + t << " { newpath x boxwidth 2 div distx add add y moveto\n"; + t << " -8 3 rlineto\n"; + t << " 0 -6 rlineto\n"; + t << " 8 3 rlineto\n"; + t << " closepath\n"; + t << " eofill\n"; + t << " stroke\n"; + t << " } if\n"; + t << "} def\n"; + t << "\n"; + t << "/vedge\n"; + t << "{\n"; + t << " /ye exch def\n"; + t << " /ys exch def\n"; + t << " /xs exch def\n"; + t << " newpath\n"; + t << " xs xspacing mul xoffset add boxwidth 2 div add dup\n"; + t << " ys yspacing mul boxheight 2 div sub\n"; + t << " moveto\n"; + t << " ye yspacing mul boxheight 2 div sub\n"; + t << " lineto\n"; + t << " stroke\n"; + t << "} def\n"; + t << "\n"; + t << "/conn % connections the blocks from col `arg1' to `arg2' of row `arg3'\n"; + t << "{\n"; + t << " /ys exch def\n"; + t << " /xe exch def\n"; + t << " /xs exch def\n"; + t << " newpath\n"; + t << " xs xspacing mul xoffset add boxwidth 2 div add\n"; + t << " ys yspacing mul disty 2 div sub\n"; + t << " moveto\n"; + t << " xspacing xe xs sub mul 0\n"; + t << " rlineto\n"; + t << " stroke\n"; + t << "} def\n"; + t << "\n"; + t << "% ----- main ------\n"; + t << "\n"; + t << "boxfont setfont\n"; + t << "1 boundaspect scale\n"; + + + bool done=FALSE; + DiagramRow *dr=base->first(); + while (dr && !done) + { + DiagramItem *di=dr->first(); + while (di) + { + done=di->isInList(); + t << "(" << di->label() << ") cw\n"; + di=dr->next(); + } + dr=base->next(); + } + dr=super->first(); + dr=super->next(); + done=FALSE; + while (dr && !done) + { + DiagramItem *di=dr->first(); + while (di) + { + done=di->isInList(); + t << "(" << di->label() << ") cw\n"; + di=dr->next(); + } + dr=super->next(); + } + + t << "/boxwidth boxwidth marginwidth 2 mul add def\n" + << "/xspacing boxwidth distx add def\n" + << "/yspacing boxheight disty add def\n" + << "/scalefactor \n" + << " boxwidth cols mul distx cols 1 sub mul add\n" + << " boxheight rows mul disty rows 1 sub mul add boundaspect mul \n" + << " max def\n" + << "boundx scalefactor div boundy scalefactor div scale\n"; + + t << "\n% ----- classes -----\n\n"; + base->drawBoxes(t,0,TRUE,FALSE,baseRows,superRows,0,0); + super->drawBoxes(t,0,FALSE,FALSE,baseRows,superRows,0,0); + + t << "\n% ----- relations -----\n\n"; + base->drawConnectors(t,0,TRUE,FALSE,baseRows,superRows,0,0); + super->drawConnectors(t,0,FALSE,FALSE,baseRows,superRows,0,0); +} + + +void ClassDiagram::writeImageMap(QTextStream &t,const char *path, + const char *fileName) +{ + uint baseRows=base->computeRows(); + uint superRows=super->computeRows(); + uint rows=baseRows+superRows-1; + +// t << "<p>Class diagram for "; +// HtmlGenerator::docifyStatic(t,name); +// t << ".\n<p><center><img src=\"" +// << fileName << ".gif\" usemap=\"#" << name << "_map\"" +// << " border=\"0\"></center>" << endl +// << "<map name=\"" << name << "_map\"><p>" << endl; + + uint lb,ls,xb,xs; + base->computeExtremes(&lb,&xb); + super->computeExtremes(&ls,&xs); + + uint cellWidth = QMAX(lb,ls)+labelHorMargin*2; + uint maxXPos = QMAX(xb,xs); + uint labelVertMargin = 6; //QMAX(6,(cellWidth-fontHeight)/6); // aspect at least 1:3 + uint cellHeight = labelVertMargin*2+fontHeight; + uint imageWidth = (maxXPos+gridWidth)*cellWidth/gridWidth+ + (maxXPos*labelHorSpacing)/gridWidth; + uint imageHeight = rows*cellHeight+(rows-1)*labelVertSpacing; + + Image image(imageWidth,imageHeight); + + base->drawBoxes(t,&image,TRUE,TRUE,baseRows,superRows,cellWidth,cellHeight); + super->drawBoxes(t,&image,FALSE,TRUE,baseRows,superRows,cellWidth,cellHeight); + base->drawConnectors(t,&image,TRUE,TRUE,baseRows,superRows,cellWidth,cellHeight); + super->drawConnectors(t,&image,FALSE,TRUE,baseRows,superRows,cellWidth,cellHeight); + + image.save((QString)path+"/"+fileName+".gif"); + + t << "</MAP></P>" << endl; +} + |