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