1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2020 by Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <fstream>
19 #include <algorithm>
20 
21 #include "diagram.h"
22 #include "image.h"
23 #include "classdef.h"
24 #include "config.h"
25 #include "message.h"
26 #include "util.h"
27 #include "doxygen.h"
28 #include "portable.h"
29 #include "index.h"
30 #include "classlist.h"
31 #include "textstream.h"
32 
33 //-----------------------------------------------------------------------------
34 
35 class TreeDiagram;
36 class DiagramItem;
37 using DiagramItemList = std::vector<DiagramItem*>;
38 
39 /** Class representing a single node in the built-in class diagram */
40 class DiagramItem
41 {
42   public:
43     DiagramItem(DiagramItem *p,uint number,const ClassDef *cd,
44                 Protection prot,Specifier virt,const QCString &ts);
45     QCString label() const;
46     QCString fileName() const;
parentItem()47     DiagramItem *parentItem() { return m_parent; }
getChildren()48     DiagramItemList getChildren() { return m_children; }
move(int dx,int dy)49     void move(int dx,int dy) { m_x+=(uint)dx; m_y+=(uint)dy; }
xPos() const50     uint xPos() const { return m_x; }
yPos() const51     uint yPos() const { return m_y; }
52     uint avgChildPos() const;
53     uint numChildren() const;
54     void addChild(DiagramItem *di);
number() const55     uint number() const { return m_num; }
protection() const56     Protection protection() const { return m_prot; }
virtualness() const57     Specifier virtualness() const { return m_virt; }
putInList()58     void putInList() { m_inList=TRUE; }
isInList() const59     bool isInList() const { return m_inList; }
getClassDef() const60     const ClassDef *getClassDef() const { return m_classDef; }
61   private:
62     DiagramItemList m_children;
63     DiagramItem *m_parent;
64     uint m_x = 0;
65     uint m_y = 0;
66     uint m_num;
67     Protection m_prot;
68     Specifier m_virt;
69     QCString m_templSpec;
70     bool m_inList = false;
71     const ClassDef *m_classDef;
72 };
73 
74 /** Class representing a row in the built-in class diagram */
75 class DiagramRow
76 {
77   public:
78     using Ptr = std::unique_ptr<DiagramItem>;
79     using Vec = std::vector<Ptr>;
80     using iterator = typename Vec::iterator;
81     using reverse_iterator = typename Vec::reverse_iterator;
DiagramRow(TreeDiagram * d,uint l)82     DiagramRow(TreeDiagram *d,uint l) : m_diagram(d), m_level(l) {}
83     void insertClass(DiagramItem *parent,const ClassDef *cd,bool doBases,
84                      Protection prot,Specifier virt,const QCString &ts);
number()85     uint number() { return m_level; }
86 
item(int index)87     DiagramItem *item(int index) { return m_items.at(index).get(); }
numItems() const88     uint numItems() const { return static_cast<uint>(m_items.size()); }
begin()89     iterator begin() { return m_items.begin();  }
end()90     iterator end()   { return m_items.end();    }
rbegin()91     reverse_iterator rbegin() { return m_items.rbegin();  }
rend()92     reverse_iterator rend()   { return m_items.rend();    }
93   private:
94     TreeDiagram *m_diagram;
95     uint m_level;
96     Vec m_items;
97 };
98 
99 /** Class representing the tree layout for the built-in class diagram. */
100 class TreeDiagram
101 {
102   public:
103     using Ptr = std::unique_ptr<DiagramRow>;
104     using Vec = std::vector<Ptr>;
105     using iterator = typename Vec::iterator;
106     TreeDiagram(const ClassDef *root,bool doBases);
107     void computeLayout();
108     uint computeRows();
109     void moveChildren(DiagramItem *root,int dx);
110     void computeExtremes(uint *labelWidth,uint *xpos);
111     void drawBoxes(TextStream &t,Image *image,
112                    bool doBase,bool bitmap,
113                    uint baseRows,uint superRows,
114                    uint cellWidth,uint cellHeight,
115                    QCString relPath="",
116                    bool generateMap=TRUE);
117     void drawConnectors(TextStream &t,Image *image,
118                    bool doBase,bool bitmap,
119                    uint baseRows,uint superRows,
120                    uint cellWidth,uint cellheight);
row(int index)121     DiagramRow *row(int index) { return m_rows.at(index).get(); }
numRows() const122     uint numRows() const { return static_cast<uint>(m_rows.size()); }
addRow(uint l)123     DiagramRow *addRow(uint l)
124     { m_rows.push_back(std::make_unique<DiagramRow>(this,l)); return m_rows.back().get(); }
begin()125     iterator begin() { return m_rows.begin();  }
end()126     iterator end()   { return m_rows.end();    }
127   private:
128     bool layoutTree(DiagramItem *root,uint row);
129     TreeDiagram &operator=(const TreeDiagram &);
130     TreeDiagram(const TreeDiagram &);
131     Vec m_rows;
132 };
133 
134 
135 
136 //-----------------------------------------------------------------------------
137 
138 const uint maxTreeWidth = 8;
139 const uint gridWidth  = 100;
140 const uint gridHeight = 100;
141 
142 const uint labelHorSpacing  = 10;  // horizontal distance between labels
143 const uint labelVertSpacing = 32;  // vertical distance between labels
144 const uint labelHorMargin   = 6;   // horiz. spacing between label and box
145 const uint fontHeight       = 12;  // height of a character
146 
protToMask(Protection p)147 static uint protToMask(Protection p)
148 {
149   switch(p)
150   {
151     case Public:    return 0xffffffff;
152     case Package: // package is not possible!
153     case Protected: return 0xcccccccc;
154     case Private:   return 0xaaaaaaaa;
155   }
156   return 0;
157 }
158 
protToColor(Protection p)159 static uchar protToColor(Protection p)
160 {
161   switch(p)
162   {
163     case Public:    return 6;
164     case Package: // package is not possible!
165     case Protected: return 5;
166     case Private:   return 4;
167   }
168   return 0;
169 }
170 
protToString(Protection p)171 static QCString protToString(Protection p)
172 {
173   switch(p)
174   {
175     case Public:    return "solid";
176     case Package: // package is not possible!
177     case Protected: return "dashed";
178     case Private:   return "dotted";
179   }
180   return QCString();
181 }
182 
virtToMask(Specifier p)183 static uint virtToMask(Specifier p)
184 {
185   switch(p)
186   {
187     case Normal:    return 0xffffffff;
188     case Virtual:   return 0xf0f0f0f0;
189     default:        return 0;
190   }
191   return 0;
192 }
193 
194 // pre: dil is not empty
getMinProtectionLevel(const DiagramItemList & dil)195 static Protection getMinProtectionLevel(const DiagramItemList &dil)
196 {
197   auto it = dil.begin();
198   Protection result = Private;
199   if (it!=dil.end())
200   {
201     result=(*it)->protection();
202     for (++it;it!=dil.end();++it)
203     {
204       Protection p=(*it)->protection();
205       if (p!=result)
206       {
207         if (result==Protected && p==Public) result=p;
208         else if (result==Private) result=p;
209       }
210     }
211   }
212   return result;
213 }
214 
writeBitmapBox(DiagramItem * di,Image * image,uint x,uint y,uint w,uint h,bool firstRow,bool hasDocs,bool children=FALSE)215 static void writeBitmapBox(DiagramItem *di,Image *image,
216                            uint x,uint y,uint w,uint h,bool firstRow,
217                            bool hasDocs,bool children=FALSE)
218 {
219   uchar colFill = hasDocs ? (firstRow ? 0 : 2) : 7;
220   uchar colBorder = (firstRow || !hasDocs) ? 1 : 3;
221   uint l = Image::stringLength(di->label());
222   uint mask=virtToMask(di->virtualness());
223   image->fillRect(x+1,y+1,w-2,h-2,colFill,mask);
224   image->drawRect(x,y,w,h,colBorder,mask);
225   image->writeString(x+(w-l)/2, y+(h-fontHeight)/2, di->label(),1);
226   if (children)
227   {
228     uint i;
229     for (i=0;i<5;i++)
230     {
231       image->drawHorzLine(y+h+i-6,x+w-2-i,x+w-2,firstRow?1:3,0xffffffff);
232     }
233   }
234 }
235 
writeVectorBox(TextStream & t,DiagramItem * di,float x,float y,bool children=FALSE)236 static void writeVectorBox(TextStream &t,DiagramItem *di,
237                            float x,float y,bool children=FALSE)
238 {
239   if (di->virtualness()==Virtual) t << "dashed\n";
240   t << " (" << convertToPSString(di->label()) << ") " << x << " " << y << " box\n";
241   if (children) t << x << " " << y << " mark\n";
242   if (di->virtualness()==Virtual) t << "solid\n";
243 }
244 
writeMapArea(TextStream & t,const ClassDef * cd,QCString relPath,uint x,uint y,uint w,uint h)245 static void writeMapArea(TextStream &t,const ClassDef *cd,QCString relPath,
246                          uint x,uint y,uint w,uint h)
247 {
248   if (cd->isLinkable())
249   {
250     QCString ref=cd->getReference();
251     t << "<area ";
252     if (!ref.isEmpty())
253     {
254       t << externalLinkTarget(true);
255     }
256     t << "href=\"";
257     t << externalRef(relPath,ref,TRUE);
258     t << addHtmlExtensionIfMissing(cd->getOutputFileBase());
259     if (!cd->anchor().isEmpty())
260     {
261       t << "#" << cd->anchor();
262     }
263     t << "\" ";
264     QCString tooltip = cd->briefDescriptionAsTooltip();
265     if (!tooltip.isEmpty())
266     {
267       t << "title=\"" << convertToHtml(tooltip) << "\" ";
268     }
269     t << "alt=\"" << convertToXML(cd->displayName());
270     t << "\" shape=\"rect\" coords=\"" << x << "," << y << ",";
271     t << (x+w) << "," << (y+h) << "\"/>\n";
272   }
273 }
274 //-----------------------------------------------------------------------------
275 
DiagramItem(DiagramItem * p,uint number,const ClassDef * cd,Protection pr,Specifier vi,const QCString & ts)276 DiagramItem::DiagramItem(DiagramItem *p,uint number,const ClassDef *cd,
277                          Protection pr,Specifier vi,const QCString &ts)
278   : m_parent(p), m_num(number), m_prot(pr), m_virt(vi), m_templSpec(ts), m_classDef(cd)
279 {
280 }
281 
label() const282 QCString DiagramItem::label() const
283 {
284   QCString result;
285   if (!m_templSpec.isEmpty())
286   {
287     // we use classDef->name() here and not displayName() in order
288     // to get the name used in the inheritance relation.
289     QCString n = m_classDef->name();
290     if (/*n.right(2)=="-g" ||*/ n.right(2)=="-p")
291     {
292       n = n.left(n.length()-2);
293     }
294     result=insertTemplateSpecifierInScope(n,m_templSpec);
295   }
296   else
297   {
298     result=m_classDef->displayName();
299   }
300   if (Config_getBool(HIDE_SCOPE_NAMES)) result=stripScope(result);
301   return result;
302 }
303 
fileName() const304 QCString DiagramItem::fileName() const
305 {
306   return m_classDef->getOutputFileBase();
307 }
308 
avgChildPos() const309 uint DiagramItem::avgChildPos() const
310 {
311   DiagramItem *di;
312   size_t c=m_children.size();
313   if (c==0) // no children -> don't move
314     return xPos();
315   if ((di=m_children.front())->isInList()) // children should be in a list
316     return di->xPos();
317   if (c&1) // odd number of children -> get pos of middle child
318     return m_children.at(c/2)->xPos();
319   else // even number of children -> get middle of most middle children
320     return (m_children.at(c/2-1)->xPos()+m_children.at(c/2)->xPos())/2;
321 }
322 
numChildren() const323 uint DiagramItem::numChildren() const
324 {
325   return static_cast<uint>(m_children.size());
326 }
327 
addChild(DiagramItem * di)328 void DiagramItem::addChild(DiagramItem *di)
329 {
330   m_children.push_back(di);
331 }
332 
333 //---------------------------------------------------------------------------
334 
insertClass(DiagramItem * parent,const ClassDef * cd,bool doBases,Protection prot,Specifier virt,const QCString & ts)335 void DiagramRow::insertClass(DiagramItem *parent,const ClassDef *cd,bool doBases,
336                              Protection prot,Specifier virt,const QCString &ts)
337 {
338   auto di = std::make_unique<DiagramItem>(parent, m_diagram->row(m_level)->numItems(),
339                                           cd,prot,virt,ts);
340   DiagramItem *di_ptr = di.get();
341   if (parent) parent->addChild(di_ptr);
342   di->move((int)(m_items.size()*gridWidth),(int)(m_level*gridHeight));
343   m_items.push_back(std::move(di));
344   int count=0;
345   for (const auto &bcd : doBases ? cd->baseClasses() : cd->subClasses())
346   {
347     /* there are base/sub classes */
348     ClassDef *ccd=bcd.classDef;
349     if (ccd && ccd->isVisibleInHierarchy()) count++;
350   }
351   if (count>0 && (prot!=Private || !doBases))
352   {
353     DiagramRow *row=0;
354     if (m_diagram->numRows()<=m_level+1) /* add new row */
355     {
356       row=m_diagram->addRow(m_level+1);
357     }
358     else /* get next row */
359     {
360       row=m_diagram->row(m_level+1);
361     }
362     for (const auto &bcd : doBases ? cd->baseClasses() : cd->subClasses())
363     {
364       ClassDef *ccd=bcd.classDef;
365       if (ccd && ccd->isVisibleInHierarchy())
366       {
367         row->insertClass(di_ptr,ccd,doBases,bcd.prot,
368             doBases?bcd.virt:Normal,
369             doBases?bcd.templSpecifiers:QCString());
370       }
371     }
372   }
373 }
374 
375 //---------------------------------------------------------------------------
376 
TreeDiagram(const ClassDef * root,bool doBases)377 TreeDiagram::TreeDiagram(const ClassDef *root,bool doBases)
378 {
379   auto row = std::make_unique<DiagramRow>(this,0);
380   DiagramRow *row_ptr = row.get();
381   m_rows.push_back(std::move(row));
382   row_ptr->insertClass(0,root,doBases,Public,Normal,QCString());
383 }
384 
moveChildren(DiagramItem * root,int dx)385 void TreeDiagram::moveChildren(DiagramItem *root,int dx)
386 {
387   for (const auto &di : root->getChildren())
388   {
389     di->move(dx,0);
390     moveChildren(di,dx);
391   }
392 }
393 
layoutTree(DiagramItem * root,uint r)394 bool TreeDiagram::layoutTree(DiagramItem *root,uint r)
395 {
396   bool moved=FALSE;
397   //printf("layoutTree(%s,%d)\n",qPrint(root->label()),r);
398 
399   if (root->numChildren()>0)
400   {
401     auto children = root->getChildren();
402     uint k;
403     uint pPos=root->xPos();
404     uint cPos=root->avgChildPos();
405     if (pPos>cPos) // move children
406     {
407       const auto &row=m_rows.at(r+1);
408       //printf("Moving children %d-%d in row %d\n",
409       //    dil->getFirst()->number(),row->count()-1,r+1);
410       for (k=children.front()->number();k<row->numItems();k++)
411         row->item(k)->move((int)(pPos-cPos),0);
412       moved=TRUE;
413     }
414     else if (pPos<cPos) // move parent
415     {
416       const auto &row=m_rows.at(r);
417       //printf("Moving parents %d-%d in row %d\n",
418       //    root->number(),row->count()-1,r);
419       for (k=root->number();k<row->numItems();k++)
420         row->item(k)->move((int)(cPos-pPos),0);
421       moved=TRUE;
422     }
423 
424     // recurse to children
425     auto it = children.begin();
426     for (;it!=children.end() && !moved && !(*it)->isInList();++it)
427     {
428       moved = layoutTree(*it,r+1);
429     }
430   }
431   return moved;
432 }
433 
computeLayout()434 void TreeDiagram::computeLayout()
435 {
436   auto it = m_rows.begin();
437   while (it!=m_rows.end() && (*it)->numItems()<maxTreeWidth) ++it;
438   if (it!=m_rows.end())
439   {
440     const auto &row = *it;
441     //printf("computeLayout() list row at %d\n",row->number());
442     DiagramItem *opi=0;
443     int delta=0;
444     bool first=TRUE;
445     for (const auto &di : *row)
446     {
447       DiagramItem *pi=di->parentItem();
448       if (pi==opi && !first) { delta-=gridWidth; }
449       first = pi!=opi;
450       opi=pi;
451       di->move(delta,0); // collapse all items in the same
452                          // list (except the first)
453       di->putInList();
454     }
455   }
456 
457   // re-organize the diagram items
458   DiagramItem *root=m_rows.front()->item(0);
459   while (layoutTree(root,0)) { }
460 
461   // move first items of the lists
462   if (it!=m_rows.end())
463   {
464     const auto &row = *it;
465     auto rit = row->begin();
466     while (rit!=row->end())
467     {
468       DiagramItem *pi=(*rit)->parentItem();
469       if (pi->numChildren()>1)
470       {
471         (*rit)->move(gridWidth,0);
472         while (rit!=row->end() && (*rit)->parentItem()==pi)
473         {
474           ++rit;
475         }
476       }
477       else
478       {
479         ++rit;
480       }
481     }
482   }
483 }
484 
computeRows()485 uint TreeDiagram::computeRows()
486 {
487   //printf("TreeDiagram::computeRows()=%d\n",count());
488   uint count=0;
489   auto it = m_rows.begin();
490   while (it!=m_rows.end() && !(*it)->item(0)->isInList())
491   {
492     ++it;
493     ++count;
494   }
495 
496   //printf("count=%d row=%p\n",count,row);
497   if (it!=m_rows.end())
498   {
499     const auto &row = *it;
500     uint maxListLen=0;
501     uint curListLen=0;
502     DiagramItem *opi=0;
503     for (const auto &di : *row) // for each item in a row
504     {
505       if (di->parentItem()!=opi) curListLen=1; else curListLen++;
506       if (curListLen>maxListLen) maxListLen=curListLen;
507       opi=di->parentItem();
508     }
509     //printf("maxListLen=%d\n",maxListLen);
510     count+=maxListLen;
511   }
512   return count;
513 }
514 
computeExtremes(uint * maxLabelLen,uint * maxXPos)515 void TreeDiagram::computeExtremes(uint *maxLabelLen,uint *maxXPos)
516 {
517   uint ml=0,mx=0;
518   for (const auto &dr : m_rows) // for each row
519   {
520     bool done=FALSE;
521     for (const auto &di : *dr) // for each item in a row
522     {
523       if (di->isInList()) done=TRUE;
524       if (maxXPos) mx=std::max(mx,(uint)di->xPos());
525       if (maxLabelLen) ml=std::max(ml,Image::stringLength(di->label()));
526     }
527     if (done) break;
528   }
529   if (maxLabelLen) *maxLabelLen=ml;
530   if (maxXPos)     *maxXPos=mx;
531 }
532 
533 //! helper class representing an iterator that can iterate forwards or backwards
534 template<class C,class I>
535 class DualDirIterator
536 {
537   public:
DualDirIterator(C & container,bool fwd)538     DualDirIterator(C &container,bool fwd)
539       : m_container(container), m_forward(fwd)
540     {
541       if (fwd) m_it = container.begin();
542       else m_rit = container.rbegin();
543     }
operator ++()544     void operator++()
545     {
546       if (m_forward) ++m_it++; else ++m_rit;
547     }
operator *()548     I &operator*()
549     {
550       return m_forward ? *m_it : *m_rit;
551     }
552 
atEnd()553     bool atEnd()
554     {
555       if (m_forward)
556         return m_it==m_container.end();
557       else
558         return m_rit==m_container.rend();
559     }
560 
561   private:
562     C &m_container;
563     bool m_forward;
564     typename C::iterator m_it;
565     typename C::reverse_iterator m_rit;
566 };
567 
drawBoxes(TextStream & t,Image * image,bool doBase,bool bitmap,uint baseRows,uint superRows,uint cellWidth,uint cellHeight,QCString relPath,bool generateMap)568 void TreeDiagram::drawBoxes(TextStream &t,Image *image,
569                             bool doBase,bool bitmap,
570                             uint baseRows,uint superRows,
571                             uint cellWidth,uint cellHeight,
572                             QCString relPath,
573                             bool generateMap)
574 {
575   auto it = m_rows.begin();
576   if (it!=m_rows.end() && !doBase) ++it;
577   bool firstRow = doBase;
578   bool done=FALSE;
579   for (;it!=m_rows.end() && !done;++it) // for each row
580   {
581     const auto &dr = *it;
582     uint x=0,y=0;
583     float xf=0.0f,yf=0.0f;
584     DiagramItem *firstDi = dr->item(0);
585     if (firstDi->isInList()) // put boxes in a list
586     {
587       DiagramItem *opi=0;
588       DualDirIterator<DiagramRow,const std::unique_ptr<DiagramItem>&> dit(*dr,!doBase);
589       while (!dit.atEnd())
590       {
591         DiagramItem *di = (*dit).get();
592         if (di->parentItem()==opi)
593         {
594           if (bitmap)
595           {
596             if (doBase) y -= cellHeight+labelVertSpacing;
597             else        y += cellHeight+labelVertSpacing;
598           }
599           else
600           {
601             if (doBase) yf += 1.0f;
602             else        yf -= 1.0f;
603           }
604         }
605         else
606         {
607           if (bitmap)
608           {
609             x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth;
610             if (doBase)
611             {
612               y = image->height()-
613                 superRows*cellHeight-
614                 (superRows-1)*labelVertSpacing-
615                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
616             }
617             else
618             {
619               y = (baseRows-1)*(cellHeight+labelVertSpacing)+
620                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
621             }
622           }
623           else
624           {
625             xf = di->xPos()/(float)gridWidth;
626             if (doBase)
627             {
628               yf = di->yPos()/(float)gridHeight+superRows-1;
629             }
630             else
631             {
632               yf = superRows-1-di->yPos()/(float)gridHeight;
633             }
634           }
635         }
636         opi=di->parentItem();
637 
638         if (bitmap)
639         {
640           bool hasDocs=di->getClassDef()->isLinkable();
641           writeBitmapBox(di,image,x,y,cellWidth,cellHeight,firstRow,
642               hasDocs,di->numChildren()>0);
643           if (!firstRow && generateMap)
644             writeMapArea(t,di->getClassDef(),relPath,x,y,cellWidth,cellHeight);
645         }
646         else
647         {
648           writeVectorBox(t,di,xf,yf,di->numChildren()>0);
649         }
650 
651         ++dit;
652       }
653       done=TRUE;
654     }
655     else // draw a tree of boxes
656     {
657       for (const auto &di : *dr)
658       {
659         if (bitmap)
660         {
661           x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth;
662           if (doBase)
663           {
664             y = image->height()-
665               superRows*cellHeight-
666               (superRows-1)*labelVertSpacing-
667               di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
668           }
669           else
670           {
671             y = (baseRows-1)*(cellHeight+labelVertSpacing)+
672               di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
673           }
674           bool hasDocs=di->getClassDef()->isLinkable();
675           writeBitmapBox(di.get(),image,x,y,cellWidth,cellHeight,firstRow,hasDocs);
676           if (!firstRow && generateMap)
677             writeMapArea(t,di->getClassDef(),relPath,x,y,cellWidth,cellHeight);
678         }
679         else
680         {
681           xf=di->xPos()/(float)gridWidth;
682           if (doBase)
683           {
684             yf = di->yPos()/(float)gridHeight+superRows-1;
685           }
686           else
687           {
688             yf = superRows-1-di->yPos()/(float)gridHeight;
689           }
690           writeVectorBox(t,di.get(),xf,yf);
691         }
692       }
693     }
694     firstRow=FALSE;
695   }
696 }
697 
drawConnectors(TextStream & t,Image * image,bool doBase,bool bitmap,uint baseRows,uint superRows,uint cellWidth,uint cellHeight)698 void TreeDiagram::drawConnectors(TextStream &t,Image *image,
699                                  bool doBase,bool bitmap,
700                                  uint baseRows,uint superRows,
701                                  uint cellWidth,uint cellHeight)
702 {
703   bool done=FALSE;
704   auto it = m_rows.begin();
705   for (;it!=m_rows.end() && !done;++it) // for each row
706   {
707     const auto &dr = *it;
708     DiagramItem *rootDi = dr->item(0);
709     if (rootDi->isInList()) // row consists of list connectors
710     {
711       uint x=0,y=0,ys=0;
712       float xf=0.0f,yf=0.0f,ysf=0.0f;
713       auto rit = dr->begin();
714       while (rit!=dr->end())
715       {
716         DiagramItem *di=(*rit).get();
717         DiagramItem *pi=di->parentItem();
718         DiagramItemList dil=pi->getChildren();
719         DiagramItem *last=dil.back();
720         if (di==last) // single child
721         {
722           if (bitmap) // draw pixels
723           {
724             x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2;
725             if (doBase) // base classes
726             {
727               y = image->height()-
728                 (superRows-1)*(cellHeight+labelVertSpacing)-
729                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
730               image->drawVertArrow(x,y,y+labelVertSpacing/2,
731                                    protToColor(di->protection()),
732                                    protToMask(di->protection()));
733             }
734             else // super classes
735             {
736               y = (baseRows-1)*(cellHeight+labelVertSpacing)-
737                 labelVertSpacing/2+
738                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
739               image->drawVertLine(x,y,y+labelVertSpacing/2,
740                                   protToColor(di->protection()),
741                                   protToMask(di->protection()));
742             }
743           }
744           else // draw vectors
745           {
746             t << protToString(di->protection()) << "\n";
747             if (doBase)
748             {
749               t << "1 " << (di->xPos()/(float)gridWidth) << " "
750                 << (di->yPos()/(float)gridHeight+superRows-1) << " in\n";
751             }
752             else
753             {
754               t << "0 " << (di->xPos()/(float)gridWidth) << " "
755                 << ((float)superRows-0.25f-di->yPos()/(float)gridHeight)
756                 << " in\n";
757             }
758           }
759         }
760         else // multiple children, put them in a vertical list
761         {
762           if (bitmap)
763           {
764             x = di->parentItem()->xPos()*
765               (cellWidth+labelHorSpacing)/gridWidth+cellWidth/2;
766             if (doBase) // base classes
767             {
768               ys = image->height()-
769                 (superRows-1)*(cellHeight+labelVertSpacing)-
770                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
771               y = ys - cellHeight/2;
772             }
773             else // super classes
774             {
775               ys = (baseRows-1)*(cellHeight+labelVertSpacing)+
776                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
777               y = ys + cellHeight/2;
778             }
779           }
780           else
781           {
782             xf = di->parentItem()->xPos()/(float)gridWidth;
783             if (doBase)
784             {
785               ysf = di->yPos()/(float)gridHeight+superRows-1;
786               yf = ysf + 0.5f;
787             }
788             else
789             {
790               ysf = (float)superRows-0.25f-di->yPos()/(float)gridHeight;
791               yf = ysf - 0.25f;
792             }
793           }
794           while (di!=last) // more children to add
795           {
796             if (bitmap)
797             {
798               if (doBase) // base classes
799               {
800                 image->drawHorzArrow(y,x,x+cellWidth/2+labelHorSpacing,
801                     protToColor(di->protection()),
802                     protToMask(di->protection()));
803                 y -= cellHeight+labelVertSpacing;
804               }
805               else // super classes
806               {
807                 image->drawHorzLine(y,x,x+cellWidth/2+labelHorSpacing,
808                     protToColor(di->protection()),
809                     protToMask(di->protection()));
810                 y += cellHeight+labelVertSpacing;
811               }
812             }
813             else
814             {
815               t << protToString(di->protection()) << "\n";
816               if (doBase)
817               {
818                 t << "1 " << xf << " " << yf << " hedge\n";
819                 yf += 1.0f;
820               }
821               else
822               {
823                 t << "0 " << xf << " " << yf << " hedge\n";
824                 yf -= 1.0f;
825               }
826             }
827             ++rit;
828             if (rit!=dr->end()) di = (*rit).get(); else di=0;
829           }
830           // add last horizontal line and a vertical connection line
831           if (bitmap)
832           {
833             if (doBase) // base classes
834             {
835               image->drawHorzArrow(y,x,x+cellWidth/2+labelHorSpacing,
836                   protToColor(di->protection()),
837                   protToMask(di->protection()));
838               image->drawVertLine(x,y,ys+labelVertSpacing/2,
839                   protToColor(getMinProtectionLevel(dil)),
840                   protToMask(getMinProtectionLevel(dil)));
841             }
842             else // super classes
843             {
844               image->drawHorzLine(y,x,x+cellWidth/2+labelHorSpacing,
845                   protToColor(di->protection()),
846                   protToMask(di->protection()));
847               image->drawVertLine(x,ys-labelVertSpacing/2,y,
848                   protToColor(getMinProtectionLevel(dil)),
849                   protToMask(getMinProtectionLevel(dil)));
850             }
851           }
852           else
853           {
854             t << protToString(di->protection()) << "\n";
855             if (doBase)
856             {
857               t << "1 " << xf << " " << yf << " hedge\n";
858             }
859             else
860             {
861               t << "0 " << xf << " " << yf << " hedge\n";
862             }
863             t << protToString(getMinProtectionLevel(dil)) << "\n";
864             if (doBase)
865             {
866               t << xf << " " << ysf << " " << yf << " vedge\n";
867             }
868             else
869             {
870               t << xf << " " << (ysf + 0.25f) << " " << yf << " vedge\n";
871             }
872           }
873         }
874         if (rit!=dr->end()) ++rit;
875       }
876       done=TRUE; // the tree is drawn now
877     }
878     else // normal tree connector
879     {
880       for (const auto &di : *dr)
881       {
882         uint x=0,y=0;
883         DiagramItemList dil = di->getChildren();
884         DiagramItem *parent  = di->parentItem();
885         if (parent) // item has a parent -> connect to it
886         {
887           if (bitmap) // draw pixels
888           {
889             x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2;
890             if (doBase) // base classes
891             {
892               y = image->height()-
893                 (superRows-1)*(cellHeight+labelVertSpacing)-
894                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
895               /* write input line */
896               image->drawVertArrow(x,y,y+labelVertSpacing/2,
897                   protToColor(di->protection()),
898                   protToMask(di->protection()));
899             }
900             else // super classes
901             {
902               y = (baseRows-1)*(cellHeight+labelVertSpacing)-
903                 labelVertSpacing/2+
904                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
905               /* write output line */
906               image->drawVertLine(x,y,y+labelVertSpacing/2,
907                   protToColor(di->protection()),
908                   protToMask(di->protection()));
909             }
910           }
911           else // draw pixels
912           {
913             t << protToString(di->protection()) << "\n";
914             if (doBase)
915             {
916               t << "1 " << di->xPos()/(float)gridWidth << " "
917                 << (di->yPos()/(float)gridHeight+superRows-1) << " in\n";
918             }
919             else
920             {
921               t << "0 " << di->xPos()/(float)gridWidth << " "
922                 << ((float)superRows-0.25f-di->yPos()/(float)gridHeight)
923                 << " in\n";
924             }
925           }
926         }
927         if (!dil.empty())
928         {
929           Protection p=getMinProtectionLevel(dil);
930           uint mask=protToMask(p);
931           uchar col=protToColor(p);
932           if (bitmap)
933           {
934             x = di->xPos()*(cellWidth+labelHorSpacing)/gridWidth + cellWidth/2;
935             if (doBase) // base classes
936             {
937               y = image->height()-
938                 (superRows-1)*(cellHeight+labelVertSpacing)-
939                 cellHeight-labelVertSpacing/2-
940                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
941               image->drawVertLine(x,y,y+labelVertSpacing/2-1,col,mask);
942             }
943             else // super classes
944             {
945               y = (baseRows-1)*(cellHeight+labelVertSpacing)+
946                 cellHeight+
947                 di->yPos()*(cellHeight+labelVertSpacing)/gridHeight;
948               image->drawVertArrow(x,y,y+labelVertSpacing/2-1,col,mask);
949             }
950           }
951           else
952           {
953             t << protToString(p) << "\n";
954             if (doBase)
955             {
956               t << "0 " << di->xPos()/(float)gridWidth  << " "
957                 << (di->yPos()/(float)gridHeight+superRows-1) << " out\n";
958             }
959             else
960             {
961               t << "1 " << di->xPos()/(float)gridWidth  << " "
962                 << ((float)superRows-1.75f-di->yPos()/(float)gridHeight)
963                 << " out\n";
964             }
965           }
966           /* write input line */
967           DiagramItem *first = dil.front();
968           DiagramItem *last  = dil.back();
969           if (first!=last && !first->isInList()) /* connect with all base classes */
970           {
971             if (bitmap)
972             {
973               uint xs = first->xPos()*(cellWidth+labelHorSpacing)/gridWidth
974                 + cellWidth/2;
975               uint xe = last->xPos()*(cellWidth+labelHorSpacing)/gridWidth
976                 + cellWidth/2;
977               if (doBase) // base classes
978               {
979                 image->drawHorzLine(y,xs,xe,col,mask);
980               }
981               else // super classes
982               {
983                 image->drawHorzLine(y+labelVertSpacing/2,xs,xe,col,mask);
984               }
985             }
986             else
987             {
988               t << protToString(p) << "\n";
989               if (doBase)
990               {
991                 t << first->xPos()/(float)gridWidth << " "
992                   << last->xPos()/(float)gridWidth << " "
993                   << (first->yPos()/(float)gridHeight+superRows-1)
994                   << " conn\n";
995               }
996               else
997               {
998                 t << first->xPos()/(float)gridWidth << " "
999                   << last->xPos()/(float)gridWidth << " "
1000                   << ((float)superRows-first->yPos()/(float)gridHeight)
1001                   << " conn\n";
1002               }
1003             }
1004           }
1005         }
1006       }
1007     }
1008   }
1009 }
1010 
1011 //-----------------------------------------------------------------
1012 
1013 struct ClassDiagram::Private
1014 {
PrivateClassDiagram::Private1015   Private(const ClassDef *root) : base(root,true), super(root,false) {}
1016   TreeDiagram base;
1017   TreeDiagram super;
1018 };
1019 
1020 //-----------------------------------------------------------------
1021 
1022 
ClassDiagram(const ClassDef * root)1023 ClassDiagram::ClassDiagram(const ClassDef *root) : p(std::make_unique<Private>(root))
1024 {
1025   p->base.computeLayout();
1026   p->super.computeLayout();
1027   DiagramItem *baseItem  = p->base.row(0)->item(0);
1028   DiagramItem *superItem = p->super.row(0)->item(0);
1029   uint xbase  = baseItem->xPos();
1030   uint xsuper = superItem->xPos();
1031   if (xbase>xsuper)
1032   {
1033     superItem->move((int)(xbase-xsuper),0);
1034     p->super.moveChildren(superItem,(int)(xbase-xsuper));
1035   }
1036   else if (xbase<xsuper)
1037   {
1038     baseItem->move((int)(xsuper-xbase),0);
1039     p->base.moveChildren(baseItem,(int)(xsuper-xbase));
1040   }
1041 }
1042 
~ClassDiagram()1043 ClassDiagram::~ClassDiagram()
1044 {
1045 }
1046 
writeFigure(TextStream & output,const QCString & path,const QCString & fileName) const1047 void ClassDiagram::writeFigure(TextStream &output,const QCString &path,
1048                                const QCString &fileName) const
1049 {
1050   uint baseRows=p->base.computeRows();
1051   uint superRows=p->super.computeRows();
1052   uint baseMaxX, baseMaxLabelWidth, superMaxX, superMaxLabelWidth;
1053   p->base.computeExtremes(&baseMaxLabelWidth,&baseMaxX);
1054   p->super.computeExtremes(&superMaxLabelWidth,&superMaxX);
1055 
1056   uint rows=std::max(1u,baseRows+superRows-1);
1057   uint cols=(std::max(baseMaxX,superMaxX)+gridWidth*2-1)/gridWidth;
1058 
1059   // Estimate the image aspect width and height in pixels.
1060   uint estHeight = rows*40;
1061   uint estWidth  = cols*(20+std::max(baseMaxLabelWidth,superMaxLabelWidth));
1062   //printf("Estimated size %d x %d\n",estWidth,estHeight);
1063 
1064   const float pageWidth = 14.0f; // estimated page width in cm.
1065                                  // Somewhat lower to deal with estimation
1066                                  // errors.
1067 
1068   // compute the image height in centimeters based on the estimates
1069   float realHeight = static_cast<float>(std::min(rows,12u)); // real height in cm
1070   float realWidth  = realHeight * estWidth/static_cast<float>(estHeight);
1071   if (realWidth>pageWidth) // assume that the page width is about 15 cm
1072   {
1073     realHeight*=pageWidth/realWidth;
1074   }
1075 
1076   //output << "}\n";
1077   output << "\\begin{figure}[H]\n"
1078             "\\begin{center}\n"
1079             "\\leavevmode\n";
1080   output << "\\includegraphics[height=" << realHeight << "cm]{"
1081                                         << fileName << "}\n";
1082   output << "\\end{center}\n"
1083             "\\end{figure}\n";
1084 
1085   //printf("writeFigure rows=%d cols=%d\n",rows,cols);
1086 
1087   QCString epsBaseName=(QCString)path+"/"+fileName;
1088   QCString epsName=epsBaseName+".eps";
1089   std::ofstream f(epsName.str(),std::ofstream::out | std::ofstream::binary);
1090   if (!f.is_open())
1091   {
1092     term("Could not open file %s for writing\n",qPrint(epsName));
1093   }
1094   else
1095   {
1096     TextStream t(&f);
1097 
1098     //printf("writeEPS() rows=%d cols=%d\n",rows,cols);
1099 
1100     // generate EPS header and postscript variables and procedures
1101 
1102     t << "%!PS-Adobe-2.0 EPSF-2.0\n";
1103     t << "%%Title: ClassName\n";
1104     t << "%%Creator: Doxygen\n";
1105     t << "%%CreationDate: Time\n";
1106     t << "%%For: \n";
1107     t << "%Magnification: 1.00\n";
1108     t << "%%Orientation: Portrait\n";
1109     t << "%%BoundingBox: 0 0 500 " << estHeight*500.0f/(float)estWidth << "\n";
1110     t << "%%Pages: 0\n";
1111     t << "%%BeginSetup\n";
1112     t << "%%EndSetup\n";
1113     t << "%%EndComments\n";
1114     t << "\n";
1115     t << "% ----- variables -----\n";
1116     t << "\n";
1117     t << "/boxwidth 0 def\n";
1118     t << "/boxheight 40 def\n";
1119     t << "/fontheight 24 def\n";
1120     t << "/marginwidth 10 def\n";
1121     t << "/distx 20 def\n";
1122     t << "/disty 40 def\n";
1123     t << "/boundaspect " << estWidth/(float)estHeight << " def  % aspect ratio of the BoundingBox (width/height)\n";
1124     t << "/boundx 500 def\n";
1125     t << "/boundy boundx boundaspect div def\n";
1126     t << "/xspacing 0 def\n";
1127     t << "/yspacing 0 def\n";
1128     t << "/rows " << rows << " def\n";
1129     t << "/cols " << cols << " def\n";
1130     t << "/scalefactor 0 def\n";
1131     t << "/boxfont /Times-Roman findfont fontheight scalefont def\n";
1132     t << "\n";
1133     t << "% ----- procedures -----\n";
1134     t << "\n";
1135     t << "/dotted { [1 4] 0 setdash } def\n";
1136     t << "/dashed { [5] 0 setdash } def\n";
1137     t << "/solid  { [] 0 setdash } def\n";
1138     t << "\n";
1139     t << "/max % result = MAX(arg1,arg2)\n";
1140     t << "{\n";
1141     t << "  /a exch def\n";
1142     t << "  /b exch def\n";
1143     t << "  a b gt {a} {b} ifelse\n";
1144     t << "} def\n";
1145     t << "\n";
1146     t << "/xoffset % result = MAX(0,(scalefactor-(boxwidth*cols+distx*(cols-1)))/2)\n";
1147     t << "{\n";
1148     t << "  0 scalefactor boxwidth cols mul distx cols 1 sub mul add sub 2 div max\n";
1149     t << "} def\n";
1150     t << "\n";
1151     t << "/cw % boxwidth = MAX(boxwidth, stringwidth(arg1))\n";
1152     t << "{\n";
1153     t << "  /str exch def\n";
1154     t << "  /boxwidth boxwidth str stringwidth pop max def\n";
1155     t << "} def\n";
1156     t << "\n";
1157     t << "/box % draws a box with text 'arg1' at grid pos (arg2,arg3)\n";
1158     t << "{ gsave\n";
1159     t << "  2 setlinewidth\n";
1160     t << "  newpath\n";
1161     t << "  exch xspacing mul xoffset add\n";
1162     t << "  exch yspacing mul\n";
1163     t << "  moveto\n";
1164     t << "  boxwidth 0 rlineto \n";
1165     t << "  0 boxheight rlineto \n";
1166     t << "  boxwidth neg 0 rlineto \n";
1167     t << "  0 boxheight neg rlineto \n";
1168     t << "  closepath\n";
1169     t << "  dup stringwidth pop neg boxwidth add 2 div\n";
1170     t << "  boxheight fontheight 2 div sub 2 div\n";
1171     t << "  rmoveto show stroke\n";
1172     t << "  grestore\n";
1173     t << "} def  \n";
1174     t << "\n";
1175     t << "/mark\n";
1176     t << "{ newpath\n";
1177     t << "  exch xspacing mul xoffset add boxwidth add\n";
1178     t << "  exch yspacing mul\n";
1179     t << "  moveto\n";
1180     t << "  0 boxheight 4 div rlineto\n";
1181     t << "  boxheight neg 4 div boxheight neg 4 div rlineto\n";
1182     t << "  closepath\n";
1183     t << "  eofill\n";
1184     t << "  stroke\n";
1185     t << "} def\n";
1186     t << "\n";
1187     t << "/arrow\n";
1188     t << "{ newpath\n";
1189     t << "  moveto\n";
1190     t << "  3 -8 rlineto\n";
1191     t << "  -6 0 rlineto\n";
1192     t << "  3 8 rlineto\n";
1193     t << "  closepath\n";
1194     t << "  eofill\n";
1195     t << "  stroke\n";
1196     t << "} def\n";
1197     t << "\n";
1198     t << "/out % draws an output connector for the block at (arg1,arg2)\n";
1199     t << "{\n";
1200     t << "  newpath\n";
1201     t << "  exch xspacing mul xoffset add boxwidth 2 div add\n";
1202     t << "  exch yspacing mul boxheight add\n";
1203     t << "  /y exch def\n";
1204     t << "  /x exch def\n";
1205     t << "  x y moveto\n";
1206     t << "  0 disty 2 div rlineto \n";
1207     t << "  stroke\n";
1208     t << "  1 eq { x y disty 2 div add arrow } if\n";
1209     t << "} def\n";
1210     t << "\n";
1211     t << "/in % draws an input connector for the block at (arg1,arg2)\n";
1212     t << "{\n";
1213     t << "  newpath\n";
1214     t << "  exch xspacing mul xoffset add boxwidth 2 div add\n";
1215     t << "  exch yspacing mul disty 2 div sub\n";
1216     t << "  /y exch def\n";
1217     t << "  /x exch def\n";
1218     t << "  x y moveto\n";
1219     t << "  0 disty 2 div rlineto\n";
1220     t << "  stroke\n";
1221     t << "  1 eq { x y disty 2 div add arrow } if\n";
1222     t << "} def\n";
1223     t << "\n";
1224     t << "/hedge\n";
1225     t << "{\n";
1226     t << "  exch xspacing mul xoffset add boxwidth 2 div add\n";
1227     t << "  exch yspacing mul boxheight 2 div sub\n";
1228     t << "  /y exch def\n";
1229     t << "  /x exch def\n";
1230     t << "  newpath\n";
1231     t << "  x y moveto\n";
1232     t << "  boxwidth 2 div distx add 0 rlineto\n";
1233     t << "  stroke\n";
1234     t << "  1 eq\n";
1235     t << "  { newpath x boxwidth 2 div distx add add y moveto\n";
1236     t << "    -8 3 rlineto\n";
1237     t << "    0 -6 rlineto\n";
1238     t << "    8 3 rlineto\n";
1239     t << "    closepath\n";
1240     t << "    eofill\n";
1241     t << "    stroke\n";
1242     t << "  } if\n";
1243     t << "} def\n";
1244     t << "\n";
1245     t << "/vedge\n";
1246     t << "{\n";
1247     t << "  /ye exch def\n";
1248     t << "  /ys exch def\n";
1249     t << "  /xs exch def\n";
1250     t << "  newpath\n";
1251     t << "  xs xspacing mul xoffset add boxwidth 2 div add dup\n";
1252     t << "  ys yspacing mul boxheight 2 div sub\n";
1253     t << "  moveto\n";
1254     t << "  ye yspacing mul boxheight 2 div sub\n";
1255     t << "  lineto\n";
1256     t << "  stroke\n";
1257     t << "} def\n";
1258     t << "\n";
1259     t << "/conn % connections the blocks from col 'arg1' to 'arg2' of row 'arg3'\n";
1260     t << "{\n";
1261     t << "  /ys exch def\n";
1262     t << "  /xe exch def\n";
1263     t << "  /xs exch def\n";
1264     t << "  newpath\n";
1265     t << "  xs xspacing mul xoffset add boxwidth 2 div add\n";
1266     t << "  ys yspacing mul disty 2 div sub\n";
1267     t << "  moveto\n";
1268     t << "  xspacing xe xs sub mul 0\n";
1269     t << "  rlineto\n";
1270     t << "  stroke\n";
1271     t << "} def\n";
1272     t << "\n";
1273     t << "% ----- main ------\n";
1274     t << "\n";
1275     t << "boxfont setfont\n";
1276     t << "1 boundaspect scale\n";
1277 
1278 
1279     for (const auto &dr : p->base)
1280     {
1281       bool done=FALSE;
1282       for (const auto &di : *dr)
1283       {
1284         done=di->isInList();
1285         t << "(" << convertToPSString(di->label()) << ") cw\n";
1286       }
1287       if (done) break;
1288     }
1289 
1290     auto it = p->super.begin();
1291     if (it!=p->super.end()) ++it;
1292     for (;it!=p->super.end();++it)
1293     {
1294       const auto &dr = *it;
1295       bool done=FALSE;
1296       for (const auto &di : *dr)
1297       {
1298         done=di->isInList();
1299         t << "(" << convertToPSString(di->label()) << ") cw\n";
1300       }
1301       if (done) break;
1302     }
1303 
1304     t << "/boxwidth boxwidth marginwidth 2 mul add def\n"
1305       << "/xspacing boxwidth distx add def\n"
1306       << "/yspacing boxheight disty add def\n"
1307       << "/scalefactor \n"
1308       << "  boxwidth cols mul distx cols 1 sub mul add\n"
1309       << "  boxheight rows mul disty rows 1 sub mul add boundaspect mul \n"
1310       << "  max def\n"
1311       << "boundx scalefactor div boundy scalefactor div scale\n";
1312 
1313     t << "\n% ----- classes -----\n\n";
1314     p->base.drawBoxes(t,0,TRUE,FALSE,baseRows,superRows,0,0);
1315     p->super.drawBoxes(t,0,FALSE,FALSE,baseRows,superRows,0,0);
1316 
1317     t << "\n% ----- relations -----\n\n";
1318     p->base.drawConnectors(t,0,TRUE,FALSE,baseRows,superRows,0,0);
1319     p->super.drawConnectors(t,0,FALSE,FALSE,baseRows,superRows,0,0);
1320 
1321   }
1322   f.close();
1323 
1324   if (Config_getBool(USE_PDFLATEX))
1325   {
1326     QCString epstopdfArgs(4096);
1327     epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"",
1328                    qPrint(epsBaseName),qPrint(epsBaseName));
1329     //printf("Converting eps using '%s'\n",qPrint(epstopdfArgs));
1330     Portable::sysTimerStart();
1331     if (Portable::system("epstopdf",epstopdfArgs)!=0)
1332     {
1333        err("Problems running epstopdf. Check your TeX installation!\n");
1334        Portable::sysTimerStop();
1335        return;
1336     }
1337     Portable::sysTimerStop();
1338   }
1339 }
1340 
1341 
writeImage(TextStream & t,const QCString & path,const QCString & relPath,const QCString & fileName,bool generateMap) const1342 void ClassDiagram::writeImage(TextStream &t,const QCString &path,
1343                               const QCString &relPath,const QCString &fileName,
1344                               bool generateMap) const
1345 {
1346   uint baseRows=p->base.computeRows();
1347   uint superRows=p->super.computeRows();
1348   uint rows=baseRows+superRows-1;
1349 
1350   uint lb,ls,xb,xs;
1351   p->base.computeExtremes(&lb,&xb);
1352   p->super.computeExtremes(&ls,&xs);
1353 
1354   uint cellWidth  = std::max(lb,ls)+labelHorMargin*2;
1355   uint maxXPos    = std::max(xb,xs);
1356   uint labelVertMargin = 6; //std::max(6,(cellWidth-fontHeight)/6); // aspect at least 1:3
1357   uint cellHeight = labelVertMargin*2+fontHeight;
1358   uint imageWidth = (maxXPos+gridWidth)*cellWidth/gridWidth+
1359                     (maxXPos*labelHorSpacing)/gridWidth;
1360   uint imageHeight = rows*cellHeight+(rows-1)*labelVertSpacing;
1361 
1362   Image image(imageWidth,imageHeight);
1363 
1364   p->base.drawBoxes(t,&image,TRUE,TRUE,baseRows,superRows,cellWidth,cellHeight,relPath,generateMap);
1365   p->super.drawBoxes(t,&image,FALSE,TRUE,baseRows,superRows,cellWidth,cellHeight,relPath,generateMap);
1366   p->base.drawConnectors(t,&image,TRUE,TRUE,baseRows,superRows,cellWidth,cellHeight);
1367   p->super.drawConnectors(t,&image,FALSE,TRUE,baseRows,superRows,cellWidth,cellHeight);
1368 
1369 #define IMAGE_EXT ".png"
1370   image.save((QCString)path+"/"+fileName+IMAGE_EXT);
1371   Doxygen::indexList->addImageFile(QCString(fileName)+IMAGE_EXT);
1372 }
1373 
1374