1 /******************************************************************************
6 * Copyright (C) 1997-2015 by Dimitri van Heesch.
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation under the terms of the GNU General Public License is hereby
10 * granted. No representations are made about the suitability of this software
11 * for any purpose. It is provided "as is" without express or implied warranty.
12 * See the GNU General Public License for more details.
14 * Documents produced by Doxygen are derivative works derived from the
15 * input used in their production; they are not affected by this license.
23 #include "ftextstream.h"
35 #include "classlist.h"
37 //-----------------------------------------------------------------------------
39 class DiagramItemList;
41 /** Class representing a single node in the built-in class diagram */
45 DiagramItem(DiagramItem *p,int number,ClassDef *cd,
46 Protection prot,Specifier virt,const char *ts);
48 QCString label() const;
49 QCString fileName() const;
50 DiagramItem *parentItem() { return parent; }
51 DiagramItemList *getChildren() { return children; }
52 void move(int dx,int dy) { x+=dx; y+=dy; }
53 int xPos() const { return x; }
54 int yPos() const { return y; }
55 int avgChildPos() const;
56 int numChildren() const;
57 void addChild(DiagramItem *di);
58 int number() const { return num; }
59 Protection protection() const { return prot; }
60 Specifier virtualness() const { return virt; }
61 void putInList() { inList=TRUE; }
62 bool isInList() const { return inList; }
63 ClassDef *getClassDef() const { return classDef; }
65 DiagramItemList *children;
76 /** Class representing a list of DiagramItem object. */
77 class DiagramItemList : public QList<DiagramItem>
80 DiagramItemList() : QList<DiagramItem>() {}
84 /** Class representing a row in the built-in class diagram */
85 class DiagramRow : public QList<DiagramItem>
88 DiagramRow(TreeDiagram *d,int l) : QList<DiagramItem>()
94 void insertClass(DiagramItem *parent,ClassDef *cd,bool doBases,
95 Protection prot,Specifier virt,const char *ts);
96 uint number() { return level; }
102 /** Class representing iterator for the rows in the built-in class diagram. */
103 class DiagramRowIterator : public QListIterator<DiagramRow>
106 DiagramRowIterator(const QList<DiagramRow> &d)
107 : QListIterator<DiagramRow>(d) {}
110 /** Class represeting the tree layout for the built-in class diagram. */
111 class TreeDiagram : public QList<DiagramRow>
114 TreeDiagram(ClassDef *root,bool doBases);
116 void computeLayout();
118 //uint computeCols();
119 void moveChildren(DiagramItem *root,int dx);
120 void computeExtremes(uint *labelWidth,uint *xpos);
121 void drawBoxes(FTextStream &t,Image *image,
122 bool doBase,bool bitmap,
123 uint baseRows,uint superRows,
124 uint cellWidth,uint cellHeight,
126 bool generateMap=TRUE);
127 void drawConnectors(FTextStream &t,Image *image,
128 bool doBase,bool bitmap,
129 uint baseRows,uint superRows,
130 uint cellWidth,uint cellheight);
132 bool layoutTree(DiagramItem *root,int row);
133 TreeDiagram &operator=(const TreeDiagram &);
134 TreeDiagram(const TreeDiagram &);
139 //-----------------------------------------------------------------------------
141 const uint maxTreeWidth = 8;
142 const int gridWidth = 100;
143 const int gridHeight = 100;
145 const uint labelHorSpacing = 10; // horizontal distance between labels
146 const uint labelVertSpacing = 32; // vertical distance between labels
147 const uint labelHorMargin = 6; // horiz. spacing between label and box
148 const uint fontHeight = 12; // height of a character
150 //static QCString escapeLatex(const char *s)
156 // if (c=='_') result+="\\_";
162 static uint protToMask(Protection p)
166 case Public: return 0xffffffff;
167 case Package: // package is not possible!
168 case Protected: return 0xcccccccc;
169 case Private: return 0xaaaaaaaa;
174 static uint protToColor(Protection p)
178 case Public: return 6;
179 case Package: // package is not possible!
180 case Protected: return 5;
181 case Private: return 4;
186 static QCString protToString(Protection p)
190 case Public: return "solid";
191 case Package: // package is not possible!
192 case Protected: return "dashed";
193 case Private: return "dotted";
198 static uint virtToMask(Specifier p)
202 case Normal: return 0xffffffff;
203 case Virtual: return 0xf0f0f0f0;
209 // pre: dil is not empty
210 static Protection getMinProtectionLevel(DiagramItemList *dil)
212 QListIterator<DiagramItem> it(*dil);
213 DiagramItem *di=it.current();
214 Protection result=di->protection();
215 for (++it;(di=it.current());++it)
217 Protection p=di->protection();
220 if (result==Protected && p==Public) result=p;
221 else if (result==Private) result=p;
227 static void writeBitmapBox(DiagramItem *di,Image *image,
228 int x,int y,int w,int h,bool firstRow,
229 bool hasDocs,bool children=FALSE)
231 int colFill = hasDocs ? (firstRow ? 0 : 2) : 7;
232 int colBorder = (firstRow || !hasDocs) ? 1 : 3;
233 int l = Image::stringLength(di->label());
234 uint mask=virtToMask(di->virtualness());
235 image->fillRect(x+1,y+1,w-2,h-2,colFill,mask);
236 image->drawRect(x,y,w,h,colBorder,mask);
237 image->writeString(x+(w-l)/2, y+(h-fontHeight)/2, di->label(),1);
242 image->drawHorzLine(y+h+i-6,x+w-2-i,x+w-2,firstRow?1:3,0xffffffff);
246 static void writeVectorBox(FTextStream &t,DiagramItem *di,
247 float x,float y,bool children=FALSE)
249 if (di->virtualness()==Virtual) t << "dashed\n";
250 t << " (" << di->label() << ") " << x << " " << y << " box\n";
251 if (children) t << x << " " << y << " mark\n";
252 if (di->virtualness()==Virtual) t << "solid\n";
255 static void writeMapArea(FTextStream &t,ClassDef *cd,QCString relPath,
256 int x,int y,int w,int h)
258 if (cd->isLinkable())
260 QCString ref=cd->getReference();
264 t << externalLinkTarget() << externalRef(relPath,ref,FALSE);
267 t << externalRef(relPath,ref,TRUE);
268 t << cd->getOutputFileBase() << Doxygen::htmlFileExtension;
269 if (!cd->anchor().isEmpty())
271 t << "#" << cd->anchor();
274 QCString tooltip = cd->briefDescriptionAsTooltip();
275 if (!tooltip.isEmpty())
277 t << "title=\"" << convertToHtml(tooltip) << "\" ";
279 t << "alt=\"" << convertToXML(cd->displayName());
280 t << "\" shape=\"rect\" coords=\"" << x << "," << y << ",";
281 t << (x+w) << "," << (y+h) << "\"/>" << endl;
284 //-----------------------------------------------------------------------------
286 DiagramItem::DiagramItem(DiagramItem *p,int number,ClassDef *cd,
287 Protection pr,Specifier vi,const char *ts)
293 children = new DiagramItemList;
301 DiagramItem::~DiagramItem()
306 QCString DiagramItem::label() const
309 if (!templSpec.isEmpty())
311 // we use classDef->name() here and not diplayName() in order
312 // to get the name used in the inheritance relation.
313 QCString n = classDef->name();
314 if (/*n.right(2)=="-g" ||*/ n.right(2)=="-p")
316 n = n.left(n.length()-2);
318 result=insertTemplateSpecifierInScope(n,templSpec);
322 result=classDef->displayName();
324 if (Config_getBool(HIDE_SCOPE_NAMES)) result=stripScope(result);
328 QCString DiagramItem::fileName() const
330 return classDef->getOutputFileBase();
333 int DiagramItem::avgChildPos() const
336 int c=children->count();
337 if (c==0) // no children -> don't move
339 if ((di=children->getFirst())->isInList()) // children should be in a list
341 if (c&1) // odd number of children -> get pos of middle child
342 return children->at(c/2)->xPos();
343 else // even number of children -> get middle of most middle children
344 return (children->at(c/2-1)->xPos()+children->at(c/2)->xPos())/2;
347 int DiagramItem::numChildren() const
349 return children->count();
352 void DiagramItem::addChild(DiagramItem *di)
354 children->append(di);
357 void DiagramRow::insertClass(DiagramItem *parent,ClassDef *cd,bool doBases,
358 Protection prot,Specifier virt,const char *ts)
360 //if (cd->visited) return; // the visit check does not work in case of
361 // multiple inheritance of the same class!
362 DiagramItem *di=new DiagramItem(parent, diagram->at(level)->count(),
365 if (parent) parent->addChild(di);
366 di->move(count()*gridWidth,level*gridHeight);
368 BaseClassList *bcl=doBases ? cd->baseClasses() : cd->subClasses();
372 /* there are base/sub classes */
373 BaseClassListIterator it(*bcl);
375 for (;(bcd=it.current());++it)
377 ClassDef *ccd=bcd->classDef;
378 if (ccd && ccd->isVisibleInHierarchy() /*&& !ccd->visited*/) count++;
381 if (count>0 && (prot!=Private || !doBases))
384 if (diagram->count()<=level+1) /* add new row */
386 row = new DiagramRow(diagram,level+1);
387 diagram->append(row);
389 else /* get next row */
391 row=diagram->at(level+1);
393 /* insert base classes in the next row */
394 BaseClassListIterator it(*bcl);
396 for (;(bcd=it.current());++it)
398 ClassDef *ccd=bcd->classDef;
399 if (ccd && ccd->isVisibleInHierarchy() /*&& !ccd->visited*/)
401 row->insertClass(di,ccd,doBases,bcd->prot,
402 doBases?bcd->virt:Normal,
403 doBases?bcd->templSpecifiers.data():"");
409 TreeDiagram::TreeDiagram(ClassDef *root,bool doBases)
412 DiagramRow *row=new DiagramRow(this,0);
414 row->insertClass(0,root,doBases,Public,Normal,0);
417 TreeDiagram::~TreeDiagram()
422 void TreeDiagram::moveChildren(DiagramItem *root,int dx)
424 DiagramItemList *dil=root->getChildren();
425 QListIterator<DiagramItem> it(*dil);
427 for (;(di=it.current());++it)
434 bool TreeDiagram::layoutTree(DiagramItem *root,int r)
437 //printf("layoutTree(%s,%d)\n",root->label().data(),r);
439 DiagramItemList *dil=root->getChildren();
443 int pPos=root->xPos();
444 int cPos=root->avgChildPos();
445 if (pPos>cPos) // move children
447 DiagramRow *row=at(r+1);
448 //printf("Moving children %d-%d in row %d\n",
449 // dil->getFirst()->number(),row->count()-1,r+1);
450 for (k=dil->getFirst()->number();k<row->count();k++)
451 row->at(k)->move(pPos-cPos,0);
454 else if (pPos<cPos) // move parent
456 DiagramRow *row=at(r);
457 //printf("Moving parents %d-%d in row %d\n",
458 // root->number(),row->count()-1,r);
459 for (k=root->number();k<row->count();k++)
460 row->at(k)->move(cPos-pPos,0);
464 // recurse to children
465 QListIterator<DiagramItem> it(*dil);
467 for (;(di=it.current()) && !moved && !di->isInList();++it)
469 moved = layoutTree(di,r+1);
475 void TreeDiagram::computeLayout()
477 QListIterator<DiagramRow> it(*this);
479 for (;(row=it.current()) && row->count()<maxTreeWidth;++it) {}
482 //printf("computeLayout() list row at %d\n",row->number());
483 QListIterator<DiagramItem> rit(*row);
488 for (;(di=rit.current());++rit)
490 DiagramItem *pi=di->parentItem();
491 if (pi==opi && !first) { delta-=gridWidth; }
494 di->move(delta,0); // collapse all items in the same
495 // list (except the first)
500 // re-organize the diagram items
501 DiagramItem *root=getFirst()->getFirst();
502 while (layoutTree(root,0)) { }
504 // move first items of the lists
507 QListIterator<DiagramItem> rit(*row);
509 while ((di=rit.current()))
511 DiagramItem *pi=di->parentItem();
512 if (pi->getChildren()->count()>1)
514 di->move(gridWidth,0);
515 while (di && di->parentItem()==pi) { ++rit; di=rit.current(); }
525 uint TreeDiagram::computeRows()
527 //printf("TreeDiagram::computeRows()=%d\n",count());
529 QListIterator<DiagramRow> it(*this);
531 for (;(row=it.current()) && !row->getFirst()->isInList();++it)
535 //printf("count=%d row=%p\n",count,row);
541 QListIterator<DiagramItem> rit(*row);
543 for (;(di=rit.current());++rit)
545 if (di->parentItem()!=opi) curListLen=1; else curListLen++;
546 if (curListLen>maxListLen) maxListLen=curListLen;
547 opi=di->parentItem();
549 //printf("maxListLen=%d\n",maxListLen);
555 void TreeDiagram::computeExtremes(uint *maxLabelLen,uint *maxXPos)
558 QListIterator<DiagramRow> it(*this);
561 for (;(dr=it.current()) && !done;++it)
563 QListIterator<DiagramItem> rit(*dr);
565 for (;(di=rit.current());++rit)
567 if (di->isInList()) done=TRUE;
568 if (maxXPos) mx=QMAX(mx,(uint)di->xPos());
569 if (maxLabelLen) ml=QMAX(ml,Image::stringLength(di->label()));
572 if (maxLabelLen) *maxLabelLen=ml;
573 if (maxXPos) *maxXPos=mx;
576 void TreeDiagram::drawBoxes(FTextStream &t,Image *image,
577 bool doBase,bool bitmap,
578 uint baseRows,uint superRows,
579 uint cellWidth,uint cellHeight,
583 QListIterator<DiagramRow> it(*this);
587 bool firstRow = doBase;
588 for (;(dr=it.current()) && !done;++it)
591 float xf=0.0f,yf=0.0f;
592 QListIterator<DiagramItem> rit(*dr);
593 DiagramItem *di = rit.current();
594 if (di->isInList()) // put boxes in a list
597 if (doBase) rit.toLast(); else rit.toFirst();
598 while ((di=rit.current()))
600 if (di->parentItem()==opi)
604 if (doBase) y -= cellHeight+labelVertSpacing;
605 else y += cellHeight+labelVertSpacing;
609 if (doBase) yf += 1.0f;
617 x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth;
620 y = image->getHeight()-
621 superRows*cellHeight-
622 (superRows-1)*labelVertSpacing-
623 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
627 y = (baseRows-1)*(cellHeight+labelVertSpacing)+
628 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
633 xf = di->xPos()/(float)gridWidth;
636 yf = di->yPos()/(float)gridHeight+superRows-1;
640 yf = superRows-1-di->yPos()/(float)gridHeight;
644 opi=di->parentItem();
648 bool hasDocs=di->getClassDef()->isLinkable();
649 writeBitmapBox(di,image,x,y,cellWidth,cellHeight,firstRow,
650 hasDocs,di->getChildren()->count()>0);
651 if (!firstRow && generateMap)
652 writeMapArea(t,di->getClassDef(),relPath,x,y,cellWidth,cellHeight);
656 writeVectorBox(t,di,xf,yf,di->getChildren()->count()>0);
659 if (doBase) --rit; else ++rit;
663 else // draw a tree of boxes
665 for (rit.toFirst();(di=rit.current());++rit)
669 x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth;
672 y = image->getHeight()-
673 superRows*cellHeight-
674 (superRows-1)*labelVertSpacing-
675 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
679 y = (baseRows-1)*(cellHeight+labelVertSpacing)+
680 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
682 bool hasDocs=di->getClassDef()->isLinkable();
683 writeBitmapBox(di,image,x,y,cellWidth,cellHeight,firstRow,hasDocs);
684 if (!firstRow && generateMap)
685 writeMapArea(t,di->getClassDef(),relPath,x,y,cellWidth,cellHeight);
689 xf=di->xPos()/(float)gridWidth;
692 yf = di->yPos()/(float)gridHeight+superRows-1;
696 yf = superRows-1-di->yPos()/(float)gridHeight;
698 writeVectorBox(t,di,xf,yf);
706 void TreeDiagram::drawConnectors(FTextStream &t,Image *image,
707 bool doBase,bool bitmap,
708 uint baseRows,uint superRows,
709 uint cellWidth,uint cellHeight)
711 QListIterator<DiagramRow> it(*this);
714 for (;(dr=it.current()) && !done;++it) // for each row
716 QListIterator<DiagramItem> rit(*dr);
717 DiagramItem *di = rit.current();
718 if (di->isInList()) // row consists of list connectors
721 float xf=0.0f,yf=0.0f,ysf=0.0f;
722 for (;(di=rit.current());++rit)
724 DiagramItem *pi=di->parentItem();
725 DiagramItemList *dil=pi->getChildren();
726 DiagramItem *last=dil->getLast();
727 if (di==last) // single child
729 if (bitmap) // draw pixels
731 x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2;
732 if (doBase) // base classes
734 y = image->getHeight()-
735 (superRows-1)*(cellHeight+labelVertSpacing)-
736 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
737 image->drawVertArrow(x,y,y+labelVertSpacing/2,
738 protToColor(di->protection()),
739 protToMask(di->protection()));
741 else // super classes
743 y = (baseRows-1)*(cellHeight+labelVertSpacing)-
745 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
746 image->drawVertLine(x,y,y+labelVertSpacing/2,
747 protToColor(di->protection()),
748 protToMask(di->protection()));
753 t << protToString(di->protection()) << endl;
756 t << "1 " << (di->xPos()/(float)gridWidth) << " "
757 << (di->yPos()/(float)gridHeight+superRows-1) << " in\n";
761 t << "0 " << (di->xPos()/(float)gridWidth) << " "
762 << ((float)superRows-0.25-di->yPos()/(float)gridHeight)
767 else // multiple children, put them in a vertical list
771 x = di->parentItem()->xPos()*
772 (cellWidth+labelHorSpacing)/gridWidth+cellWidth/2;
773 if (doBase) // base classes
775 ys = image->getHeight()-
776 (superRows-1)*(cellHeight+labelVertSpacing)-
777 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
778 y = ys - cellHeight/2;
780 else // super classes
782 ys = (baseRows-1)*(cellHeight+labelVertSpacing)+
783 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
784 y = ys + cellHeight/2;
789 xf = di->parentItem()->xPos()/(float)gridWidth;
792 ysf = di->yPos()/(float)gridHeight+superRows-1;
797 ysf = (float)superRows-0.25f-di->yPos()/(float)gridHeight;
801 while (di!=last) // more children to add
805 if (doBase) // base classes
807 image->drawHorzArrow(y,x,x+cellWidth/2+labelHorSpacing,
808 protToColor(di->protection()),
809 protToMask(di->protection()));
810 y -= cellHeight+labelVertSpacing;
812 else // super classes
814 image->drawHorzLine(y,x,x+cellWidth/2+labelHorSpacing,
815 protToColor(di->protection()),
816 protToMask(di->protection()));
817 y += cellHeight+labelVertSpacing;
822 t << protToString(di->protection()) << endl;
825 t << "1 " << xf << " " << yf << " hedge\n";
830 t << "0 " << xf << " " << yf << " hedge\n";
834 ++rit; di=rit.current();
836 // add last horizonal line and a vertical connection line
839 if (doBase) // base classes
841 image->drawHorzArrow(y,x,x+cellWidth/2+labelHorSpacing,
842 protToColor(di->protection()),
843 protToMask(di->protection()));
844 image->drawVertLine(x,y,ys+labelVertSpacing/2,
845 protToColor(getMinProtectionLevel(dil)),
846 protToMask(getMinProtectionLevel(dil)));
848 else // super classes
850 image->drawHorzLine(y,x,x+cellWidth/2+labelHorSpacing,
851 protToColor(di->protection()),
852 protToMask(di->protection()));
853 image->drawVertLine(x,ys-labelVertSpacing/2,y,
854 protToColor(getMinProtectionLevel(dil)),
855 protToMask(getMinProtectionLevel(dil)));
860 t << protToString(di->protection()) << endl;
863 t << "1 " << xf << " " << yf << " hedge\n";
867 t << "0 " << xf << " " << yf << " hedge\n";
869 t << protToString(getMinProtectionLevel(dil)) << endl;
872 t << xf << " " << ysf << " " << yf << " vedge\n";
876 t << xf << " " << (ysf + 0.25) << " " << yf << " vedge\n";
881 done=TRUE; // the tree is drawn now
883 else // normal tree connector
885 for (;(di=rit.current());++rit)
888 DiagramItemList *dil = di->getChildren();
889 DiagramItem *parent = di->parentItem();
890 if (parent) // item has a parent -> connect to it
892 if (bitmap) // draw pixels
894 x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2;
895 if (doBase) // base classes
897 y = image->getHeight()-
898 (superRows-1)*(cellHeight+labelVertSpacing)-
899 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
900 /* write input line */
901 image->drawVertArrow(x,y,y+labelVertSpacing/2,
902 protToColor(di->protection()),
903 protToMask(di->protection()));
905 else // super classes
907 y = (baseRows-1)*(cellHeight+labelVertSpacing)-
909 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
910 /* write output line */
911 image->drawVertLine(x,y,y+labelVertSpacing/2,
912 protToColor(di->protection()),
913 protToMask(di->protection()));
918 t << protToString(di->protection()) << endl;
921 t << "1 " << di->xPos()/(float)gridWidth << " "
922 << (di->yPos()/(float)gridHeight+superRows-1) << " in\n";
926 t << "0 " << di->xPos()/(float)gridWidth << " "
927 << ((float)superRows-0.25-di->yPos()/(float)gridHeight)
934 Protection p=getMinProtectionLevel(dil);
935 uint mask=protToMask(p);
936 uint col=protToColor(p);
939 x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2;
940 if (doBase) // base classes
942 y = image->getHeight()-
943 (superRows-1)*(cellHeight+labelVertSpacing)-
944 cellHeight-labelVertSpacing/2-
945 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
946 image->drawVertLine(x,y,y+labelVertSpacing/2-1,col,mask);
948 else // super classes
950 y = (baseRows-1)*(cellHeight+labelVertSpacing)+
952 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
953 image->drawVertArrow(x,y,y+labelVertSpacing/2-1,col,mask);
958 t << protToString(p) << endl;
961 t << "0 " << di->xPos()/(float)gridWidth << " "
962 << (di->yPos()/(float)gridHeight+superRows-1) << " out\n";
966 t << "1 " << di->xPos()/(float)gridWidth << " "
967 << ((float)superRows-1.75-di->yPos()/(float)gridHeight)
971 /* write input line */
972 DiagramItem *first = dil->getFirst();
973 DiagramItem *last = dil->getLast();
974 if (first!=last && !first->isInList()) /* connect with all base classes */
978 int xs = first->xPos()*(cellWidth+labelHorSpacing)/gridWidth
980 int xe = last->xPos()*(cellWidth+labelHorSpacing)/gridWidth
982 if (doBase) // base classes
984 image->drawHorzLine(y,xs,xe,col,mask);
986 else // super classes
988 image->drawHorzLine(y+labelVertSpacing/2,xs,xe,col,mask);
993 t << protToString(p) << endl;
996 t << first->xPos()/(float)gridWidth << " "
997 << last->xPos()/(float)gridWidth << " "
998 << (first->yPos()/(float)gridHeight+superRows-1)
1003 t << first->xPos()/(float)gridWidth << " "
1004 << last->xPos()/(float)gridWidth << " "
1005 << ((float)superRows-first->yPos()/(float)gridHeight)
1017 void clearVisitFlags()
1019 ClassSDict::Iterator cli(*Doxygen::classSDict);
1021 for (;(cd=cli.current());++cli)
1027 ClassDiagram::ClassDiagram(ClassDef *root)
1030 base = new TreeDiagram(root,TRUE);
1031 base->computeLayout();
1033 super = new TreeDiagram(root,FALSE);
1034 super->computeLayout();
1035 DiagramItem *baseItem = base->getFirst()->getFirst();
1036 DiagramItem *superItem = super->getFirst()->getFirst();
1037 int xbase = baseItem->xPos();
1038 int xsuper = superItem->xPos();
1041 superItem->move(xbase-xsuper,0);
1042 super->moveChildren(superItem,xbase-xsuper);
1044 else if (xbase<xsuper)
1046 baseItem->move(xsuper-xbase,0);
1047 base->moveChildren(baseItem,xsuper-xbase);
1051 ClassDiagram::~ClassDiagram()
1057 void ClassDiagram::writeFigure(FTextStream &output,const char *path,
1058 const char *fileName) const
1060 uint baseRows=base->computeRows();
1061 uint superRows=super->computeRows();
1062 uint baseMaxX, baseMaxLabelWidth, superMaxX, superMaxLabelWidth;
1063 base->computeExtremes(&baseMaxLabelWidth,&baseMaxX);
1064 super->computeExtremes(&superMaxLabelWidth,&superMaxX);
1066 uint rows=baseRows+superRows-1;
1067 uint cols=(QMAX(baseMaxX,superMaxX)+gridWidth*2-1)/gridWidth;
1069 // Estimate the image aspect width and height in pixels.
1070 uint estHeight = rows*40;
1071 uint estWidth = cols*(20+QMAX(baseMaxLabelWidth,superMaxLabelWidth));
1072 //printf("Estimated size %d x %d\n",estWidth,estHeight);
1074 const float pageWidth = 14.0f; // estimated page width in cm.
1075 // Somewhat lower to deal with estimation
1078 // compute the image height in centimeters based on the estimates
1079 float realHeight = QMIN(rows,12); // real height in cm
1080 float realWidth = realHeight * estWidth/(float)estHeight;
1081 if (realWidth>pageWidth) // assume that the page width is about 15 cm
1083 realHeight*=pageWidth/realWidth;
1084 realWidth=pageWidth;
1088 output << "\\begin{figure}[H]\n"
1091 output << "\\includegraphics[height=" << realHeight << "cm]{"
1092 << fileName << "}" << endl;
1093 output << "\\end{center}\n"
1096 //printf("writeFigure rows=%d cols=%d\n",rows,cols);
1098 QCString epsBaseName=(QCString)path+"/"+fileName;
1099 QCString epsName=epsBaseName+".eps";
1101 f1.setName(epsName.data());
1102 if (!f1.open(IO_WriteOnly))
1104 err("Could not open file %s for writing\n",f1.name().data());
1109 //printf("writeEPS() rows=%d cols=%d\n",rows,cols);
1111 // generate EPS header and postscript variables and procedures
1113 t << "%!PS-Adobe-2.0 EPSF-2.0\n";
1114 t << "%%Title: ClassName\n";
1115 t << "%%Creator: Doxygen\n";
1116 t << "%%CreationDate: Time\n";
1118 t << "%Magnification: 1.00\n";
1119 t << "%%Orientation: Portrait\n";
1120 t << "%%BoundingBox: 0 0 500 " << estHeight*500.0/(float)estWidth << "\n";
1121 t << "%%Pages: 0\n";
1122 t << "%%BeginSetup\n";
1123 t << "%%EndSetup\n";
1124 t << "%%EndComments\n";
1126 t << "% ----- variables -----\n";
1128 t << "/boxwidth 0 def\n";
1129 t << "/boxheight 40 def\n";
1130 t << "/fontheight 24 def\n";
1131 t << "/marginwidth 10 def\n";
1132 t << "/distx 20 def\n";
1133 t << "/disty 40 def\n";
1134 t << "/boundaspect " << estWidth/(float)estHeight << " def % aspect ratio of the BoundingBox (width/height)\n";
1135 t << "/boundx 500 def\n";
1136 t << "/boundy boundx boundaspect div def\n";
1137 t << "/xspacing 0 def\n";
1138 t << "/yspacing 0 def\n";
1139 t << "/rows " << rows << " def\n";
1140 t << "/cols " << cols << " def\n";
1141 t << "/scalefactor 0 def\n";
1142 t << "/boxfont /Times-Roman findfont fontheight scalefont def\n";
1144 t << "% ----- procedures -----\n";
1146 t << "/dotted { [1 4] 0 setdash } def\n";
1147 t << "/dashed { [5] 0 setdash } def\n";
1148 t << "/solid { [] 0 setdash } def\n";
1150 t << "/max % result = MAX(arg1,arg2)\n";
1152 t << " /a exch def\n";
1153 t << " /b exch def\n";
1154 t << " a b gt {a} {b} ifelse\n";
1157 t << "/xoffset % result = MAX(0,(scalefactor-(boxwidth*cols+distx*(cols-1)))/2)\n";
1159 t << " 0 scalefactor boxwidth cols mul distx cols 1 sub mul add sub 2 div max\n";
1162 t << "/cw % boxwidth = MAX(boxwidth, stringwidth(arg1))\n";
1164 t << " /str exch def\n";
1165 t << " /boxwidth boxwidth str stringwidth pop max def\n";
1168 t << "/box % draws a box with text `arg1' at grid pos (arg2,arg3)\n";
1170 t << " 2 setlinewidth\n";
1172 t << " exch xspacing mul xoffset add\n";
1173 t << " exch yspacing mul\n";
1175 t << " boxwidth 0 rlineto \n";
1176 t << " 0 boxheight rlineto \n";
1177 t << " boxwidth neg 0 rlineto \n";
1178 t << " 0 boxheight neg rlineto \n";
1179 t << " closepath\n";
1180 t << " dup stringwidth pop neg boxwidth add 2 div\n";
1181 t << " boxheight fontheight 2 div sub 2 div\n";
1182 t << " rmoveto show stroke\n";
1188 t << " exch xspacing mul xoffset add boxwidth add\n";
1189 t << " exch yspacing mul\n";
1191 t << " 0 boxheight 4 div rlineto\n";
1192 t << " boxheight neg 4 div boxheight neg 4 div rlineto\n";
1193 t << " closepath\n";
1201 t << " 3 -8 rlineto\n";
1202 t << " -6 0 rlineto\n";
1203 t << " 3 8 rlineto\n";
1204 t << " closepath\n";
1209 t << "/out % draws an output connector for the block at (arg1,arg2)\n";
1212 t << " exch xspacing mul xoffset add boxwidth 2 div add\n";
1213 t << " exch yspacing mul boxheight add\n";
1214 t << " /y exch def\n";
1215 t << " /x exch def\n";
1216 t << " x y moveto\n";
1217 t << " 0 disty 2 div rlineto \n";
1219 t << " 1 eq { x y disty 2 div add arrow } if\n";
1222 t << "/in % draws an input connector for the block at (arg1,arg2)\n";
1225 t << " exch xspacing mul xoffset add boxwidth 2 div add\n";
1226 t << " exch yspacing mul disty 2 div sub\n";
1227 t << " /y exch def\n";
1228 t << " /x exch def\n";
1229 t << " x y moveto\n";
1230 t << " 0 disty 2 div rlineto\n";
1232 t << " 1 eq { x y disty 2 div add arrow } if\n";
1237 t << " exch xspacing mul xoffset add boxwidth 2 div add\n";
1238 t << " exch yspacing mul boxheight 2 div sub\n";
1239 t << " /y exch def\n";
1240 t << " /x exch def\n";
1242 t << " x y moveto\n";
1243 t << " boxwidth 2 div distx add 0 rlineto\n";
1246 t << " { newpath x boxwidth 2 div distx add add y moveto\n";
1247 t << " -8 3 rlineto\n";
1248 t << " 0 -6 rlineto\n";
1249 t << " 8 3 rlineto\n";
1250 t << " closepath\n";
1258 t << " /ye exch def\n";
1259 t << " /ys exch def\n";
1260 t << " /xs exch def\n";
1262 t << " xs xspacing mul xoffset add boxwidth 2 div add dup\n";
1263 t << " ys yspacing mul boxheight 2 div sub\n";
1265 t << " ye yspacing mul boxheight 2 div sub\n";
1270 t << "/conn % connections the blocks from col `arg1' to `arg2' of row `arg3'\n";
1272 t << " /ys exch def\n";
1273 t << " /xe exch def\n";
1274 t << " /xs exch def\n";
1276 t << " xs xspacing mul xoffset add boxwidth 2 div add\n";
1277 t << " ys yspacing mul disty 2 div sub\n";
1279 t << " xspacing xe xs sub mul 0\n";
1284 t << "% ----- main ------\n";
1286 t << "boxfont setfont\n";
1287 t << "1 boundaspect scale\n";
1291 QListIterator<DiagramRow> bit(*base);
1293 for (;(dr=bit.current()) && !done;++bit)
1295 QListIterator<DiagramItem> rit(*dr);
1297 for (;(di=rit.current());++rit)
1299 done=di->isInList();
1300 t << "(" << di->label() << ") cw\n";
1303 QListIterator<DiagramRow> sit(*super);
1306 for (;(dr=sit.current()) && !done;++sit)
1308 QListIterator<DiagramItem> rit(*dr);
1310 for (;(di=rit.current());++rit)
1312 done=di->isInList();
1313 t << "(" << di->label() << ") cw\n";
1317 t << "/boxwidth boxwidth marginwidth 2 mul add def\n"
1318 << "/xspacing boxwidth distx add def\n"
1319 << "/yspacing boxheight disty add def\n"
1320 << "/scalefactor \n"
1321 << " boxwidth cols mul distx cols 1 sub mul add\n"
1322 << " boxheight rows mul disty rows 1 sub mul add boundaspect mul \n"
1324 << "boundx scalefactor div boundy scalefactor div scale\n";
1326 t << "\n% ----- classes -----\n\n";
1327 base->drawBoxes(t,0,TRUE,FALSE,baseRows,superRows,0,0);
1328 super->drawBoxes(t,0,FALSE,FALSE,baseRows,superRows,0,0);
1330 t << "\n% ----- relations -----\n\n";
1331 base->drawConnectors(t,0,TRUE,FALSE,baseRows,superRows,0,0);
1332 super->drawConnectors(t,0,FALSE,FALSE,baseRows,superRows,0,0);
1335 if (Config_getBool(USE_PDFLATEX))
1337 QCString epstopdfArgs(4096);
1338 epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
1339 epsBaseName.data(),epsBaseName.data());
1340 //printf("Converting eps using `%s'\n",epstopdfArgs.data());
1341 portable_sysTimerStart();
1342 if (portable_system("epstopdf",epstopdfArgs)!=0)
1344 err("Problems running epstopdf. Check your TeX installation!\n");
1345 portable_sysTimerStop();
1348 portable_sysTimerStop();
1353 void ClassDiagram::writeImage(FTextStream &t,const char *path,
1354 const char *relPath,const char *fileName,
1355 bool generateMap) const
1357 uint baseRows=base->computeRows();
1358 uint superRows=super->computeRows();
1359 uint rows=baseRows+superRows-1;
1362 base->computeExtremes(&lb,&xb);
1363 super->computeExtremes(&ls,&xs);
1365 uint cellWidth = QMAX(lb,ls)+labelHorMargin*2;
1366 uint maxXPos = QMAX(xb,xs);
1367 uint labelVertMargin = 6; //QMAX(6,(cellWidth-fontHeight)/6); // aspect at least 1:3
1368 uint cellHeight = labelVertMargin*2+fontHeight;
1369 uint imageWidth = (maxXPos+gridWidth)*cellWidth/gridWidth+
1370 (maxXPos*labelHorSpacing)/gridWidth;
1371 uint imageHeight = rows*cellHeight+(rows-1)*labelVertSpacing;
1373 Image image(imageWidth,imageHeight);
1375 base->drawBoxes(t,&image,TRUE,TRUE,baseRows,superRows,cellWidth,cellHeight,relPath,generateMap);
1376 super->drawBoxes(t,&image,FALSE,TRUE,baseRows,superRows,cellWidth,cellHeight,relPath,generateMap);
1377 base->drawConnectors(t,&image,TRUE,TRUE,baseRows,superRows,cellWidth,cellHeight);
1378 super->drawConnectors(t,&image,FALSE,TRUE,baseRows,superRows,cellWidth,cellHeight);
1380 #define IMAGE_EXT ".png"
1381 image.save((QCString)path+"/"+fileName+IMAGE_EXT);
1382 Doxygen::indexList->addImageFile(QCString(fileName)+IMAGE_EXT);
1384 if (generateMap) t << "</map>" << endl;