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