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