|
1 /***************************************************************************** |
|
2 * |
|
3 * |
|
4 * |
|
5 * |
|
6 * Copyright (C) 1997-2008 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 #ifdef _WIN32 |
|
20 #include <windows.h> |
|
21 #define BITMAP W_BITMAP |
|
22 #endif |
|
23 |
|
24 #include <stdlib.h> |
|
25 |
|
26 #include "dot.h" |
|
27 #include "doxygen.h" |
|
28 #include "message.h" |
|
29 #include "util.h" |
|
30 #include "config.h" |
|
31 #include "language.h" |
|
32 #include "defargs.h" |
|
33 #include "docparser.h" |
|
34 #include "debug.h" |
|
35 #include "pagedef.h" |
|
36 #include "portable.h" |
|
37 #include "dirdef.h" |
|
38 |
|
39 #include <qdir.h> |
|
40 #include <qfile.h> |
|
41 #include <qtextstream.h> |
|
42 #include <md5.h> |
|
43 |
|
44 #define MAP_CMD "cmapx" |
|
45 |
|
46 //#define FONTNAME "FreeSans" |
|
47 #define FONTNAME getDotFontName() |
|
48 #define FONTSIZE getDotFontSize() |
|
49 |
|
50 //-------------------------------------------------------------------- |
|
51 |
|
52 static const int maxCmdLine = 40960; |
|
53 |
|
54 /*! mapping from protection levels to color names */ |
|
55 static const char *edgeColorMap[] = |
|
56 { |
|
57 "midnightblue", // Public |
|
58 "darkgreen", // Protected |
|
59 "firebrick4", // Private |
|
60 "darkorchid3", // "use" relation |
|
61 "grey75", // Undocumented |
|
62 "orange" // template relation |
|
63 }; |
|
64 |
|
65 static const char *arrowStyle[] = |
|
66 { |
|
67 "empty", // Public |
|
68 "empty", // Protected |
|
69 "empty", // Private |
|
70 "open", // "use" relation |
|
71 0, // Undocumented |
|
72 0 // template relation |
|
73 }; |
|
74 |
|
75 static const char *edgeStyleMap[] = |
|
76 { |
|
77 "solid", // inheritance |
|
78 "dashed" // usage |
|
79 }; |
|
80 |
|
81 static QCString getDotFontName() |
|
82 { |
|
83 static QCString dotFontName = Config_getString("DOT_FONTNAME"); |
|
84 if (dotFontName.isEmpty()) dotFontName="FreeSans"; |
|
85 return dotFontName; |
|
86 } |
|
87 |
|
88 static int getDotFontSize() |
|
89 { |
|
90 static int dotFontSize = Config_getInt("DOT_FONTSIZE"); |
|
91 if (dotFontSize<4) dotFontSize=4; |
|
92 return dotFontSize; |
|
93 } |
|
94 |
|
95 static void writeGraphHeader(QTextStream &t) |
|
96 { |
|
97 t << "digraph G" << endl; |
|
98 t << "{" << endl; |
|
99 if (Config_getBool("DOT_TRANSPARENT")) |
|
100 { |
|
101 t << " bgcolor=\"transparent\";" << endl; |
|
102 } |
|
103 t << " edge [fontname=\"" << FONTNAME << "\"," |
|
104 "fontsize=\"" << FONTSIZE << "\"," |
|
105 "labelfontname=\"" << FONTNAME << "\"," |
|
106 "labelfontsize=\"" << FONTSIZE << "\"];\n"; |
|
107 t << " node [fontname=\"" << FONTNAME << "\"," |
|
108 "fontsize=\"" << FONTSIZE << "\",shape=record];\n"; |
|
109 } |
|
110 |
|
111 static void writeGraphFooter(QTextStream &t) |
|
112 { |
|
113 t << "}" << endl; |
|
114 } |
|
115 |
|
116 /*! converts the rectangles in a client site image map into a stream |
|
117 * \param t the stream to which the result is written. |
|
118 * \param mapName the name of the map file. |
|
119 * \param relPath the relative path to the root of the output directory |
|
120 * (used in case CREATE_SUBDIRS is enabled). |
|
121 * \param urlOnly if FALSE the url field in the map contains an external |
|
122 * references followed by a $ and then the URL. |
|
123 * \param context the context (file, class, or namespace) in which the |
|
124 * map file was found |
|
125 * \returns TRUE if succesful. |
|
126 */ |
|
127 static bool convertMapFile(QTextStream &t,const char *mapName, |
|
128 const QCString relPath, bool urlOnly=FALSE, |
|
129 const QString &context=QString()) |
|
130 { |
|
131 QFile f(mapName); |
|
132 if (!f.open(IO_ReadOnly)) |
|
133 { |
|
134 err("Error opening map file %s for inclusion in the docs!\n" |
|
135 "If you installed Graphviz/dot after a previous failing run, \n" |
|
136 "try deleting the output directory and rerun doxygen.\n",mapName); |
|
137 return FALSE; |
|
138 } |
|
139 const int maxLineLen=10240; |
|
140 while (!f.atEnd()) // foreach line |
|
141 { |
|
142 QCString buf(maxLineLen); |
|
143 int numBytes = f.readLine(buf.data(),maxLineLen); |
|
144 buf[numBytes-1]='\0'; |
|
145 |
|
146 if (buf.left(5)=="<area") |
|
147 { |
|
148 // search for href="...", store ... part in link |
|
149 int indexS = buf.find("href=\""), indexE; |
|
150 if (indexS!=-1 && (indexE=buf.find('"',indexS+6))!=-1) |
|
151 { |
|
152 QCString link = buf.mid(indexS+6,indexE-indexS-6); |
|
153 QCString result; |
|
154 QCString *dest; |
|
155 if (urlOnly) // for user defined dot graphs |
|
156 { |
|
157 if (link.left(5)=="\\ref ") // \ref url |
|
158 { |
|
159 result="href=\""; |
|
160 // fake ref node to resolve the url |
|
161 DocRef *df = new DocRef( (DocNode*) 0, link.mid(5), context ); |
|
162 if (!df->ref().isEmpty()) |
|
163 { |
|
164 if ((dest=Doxygen::tagDestinationDict[df->ref()])) |
|
165 result += *dest + "/"; |
|
166 } |
|
167 else if (!relPath.isEmpty()) |
|
168 { |
|
169 result += relPath; |
|
170 } |
|
171 if (!df->file().isEmpty()) |
|
172 result += df->file().data() + Doxygen::htmlFileExtension; |
|
173 if (!df->anchor().isEmpty()) |
|
174 result += "#" + df->anchor(); |
|
175 delete df; |
|
176 result += "\""; |
|
177 } |
|
178 else |
|
179 { |
|
180 result = "href=\"" + link + "\""; |
|
181 } |
|
182 } |
|
183 else // ref$url (external ref via tag file), or $url (local ref) |
|
184 { |
|
185 int marker = link.find('$'); |
|
186 if (marker!=-1) |
|
187 { |
|
188 QCString ref = link.left(marker); |
|
189 QCString url = link.mid(marker+1); |
|
190 if (!ref.isEmpty()) |
|
191 { |
|
192 result = "doxygen=\"" + ref + ":"; |
|
193 if ((dest=Doxygen::tagDestinationDict[ref])) result += *dest + "/"; |
|
194 result += "\" "; |
|
195 } |
|
196 result+= "href=\""; |
|
197 if (!ref.isEmpty()) |
|
198 { |
|
199 if ((dest=Doxygen::tagDestinationDict[ref])) result += *dest + "/"; |
|
200 } |
|
201 else if (!relPath.isEmpty()) |
|
202 { |
|
203 result += relPath; |
|
204 } |
|
205 result+= url + "\""; |
|
206 } |
|
207 else // should not happen, but handle properly anyway |
|
208 { |
|
209 result = "href=\"" + link + "\""; |
|
210 } |
|
211 } |
|
212 QCString leftPart = buf.left(indexS); |
|
213 QCString rightPart = buf.mid(indexE+1); |
|
214 buf = leftPart + result + rightPart; |
|
215 } |
|
216 t << buf; |
|
217 } |
|
218 } |
|
219 return TRUE; |
|
220 } |
|
221 |
|
222 static QArray<int> s_newNumber; |
|
223 static int s_max_newNumber=0; |
|
224 |
|
225 inline int reNumberNode(int number, bool doReNumbering) |
|
226 { |
|
227 if (!doReNumbering) |
|
228 { |
|
229 return number; |
|
230 } |
|
231 else |
|
232 { |
|
233 int s = s_newNumber.size(); |
|
234 if (number>=s) |
|
235 { |
|
236 int ns=0; |
|
237 ns = s * 3 / 2 + 5; // new size |
|
238 if (number>=ns) // number still doesn't fit |
|
239 { |
|
240 ns = number * 3 / 2 + 5; |
|
241 } |
|
242 s_newNumber.resize(ns); |
|
243 for (int i=s;i<ns;i++) // clear new part of the array |
|
244 { |
|
245 s_newNumber.at(i)=0; |
|
246 } |
|
247 } |
|
248 int i = s_newNumber.at(number); |
|
249 if (i == 0) // not yet mapped |
|
250 { |
|
251 i = ++s_max_newNumber; // start from 1 |
|
252 s_newNumber.at(number) = i; |
|
253 } |
|
254 return i; |
|
255 } |
|
256 } |
|
257 |
|
258 static void resetReNumbering() |
|
259 { |
|
260 s_max_newNumber=0; |
|
261 s_newNumber.resize(s_max_newNumber); |
|
262 } |
|
263 |
|
264 static QCString g_dotFontPath; |
|
265 |
|
266 static void setDotFontPath(const char *path) |
|
267 { |
|
268 ASSERT(g_dotFontPath.isEmpty()); |
|
269 g_dotFontPath = portable_getenv("DOTFONTPATH"); |
|
270 QCString newFontPath = Config_getString("DOT_FONTPATH"); |
|
271 if (!newFontPath.isEmpty() && path) |
|
272 { |
|
273 newFontPath.prepend(path+portable_pathListSeparator()); |
|
274 } |
|
275 else if (newFontPath.isEmpty() && path) |
|
276 { |
|
277 newFontPath=path; |
|
278 } |
|
279 else |
|
280 { |
|
281 portable_unsetenv("DOTFONTPATH"); |
|
282 return; |
|
283 } |
|
284 portable_setenv("DOTFONTPATH",newFontPath); |
|
285 } |
|
286 |
|
287 static void unsetDotFontPath() |
|
288 { |
|
289 portable_setenv("DOTFONTPATH",g_dotFontPath); |
|
290 g_dotFontPath=""; |
|
291 } |
|
292 |
|
293 static bool readBoundingBoxEPS(const char *fileName,int *width,int *height) |
|
294 { |
|
295 QCString bb("%%PageBoundingBox:"); |
|
296 QFile f(fileName); |
|
297 if (!f.open(IO_ReadOnly)) return FALSE; |
|
298 const int maxLineLen=1024; |
|
299 char buf[maxLineLen]; |
|
300 while (!f.atEnd()) |
|
301 { |
|
302 int numBytes = f.readLine(buf,maxLineLen-1); // read line |
|
303 buf[numBytes]='\0'; |
|
304 if (strncmp(buf,bb.data(),bb.length()-1)==0) // found PageBoundingBox string |
|
305 { |
|
306 int x,y; |
|
307 if (sscanf(buf+bb.length(),"%d %d %d %d",&x,&y,width,height)!=4) |
|
308 { |
|
309 return FALSE; |
|
310 } |
|
311 return TRUE; |
|
312 } |
|
313 } |
|
314 return FALSE; |
|
315 } |
|
316 |
|
317 // since dot silently reproduces the input file when it does not |
|
318 // support the PNG format, we need to check the result. |
|
319 static void checkDotResult(const QCString &imgName) |
|
320 { |
|
321 if (Config_getEnum("DOT_IMAGE_FORMAT")=="png") |
|
322 { |
|
323 QFile f(imgName); |
|
324 if (f.open(IO_ReadOnly)) |
|
325 { |
|
326 char data[4]; |
|
327 if (f.readBlock(data,4)==4) |
|
328 { |
|
329 if (!(data[1]=='P' && data[2]=='N' && data[3]=='G')) |
|
330 { |
|
331 err("Error! Image `%s' produced by dot is not a valid PNG!\n" |
|
332 "You should either select a different format " |
|
333 "(DOT_IMAGE_FORMAT in the config file) or install a more " |
|
334 "recent version of graphviz (1.7+)\n",imgName.data() |
|
335 ); |
|
336 } |
|
337 } |
|
338 else |
|
339 { |
|
340 err("Error: Could not read image `%s' generated by dot!\n",imgName.data()); |
|
341 } |
|
342 } |
|
343 else |
|
344 { |
|
345 err("Error: Could not open image `%s' generated by dot!\n",imgName.data()); |
|
346 } |
|
347 } |
|
348 } |
|
349 |
|
350 /*! Checks if a file "baseName".md5 exists. If so the contents |
|
351 * are compared with \a md5. If equal FALSE is returned. If the .md5 |
|
352 * file does not exist or its contents are not equal to \a md5, |
|
353 * a new .md5 is generated with the \a md5 string as contents. |
|
354 */ |
|
355 static bool checkAndUpdateMd5Signature(const QCString &baseName,const QCString &md5) |
|
356 { |
|
357 QFile f(baseName+".md5"); |
|
358 if (f.open(IO_ReadOnly)) |
|
359 { |
|
360 // read checksum |
|
361 QCString md5stored(33); |
|
362 int bytesRead=f.readBlock(md5stored.data(),32); |
|
363 md5stored[32]='\0'; |
|
364 // compare checksum |
|
365 if (bytesRead==32 && md5==md5stored) |
|
366 { |
|
367 // bail out if equal |
|
368 return FALSE; |
|
369 } |
|
370 } |
|
371 f.close(); |
|
372 // create checksum file |
|
373 if (f.open(IO_WriteOnly)) |
|
374 { |
|
375 f.writeBlock(md5.data(),32); |
|
376 f.close(); |
|
377 } |
|
378 return TRUE; |
|
379 } |
|
380 |
|
381 //-------------------------------------------------------------------- |
|
382 |
|
383 class DotNodeList : public QList<DotNode> |
|
384 { |
|
385 public: |
|
386 DotNodeList() : QList<DotNode>() {} |
|
387 ~DotNodeList() {} |
|
388 int compareItems(GCI item1,GCI item2) |
|
389 { |
|
390 return stricmp(((DotNode *)item1)->m_label,((DotNode *)item2)->m_label); |
|
391 } |
|
392 }; |
|
393 |
|
394 //-------------------------------------------------------------------- |
|
395 |
|
396 DotRunner::DotRunner(const char *file) : m_file(file) |
|
397 { |
|
398 m_jobs.setAutoDelete(TRUE); |
|
399 } |
|
400 |
|
401 void DotRunner::addJob(const char *format,const char *output) |
|
402 { |
|
403 QCString args = QCString("-T")+format+" -o \""+output+"\""; |
|
404 m_jobs.append(new QCString(args)); |
|
405 } |
|
406 |
|
407 void DotRunner::addPostProcessing(const char *cmd,const char *args) |
|
408 { |
|
409 m_postCmd = cmd; |
|
410 m_postArgs = args; |
|
411 } |
|
412 |
|
413 bool DotRunner::run() |
|
414 { |
|
415 int exitCode=0; |
|
416 static QCString dotExe = Config_getString("DOT_PATH")+"dot"; |
|
417 QCString dotArgs; |
|
418 QListIterator<QCString> li(m_jobs); |
|
419 QCString *s; |
|
420 if (Config_getBool("DOT_MULTI_TARGETS")) |
|
421 { |
|
422 dotArgs="\""+m_file+"\""; |
|
423 for (li.toFirst();(s=li.current());++li) |
|
424 { |
|
425 dotArgs+=' '; |
|
426 dotArgs+=*s; |
|
427 } |
|
428 if ((exitCode=portable_system(dotExe,dotArgs,FALSE))!=0) |
|
429 { |
|
430 goto error; |
|
431 } |
|
432 } |
|
433 else |
|
434 { |
|
435 for (li.toFirst();(s=li.current());++li) |
|
436 { |
|
437 dotArgs="\""+m_file+"\" "+*s; |
|
438 if ((exitCode=portable_system(dotExe,dotArgs,FALSE))!=0) |
|
439 { |
|
440 goto error; |
|
441 } |
|
442 } |
|
443 } |
|
444 if (!m_postCmd.isEmpty() && portable_system(m_postCmd,m_postArgs)!=0) |
|
445 { |
|
446 err("Error: Problems running '%s' as a post-processing step for dot output\n",m_postCmd.data()); |
|
447 return FALSE; |
|
448 } |
|
449 return TRUE; |
|
450 error: |
|
451 err("Problems running dot: exit code=%d, command='%s', arguments='%s'\n", |
|
452 exitCode,dotExe.data(),dotArgs.data()); |
|
453 return FALSE; |
|
454 } |
|
455 |
|
456 //-------------------------------------------------------------------- |
|
457 |
|
458 |
|
459 /*! helper function that deletes all nodes in a connected graph, given |
|
460 * one of the graph's nodes |
|
461 */ |
|
462 static void deleteNodes(DotNode *node,SDict<DotNode> *skipNodes=0) |
|
463 { |
|
464 //printf("deleteNodes skipNodes=%p\n",skipNodes); |
|
465 static DotNodeList deletedNodes; |
|
466 deletedNodes.setAutoDelete(TRUE); |
|
467 node->deleteNode(deletedNodes,skipNodes); // collect nodes to be deleted. |
|
468 deletedNodes.clear(); // actually remove the nodes. |
|
469 } |
|
470 |
|
471 DotNode::DotNode(int n,const char *lab,const char *tip, const char *url, |
|
472 bool isRoot,ClassDef *cd) |
|
473 : m_subgraphId(-1) |
|
474 , m_number(n) |
|
475 , m_label(lab) |
|
476 , m_tooltip(tip) |
|
477 , m_url(url) |
|
478 , m_parents(0) |
|
479 , m_children(0) |
|
480 , m_edgeInfo(0) |
|
481 , m_deleted(FALSE) |
|
482 , m_written(FALSE) |
|
483 , m_hasDoc(FALSE) |
|
484 , m_isRoot(isRoot) |
|
485 , m_classDef(cd) |
|
486 , m_visible(FALSE) |
|
487 , m_truncated(Unknown) |
|
488 , m_distance(1000) |
|
489 { |
|
490 } |
|
491 |
|
492 DotNode::~DotNode() |
|
493 { |
|
494 delete m_children; |
|
495 delete m_parents; |
|
496 delete m_edgeInfo; |
|
497 } |
|
498 |
|
499 void DotNode::addChild(DotNode *n, |
|
500 int edgeColor, |
|
501 int edgeStyle, |
|
502 const char *edgeLab, |
|
503 const char *edgeURL, |
|
504 int edgeLabCol |
|
505 ) |
|
506 { |
|
507 if (m_children==0) |
|
508 { |
|
509 m_children = new QList<DotNode>; |
|
510 m_edgeInfo = new QList<EdgeInfo>; |
|
511 m_edgeInfo->setAutoDelete(TRUE); |
|
512 } |
|
513 m_children->append(n); |
|
514 EdgeInfo *ei = new EdgeInfo; |
|
515 ei->m_color = edgeColor; |
|
516 ei->m_style = edgeStyle; |
|
517 ei->m_label = edgeLab; |
|
518 ei->m_url = edgeURL; |
|
519 if (edgeLabCol==-1) |
|
520 ei->m_labColor=edgeColor; |
|
521 else |
|
522 ei->m_labColor=edgeLabCol; |
|
523 m_edgeInfo->append(ei); |
|
524 } |
|
525 |
|
526 void DotNode::addParent(DotNode *n) |
|
527 { |
|
528 if (m_parents==0) |
|
529 { |
|
530 m_parents = new QList<DotNode>; |
|
531 } |
|
532 m_parents->append(n); |
|
533 } |
|
534 |
|
535 void DotNode::removeChild(DotNode *n) |
|
536 { |
|
537 if (m_children) m_children->remove(n); |
|
538 } |
|
539 |
|
540 void DotNode::removeParent(DotNode *n) |
|
541 { |
|
542 if (m_parents) m_parents->remove(n); |
|
543 } |
|
544 |
|
545 void DotNode::deleteNode(DotNodeList &deletedList,SDict<DotNode> *skipNodes) |
|
546 { |
|
547 if (m_deleted) return; // avoid recursive loops in case the graph has cycles |
|
548 m_deleted=TRUE; |
|
549 if (m_parents!=0) // delete all parent nodes of this node |
|
550 { |
|
551 QListIterator<DotNode> dnlip(*m_parents); |
|
552 DotNode *pn; |
|
553 for (dnlip.toFirst();(pn=dnlip.current());++dnlip) |
|
554 { |
|
555 //pn->removeChild(this); |
|
556 pn->deleteNode(deletedList,skipNodes); |
|
557 } |
|
558 } |
|
559 if (m_children!=0) // delete all child nodes of this node |
|
560 { |
|
561 QListIterator<DotNode> dnlic(*m_children); |
|
562 DotNode *cn; |
|
563 for (dnlic.toFirst();(cn=dnlic.current());++dnlic) |
|
564 { |
|
565 //cn->removeParent(this); |
|
566 cn->deleteNode(deletedList,skipNodes); |
|
567 } |
|
568 } |
|
569 // add this node to the list of deleted nodes. |
|
570 //printf("skipNodes=%p find(%p)=%p\n",skipNodes,this,skipNodes ? skipNodes->find((int)this) : 0); |
|
571 if (skipNodes==0 || skipNodes->find((char*)this)==0) |
|
572 { |
|
573 //printf("deleting\n"); |
|
574 deletedList.append(this); |
|
575 } |
|
576 } |
|
577 |
|
578 void DotNode::setDistance(int distance) |
|
579 { |
|
580 if (distance<m_distance) m_distance = distance; |
|
581 } |
|
582 |
|
583 static QCString convertLabel(const QCString &l) |
|
584 { |
|
585 QCString result; |
|
586 const char *p=l.data(); |
|
587 if (p==0) return result; |
|
588 char c; |
|
589 while ((c=*p++)) |
|
590 { |
|
591 switch(c) |
|
592 { |
|
593 case '\\': result+="\\\\"; break; |
|
594 case '\n': result+="\\n"; break; |
|
595 case '<': result+="\\<"; break; |
|
596 case '>': result+="\\>"; break; |
|
597 case '|': result+="\\|"; break; |
|
598 case '{': result+="\\{"; break; |
|
599 case '}': result+="\\}"; break; |
|
600 case '"': result+="\\\""; break; |
|
601 default: result+=c; break; |
|
602 } |
|
603 } |
|
604 return result; |
|
605 } |
|
606 |
|
607 static QCString escapeTooltip(const QCString &tooltip) |
|
608 { |
|
609 QCString result; |
|
610 const char *p=tooltip.data(); |
|
611 if (p==0) return result; |
|
612 char c; |
|
613 while ((c=*p++)) |
|
614 { |
|
615 switch(c) |
|
616 { |
|
617 case '\\': result+="\\\\"; break; |
|
618 default: result+=c; break; |
|
619 } |
|
620 } |
|
621 return result; |
|
622 } |
|
623 |
|
624 static void writeBoxMemberList(QTextStream &t,char prot,MemberList *ml,ClassDef *scope) |
|
625 { |
|
626 if (ml) |
|
627 { |
|
628 MemberListIterator mlia(*ml); |
|
629 MemberDef *mma; |
|
630 for (mlia.toFirst();(mma = mlia.current());++mlia) |
|
631 { |
|
632 if (mma->getClassDef() == scope) |
|
633 { |
|
634 t << prot << " " << convertLabel(mma->name()); |
|
635 if (!mma->isObjCMethod() && |
|
636 (mma->isFunction() || mma->isSlot() || mma->isSignal())) t << "()"; |
|
637 t << "\\l"; |
|
638 } |
|
639 } |
|
640 // write member groups within the memberlist |
|
641 MemberGroupList *mgl = ml->getMemberGroupList(); |
|
642 if (mgl) |
|
643 { |
|
644 MemberGroupListIterator mgli(*mgl); |
|
645 MemberGroup *mg; |
|
646 for (mgli.toFirst();(mg=mgli.current());++mgli) |
|
647 { |
|
648 if (mg->members()) |
|
649 { |
|
650 writeBoxMemberList(t,prot,mg->members(),scope); |
|
651 } |
|
652 } |
|
653 } |
|
654 } |
|
655 } |
|
656 |
|
657 void DotNode::writeBox(QTextStream &t, |
|
658 GraphType gt, |
|
659 GraphOutputFormat /*format*/, |
|
660 bool hasNonReachableChildren, |
|
661 bool reNumber) |
|
662 { |
|
663 const char *labCol = |
|
664 m_url.isEmpty() ? "grey75" : // non link |
|
665 ( |
|
666 (hasNonReachableChildren) ? "red" : "black" |
|
667 ); |
|
668 t << " Node" << reNumberNode(m_number,reNumber) << " [label=\""; |
|
669 |
|
670 if (m_classDef && Config_getBool("UML_LOOK") && |
|
671 (gt==Inheritance || gt==Collaboration)) |
|
672 { |
|
673 t << "{" << convertLabel(m_label); |
|
674 t << "\\n|"; |
|
675 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubAttribs),m_classDef); |
|
676 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubStaticAttribs),m_classDef); |
|
677 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::properties),m_classDef); |
|
678 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberList::pacAttribs),m_classDef); |
|
679 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberList::pacStaticAttribs),m_classDef); |
|
680 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proAttribs),m_classDef); |
|
681 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proStaticAttribs),m_classDef); |
|
682 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priAttribs),m_classDef); |
|
683 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priStaticAttribs),m_classDef); |
|
684 t << "|"; |
|
685 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubMethods),m_classDef); |
|
686 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubStaticMethods),m_classDef); |
|
687 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberList::pubSlots),m_classDef); |
|
688 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberList::pacMethods),m_classDef); |
|
689 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberList::pacStaticMethods),m_classDef); |
|
690 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proMethods),m_classDef); |
|
691 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proStaticMethods),m_classDef); |
|
692 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberList::proSlots),m_classDef); |
|
693 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priMethods),m_classDef); |
|
694 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priStaticMethods),m_classDef); |
|
695 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberList::priSlots),m_classDef); |
|
696 if (m_classDef->getMemberGroupSDict()) |
|
697 { |
|
698 MemberGroupSDict::Iterator mgdi(*m_classDef->getMemberGroupSDict()); |
|
699 MemberGroup *mg; |
|
700 for (mgdi.toFirst();(mg=mgdi.current());++mgdi) |
|
701 { |
|
702 if (mg->members()) |
|
703 { |
|
704 writeBoxMemberList(t,'*',mg->members(),m_classDef); |
|
705 } |
|
706 } |
|
707 } |
|
708 t << "}"; |
|
709 } |
|
710 else // standard look |
|
711 { |
|
712 t << convertLabel(m_label); |
|
713 } |
|
714 t << "\",height=0.2,width=0.4"; |
|
715 if (m_isRoot) |
|
716 { |
|
717 t << ",color=\"black\", fillcolor=\"grey75\", style=\"filled\" fontcolor=\"black\""; |
|
718 } |
|
719 else |
|
720 { |
|
721 if (!Config_getBool("DOT_TRANSPARENT")) |
|
722 { |
|
723 t << ",color=\"" << labCol << "\", fillcolor=\"white\", style=\"filled\""; |
|
724 } |
|
725 else |
|
726 { |
|
727 t << ",color=\"" << labCol << "\""; |
|
728 } |
|
729 if (!m_url.isEmpty()) |
|
730 { |
|
731 int anchorPos = m_url.findRev('#'); |
|
732 if (anchorPos==-1) |
|
733 { |
|
734 t << ",URL=\"" << m_url << Doxygen::htmlFileExtension << "\""; |
|
735 } |
|
736 else |
|
737 { |
|
738 t << ",URL=\"" << m_url.left(anchorPos) << Doxygen::htmlFileExtension |
|
739 << m_url.right(m_url.length()-anchorPos) << "\""; |
|
740 } |
|
741 } |
|
742 if (!m_tooltip.isEmpty()) |
|
743 { |
|
744 t << ",tooltip=\"" << escapeTooltip(m_tooltip) << "\""; |
|
745 } |
|
746 } |
|
747 t << "];" << endl; |
|
748 } |
|
749 |
|
750 void DotNode::writeArrow(QTextStream &t, |
|
751 GraphType gt, |
|
752 GraphOutputFormat format, |
|
753 DotNode *cn, |
|
754 EdgeInfo *ei, |
|
755 bool topDown, |
|
756 bool pointBack, |
|
757 bool reNumber |
|
758 ) |
|
759 { |
|
760 t << " Node"; |
|
761 if (topDown) |
|
762 t << reNumberNode(cn->number(),reNumber); |
|
763 else |
|
764 t << reNumberNode(m_number,reNumber); |
|
765 t << " -> Node"; |
|
766 if (topDown) |
|
767 t << reNumberNode(m_number,reNumber); |
|
768 else |
|
769 t << reNumberNode(cn->number(),reNumber); |
|
770 t << " ["; |
|
771 if (pointBack) t << "dir=back,"; |
|
772 t << "color=\"" << edgeColorMap[ei->m_color] |
|
773 << "\",fontsize=\"" << FONTSIZE << "\",style=\"" << edgeStyleMap[ei->m_style] << "\""; |
|
774 if (!ei->m_label.isEmpty()) |
|
775 { |
|
776 t << ",label=\"" << convertLabel(ei->m_label) << "\""; |
|
777 } |
|
778 if (Config_getBool("UML_LOOK") && |
|
779 arrowStyle[ei->m_color] && |
|
780 (gt==Inheritance || gt==Collaboration) |
|
781 ) |
|
782 { |
|
783 if (pointBack) |
|
784 t << ",arrowtail=\"" << arrowStyle[ei->m_color] << "\""; |
|
785 else |
|
786 t << ",arrowhead=\"" << arrowStyle[ei->m_color] << "\""; |
|
787 } |
|
788 |
|
789 if (format==BITMAP) t << ",fontname=\"" << FONTNAME << "\""; |
|
790 t << "];" << endl; |
|
791 } |
|
792 |
|
793 void DotNode::write(QTextStream &t, |
|
794 GraphType gt, |
|
795 GraphOutputFormat format, |
|
796 bool topDown, |
|
797 bool toChildren, |
|
798 bool backArrows, |
|
799 bool reNumber |
|
800 ) |
|
801 { |
|
802 //printf("DotNode::write(%d) name=%s this=%p written=%d\n",distance,m_label.data(),this,m_written); |
|
803 if (m_written) return; // node already written to the output |
|
804 if (!m_visible) return; // node is not visible |
|
805 writeBox(t,gt,format,m_truncated==Truncated,reNumber); |
|
806 m_written=TRUE; |
|
807 QList<DotNode> *nl = toChildren ? m_children : m_parents; |
|
808 if (nl) |
|
809 { |
|
810 if (toChildren) |
|
811 { |
|
812 QListIterator<DotNode> dnli1(*nl); |
|
813 QListIterator<EdgeInfo> dnli2(*m_edgeInfo); |
|
814 DotNode *cn; |
|
815 for (dnli1.toFirst();(cn=dnli1.current());++dnli1,++dnli2) |
|
816 { |
|
817 if (cn->isVisible()) |
|
818 { |
|
819 //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",cn->label().data()); |
|
820 writeArrow(t,gt,format,cn,dnli2.current(),topDown,backArrows,reNumber); |
|
821 } |
|
822 cn->write(t,gt,format,topDown,toChildren,backArrows,reNumber); |
|
823 } |
|
824 } |
|
825 else // render parents |
|
826 { |
|
827 QListIterator<DotNode> dnli(*nl); |
|
828 DotNode *pn; |
|
829 for (dnli.toFirst();(pn=dnli.current());++dnli) |
|
830 { |
|
831 if (pn->isVisible()) |
|
832 { |
|
833 //printf("write arrow %s%s%s\n",label().data(),backArrows?"<-":"->",pn->label().data()); |
|
834 writeArrow(t, |
|
835 gt, |
|
836 format, |
|
837 pn, |
|
838 pn->m_edgeInfo->at(pn->m_children->findRef(this)), |
|
839 FALSE, |
|
840 backArrows, |
|
841 reNumber |
|
842 ); |
|
843 } |
|
844 pn->write(t,gt,format,TRUE,FALSE,backArrows,reNumber); |
|
845 } |
|
846 } |
|
847 } |
|
848 //printf("end DotNode::write(%d) name=%s\n",distance,m_label.data()); |
|
849 } |
|
850 |
|
851 void DotNode::writeXML(QTextStream &t,bool isClassGraph) |
|
852 { |
|
853 t << " <node id=\"" << m_number << "\">" << endl; |
|
854 t << " <label>" << convertToXML(m_label) << "</label>" << endl; |
|
855 if (!m_url.isEmpty()) |
|
856 { |
|
857 QCString url(m_url); |
|
858 char *refPtr = url.data(); |
|
859 char *urlPtr = strchr(url.data(),'$'); |
|
860 if (urlPtr) |
|
861 { |
|
862 *urlPtr++='\0'; |
|
863 t << " <link refid=\"" << convertToXML(urlPtr) << "\""; |
|
864 if (*refPtr!='\0') |
|
865 { |
|
866 t << " external=\"" << convertToXML(refPtr) << "\""; |
|
867 } |
|
868 t << "/>" << endl; |
|
869 } |
|
870 } |
|
871 if (m_children) |
|
872 { |
|
873 QListIterator<DotNode> nli(*m_children); |
|
874 QListIterator<EdgeInfo> eli(*m_edgeInfo); |
|
875 DotNode *childNode; |
|
876 EdgeInfo *edgeInfo; |
|
877 for (;(childNode=nli.current());++nli,++eli) |
|
878 { |
|
879 edgeInfo=eli.current(); |
|
880 t << " <childnode refid=\"" << childNode->m_number << "\" relation=\""; |
|
881 if (isClassGraph) |
|
882 { |
|
883 switch(edgeInfo->m_color) |
|
884 { |
|
885 case EdgeInfo::Blue: t << "public-inheritance"; break; |
|
886 case EdgeInfo::Green: t << "protected-inheritance"; break; |
|
887 case EdgeInfo::Red: t << "private-inheritance"; break; |
|
888 case EdgeInfo::Purple: t << "usage"; break; |
|
889 case EdgeInfo::Orange: t << "template-instance"; break; |
|
890 case EdgeInfo::Grey: ASSERT(0); break; |
|
891 } |
|
892 } |
|
893 else // include graph |
|
894 { |
|
895 t << "include"; |
|
896 } |
|
897 t << "\">" << endl; |
|
898 if (!edgeInfo->m_label.isEmpty()) |
|
899 { |
|
900 int p=0; |
|
901 int ni; |
|
902 while ((ni=edgeInfo->m_label.find('\n',p))!=-1) |
|
903 { |
|
904 t << " <edgelabel>" |
|
905 << convertToXML(edgeInfo->m_label.mid(p,ni-p)) |
|
906 << "</edgelabel>" << endl; |
|
907 p=ni+1; |
|
908 } |
|
909 t << " <edgelabel>" |
|
910 << convertToXML(edgeInfo->m_label.right(edgeInfo->m_label.length()-p)) |
|
911 << "</edgelabel>" << endl; |
|
912 } |
|
913 t << " </childnode>" << endl; |
|
914 } |
|
915 } |
|
916 t << " </node>" << endl; |
|
917 } |
|
918 |
|
919 void DotNode::writeXML(XmlStream &xt, bool isClassGraph) |
|
920 { |
|
921 QString idNumber; |
|
922 idNumber.setNum(m_number); |
|
923 XmlElement nodeElem(xt, "node", "id", idNumber); |
|
924 { |
|
925 XmlElement nodeLabel(xt, "label"); |
|
926 xt << m_label; |
|
927 } |
|
928 if (!m_url.isEmpty()) { |
|
929 QCString url(m_url); |
|
930 char *refPtr = url.data(); |
|
931 char *urlPtr = strchr(url.data(),'$'); |
|
932 if (urlPtr) { |
|
933 *urlPtr++='\0'; |
|
934 AttributeMap linkAttrs; |
|
935 linkAttrs["refid"] = convertToXML(urlPtr); |
|
936 if (*refPtr!='\0') { |
|
937 linkAttrs["external"] = convertToXML(refPtr); |
|
938 } |
|
939 XmlElement(xt, "link", linkAttrs); |
|
940 } |
|
941 } |
|
942 if (m_children) { |
|
943 QListIterator<DotNode> nli(*m_children); |
|
944 QListIterator<EdgeInfo> eli(*m_edgeInfo); |
|
945 DotNode *childNode; |
|
946 EdgeInfo *edgeInfo; |
|
947 for (;(childNode=nli.current());++nli,++eli) { |
|
948 edgeInfo=eli.current(); |
|
949 AttributeMap childAttrs; |
|
950 QString childIdNumber; |
|
951 childIdNumber.setNum(childNode->m_number); |
|
952 childAttrs["refid"] = childIdNumber; |
|
953 if (isClassGraph) { |
|
954 switch(edgeInfo->m_color) { |
|
955 case EdgeInfo::Blue: childAttrs["relation"] = "public-inheritance"; break; |
|
956 case EdgeInfo::Green: childAttrs["relation"] = "protected-inheritance"; break; |
|
957 case EdgeInfo::Red: childAttrs["relation"] = "private-inheritance"; break; |
|
958 case EdgeInfo::Purple: childAttrs["relation"] = "usage"; break; |
|
959 case EdgeInfo::Orange: childAttrs["relation"] = "template-instance"; break; |
|
960 case EdgeInfo::Grey: ASSERT(0); break; |
|
961 } |
|
962 } else { |
|
963 // include graph |
|
964 childAttrs["relation"] = "include"; |
|
965 } |
|
966 XmlElement childnodeElem(xt, "childnode", childAttrs); |
|
967 if (!edgeInfo->m_label.isEmpty()) { |
|
968 int p=0; |
|
969 int ni; |
|
970 while ((ni=edgeInfo->m_label.find('\n',p))!=-1) { |
|
971 XmlElement edgelabelElem(xt, "edgelabel"); |
|
972 xt << edgeInfo->m_label.mid(p,ni-p); |
|
973 p=ni+1; |
|
974 } |
|
975 XmlElement edgelabelElem(xt, "edgelabel"); |
|
976 xt << edgeInfo->m_label.right(edgeInfo->m_label.length()-p); |
|
977 } |
|
978 } |
|
979 } |
|
980 } |
|
981 |
|
982 void DotNode::writeXMLDITA(XmlStream &xt, bool isClassGraph) |
|
983 { |
|
984 QString idNumber; |
|
985 idNumber.setNum(m_number); |
|
986 XmlElement nodeElem(xt, "node", "id", idNumber); |
|
987 { |
|
988 XmlElement nodeLabel(xt, "label"); |
|
989 xt << m_label; |
|
990 } |
|
991 if (!m_url.isEmpty()) { |
|
992 QCString url(m_url); |
|
993 char *refPtr = url.data(); |
|
994 char *urlPtr = strchr(url.data(),'$'); |
|
995 if (urlPtr) { |
|
996 *urlPtr++='\0'; |
|
997 AttributeMap linkAttrs; |
|
998 linkAttrs["refid"] = convertToXML(urlPtr); |
|
999 if (*refPtr!='\0') { |
|
1000 linkAttrs["external"] = convertToXML(refPtr); |
|
1001 } |
|
1002 XmlElement(xt, "link", linkAttrs); |
|
1003 } |
|
1004 } |
|
1005 if (m_children) { |
|
1006 QListIterator<DotNode> nli(*m_children); |
|
1007 QListIterator<EdgeInfo> eli(*m_edgeInfo); |
|
1008 DotNode *childNode; |
|
1009 EdgeInfo *edgeInfo; |
|
1010 for (;(childNode=nli.current());++nli,++eli) { |
|
1011 edgeInfo=eli.current(); |
|
1012 AttributeMap childAttrs; |
|
1013 QString childIdNumber; |
|
1014 childIdNumber.setNum(childNode->m_number); |
|
1015 childAttrs["refid"] = childIdNumber; |
|
1016 if (isClassGraph) { |
|
1017 switch(edgeInfo->m_color) { |
|
1018 case EdgeInfo::Blue: childAttrs["relation"] = "public-inheritance"; break; |
|
1019 case EdgeInfo::Green: childAttrs["relation"] = "protected-inheritance"; break; |
|
1020 case EdgeInfo::Red: childAttrs["relation"] = "private-inheritance"; break; |
|
1021 case EdgeInfo::Purple: childAttrs["relation"] = "usage"; break; |
|
1022 case EdgeInfo::Orange: childAttrs["relation"] = "template-instance"; break; |
|
1023 case EdgeInfo::Grey: ASSERT(0); break; |
|
1024 } |
|
1025 } else { |
|
1026 // include graph |
|
1027 childAttrs["relation"] = "include"; |
|
1028 } |
|
1029 XmlElement childnodeElem(xt, "childnode", childAttrs); |
|
1030 if (!edgeInfo->m_label.isEmpty()) { |
|
1031 int p=0; |
|
1032 int ni; |
|
1033 while ((ni=edgeInfo->m_label.find('\n',p))!=-1) { |
|
1034 XmlElement edgelabelElem(xt, "edgelabel"); |
|
1035 xt << edgeInfo->m_label.mid(p,ni-p); |
|
1036 p=ni+1; |
|
1037 } |
|
1038 XmlElement edgelabelElem(xt, "edgelabel"); |
|
1039 xt << edgeInfo->m_label.right(edgeInfo->m_label.length()-p); |
|
1040 } |
|
1041 } |
|
1042 } |
|
1043 } |
|
1044 |
|
1045 void DotNode::writeDEF(QTextStream &t) |
|
1046 { |
|
1047 const char* nodePrefix = " node-"; |
|
1048 |
|
1049 t << " node = {" << endl; |
|
1050 t << nodePrefix << "id = " << m_number << ';' << endl; |
|
1051 t << nodePrefix << "label = '" << m_label << "';" << endl; |
|
1052 |
|
1053 if (!m_url.isEmpty()) |
|
1054 { |
|
1055 QCString url(m_url); |
|
1056 char *refPtr = url.data(); |
|
1057 char *urlPtr = strchr(url.data(),'$'); |
|
1058 if (urlPtr) |
|
1059 { |
|
1060 *urlPtr++='\0'; |
|
1061 t << nodePrefix << "link = {" << endl << " " |
|
1062 << nodePrefix << "link-id = '" << urlPtr << "';" << endl; |
|
1063 |
|
1064 if (*refPtr!='\0') |
|
1065 { |
|
1066 t << " " << nodePrefix << "link-external = '" |
|
1067 << refPtr << "';" << endl; |
|
1068 } |
|
1069 t << " };" << endl; |
|
1070 } |
|
1071 } |
|
1072 if (m_children) |
|
1073 { |
|
1074 QListIterator<DotNode> nli(*m_children); |
|
1075 QListIterator<EdgeInfo> eli(*m_edgeInfo); |
|
1076 DotNode *childNode; |
|
1077 EdgeInfo *edgeInfo; |
|
1078 for (;(childNode=nli.current());++nli,++eli) |
|
1079 { |
|
1080 edgeInfo=eli.current(); |
|
1081 t << " node-child = {" << endl; |
|
1082 t << " child-id = '" << childNode->m_number << "';" << endl; |
|
1083 t << " relation = "; |
|
1084 |
|
1085 switch(edgeInfo->m_color) |
|
1086 { |
|
1087 case EdgeInfo::Blue: t << "public-inheritance"; break; |
|
1088 case EdgeInfo::Green: t << "protected-inheritance"; break; |
|
1089 case EdgeInfo::Red: t << "private-inheritance"; break; |
|
1090 case EdgeInfo::Purple: t << "usage"; break; |
|
1091 case EdgeInfo::Orange: t << "template-instance"; break; |
|
1092 case EdgeInfo::Grey: ASSERT(0); break; |
|
1093 } |
|
1094 t << ';' << endl; |
|
1095 |
|
1096 if (!edgeInfo->m_label.isEmpty()) |
|
1097 { |
|
1098 t << " edgelabel = <<_EnD_oF_dEf_TeXt_" << endl |
|
1099 << edgeInfo->m_label << endl |
|
1100 << "_EnD_oF_dEf_TeXt_;" << endl; |
|
1101 } |
|
1102 t << " }; /* node-child */" << endl; |
|
1103 } /* for (;childNode...) */ |
|
1104 } |
|
1105 t << " }; /* node */" << endl; |
|
1106 } |
|
1107 |
|
1108 |
|
1109 void DotNode::clearWriteFlag() |
|
1110 { |
|
1111 m_written=FALSE; |
|
1112 if (m_parents!=0) |
|
1113 { |
|
1114 QListIterator<DotNode> dnlip(*m_parents); |
|
1115 DotNode *pn; |
|
1116 for (dnlip.toFirst();(pn=dnlip.current());++dnlip) |
|
1117 { |
|
1118 if (pn->m_written) |
|
1119 { |
|
1120 pn->clearWriteFlag(); |
|
1121 } |
|
1122 } |
|
1123 } |
|
1124 if (m_children!=0) |
|
1125 { |
|
1126 QListIterator<DotNode> dnlic(*m_children); |
|
1127 DotNode *cn; |
|
1128 for (dnlic.toFirst();(cn=dnlic.current());++dnlic) |
|
1129 { |
|
1130 if (cn->m_written) |
|
1131 { |
|
1132 cn->clearWriteFlag(); |
|
1133 } |
|
1134 } |
|
1135 } |
|
1136 } |
|
1137 |
|
1138 void DotNode::colorConnectedNodes(int curColor) |
|
1139 { |
|
1140 if (m_children) |
|
1141 { |
|
1142 QListIterator<DotNode> dnlic(*m_children); |
|
1143 DotNode *cn; |
|
1144 for (dnlic.toFirst();(cn=dnlic.current());++dnlic) |
|
1145 { |
|
1146 if (cn->m_subgraphId==-1) // uncolored child node |
|
1147 { |
|
1148 cn->m_subgraphId=curColor; |
|
1149 cn->markAsVisible(); |
|
1150 cn->colorConnectedNodes(curColor); |
|
1151 //printf("coloring node %s (%p): %d\n",cn->m_label.data(),cn,cn->m_subgraphId); |
|
1152 } |
|
1153 } |
|
1154 } |
|
1155 |
|
1156 if (m_parents) |
|
1157 { |
|
1158 QListIterator<DotNode> dnlip(*m_parents); |
|
1159 DotNode *pn; |
|
1160 for (dnlip.toFirst();(pn=dnlip.current());++dnlip) |
|
1161 { |
|
1162 if (pn->m_subgraphId==-1) // uncolored parent node |
|
1163 { |
|
1164 pn->m_subgraphId=curColor; |
|
1165 pn->markAsVisible(); |
|
1166 pn->colorConnectedNodes(curColor); |
|
1167 //printf("coloring node %s (%p): %d\n",pn->m_label.data(),pn,pn->m_subgraphId); |
|
1168 } |
|
1169 } |
|
1170 } |
|
1171 } |
|
1172 |
|
1173 const DotNode *DotNode::findDocNode() const |
|
1174 { |
|
1175 if (!m_url.isEmpty()) return this; |
|
1176 //printf("findDocNode(): `%s'\n",m_label.data()); |
|
1177 if (m_parents) |
|
1178 { |
|
1179 QListIterator<DotNode> dnli(*m_parents); |
|
1180 DotNode *pn; |
|
1181 for (dnli.toFirst();(pn=dnli.current());++dnli) |
|
1182 { |
|
1183 if (!pn->m_hasDoc) |
|
1184 { |
|
1185 pn->m_hasDoc=TRUE; |
|
1186 const DotNode *dn = pn->findDocNode(); |
|
1187 if (dn) return dn; |
|
1188 } |
|
1189 } |
|
1190 } |
|
1191 if (m_children) |
|
1192 { |
|
1193 QListIterator<DotNode> dnli(*m_children); |
|
1194 DotNode *cn; |
|
1195 for (dnli.toFirst();(cn=dnli.current());++dnli) |
|
1196 { |
|
1197 if (!cn->m_hasDoc) |
|
1198 { |
|
1199 cn->m_hasDoc=TRUE; |
|
1200 const DotNode *dn = cn->findDocNode(); |
|
1201 if (dn) return dn; |
|
1202 } |
|
1203 } |
|
1204 } |
|
1205 return 0; |
|
1206 } |
|
1207 |
|
1208 //-------------------------------------------------------------------- |
|
1209 |
|
1210 int DotGfxHierarchyTable::m_curNodeNumber; |
|
1211 |
|
1212 void DotGfxHierarchyTable::writeGraph(QTextStream &out,const char *path) const |
|
1213 { |
|
1214 //printf("DotGfxHierarchyTable::writeGraph(%s)\n",name); |
|
1215 //printf("m_rootNodes=%p count=%d\n",m_rootNodes,m_rootNodes->count()); |
|
1216 if (m_rootSubgraphs->count()==0) return; |
|
1217 |
|
1218 QDir d(path); |
|
1219 // store the original directory |
|
1220 if (!d.exists()) |
|
1221 { |
|
1222 err("Error: Output dir %s does not exist!\n",path); exit(1); |
|
1223 } |
|
1224 setDotFontPath(d.absPath()); |
|
1225 //QCString oldDir = convertToQCString(QDir::currentDirPath()); |
|
1226 // go to the html output directory (i.e. path) |
|
1227 //QDir::setCurrent(d.absPath()); |
|
1228 //QDir thisDir; |
|
1229 |
|
1230 // put each connected subgraph of the hierarchy in a row of the HTML output |
|
1231 out << "<table border=\"0\" cellspacing=\"10\" cellpadding=\"0\">" << endl; |
|
1232 |
|
1233 QListIterator<DotNode> dnli(*m_rootSubgraphs); |
|
1234 DotNode *n; |
|
1235 int count=0; |
|
1236 for (dnli.toFirst();(n=dnli.current());++dnli) |
|
1237 { |
|
1238 QCString baseName; |
|
1239 QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT"); |
|
1240 baseName.sprintf("inherit_graph_%d",count++); |
|
1241 baseName = convertNameToFile(baseName); |
|
1242 QCString imgName = baseName+"."+ imgExt; |
|
1243 QCString mapName = baseName+".map"; |
|
1244 QCString absImgName = QCString(d.absPath().data())+"/"+imgName; |
|
1245 QCString absMapName = QCString(d.absPath().data())+"/"+mapName; |
|
1246 QCString absBaseName = QCString(d.absPath().data())+"/"+baseName; |
|
1247 QListIterator<DotNode> dnli2(*m_rootNodes); |
|
1248 DotNode *node; |
|
1249 |
|
1250 // compute md5 checksum of the graph were are about to generate |
|
1251 QString theGraph; |
|
1252 QTextStream md5stream(&theGraph,IO_WriteOnly); |
|
1253 md5stream.setEncoding(md5stream.UnicodeUTF8); |
|
1254 writeGraphHeader(md5stream); |
|
1255 md5stream << " rankdir=LR;" << endl; |
|
1256 for (dnli2.toFirst();(node=dnli2.current());++dnli2) |
|
1257 { |
|
1258 if (node->m_subgraphId==n->m_subgraphId) |
|
1259 { |
|
1260 node->clearWriteFlag(); |
|
1261 } |
|
1262 } |
|
1263 for (dnli2.toFirst();(node=dnli2.current());++dnli2) |
|
1264 { |
|
1265 if (node->m_subgraphId==n->m_subgraphId) |
|
1266 { |
|
1267 node->write(md5stream,DotNode::Hierarchy,BITMAP,FALSE,TRUE,TRUE,TRUE); |
|
1268 } |
|
1269 } |
|
1270 writeGraphFooter(md5stream); |
|
1271 resetReNumbering(); |
|
1272 uchar md5_sig[16]; |
|
1273 QCString sigStr(33); |
|
1274 MD5Buffer((const unsigned char *)theGraph.ascii(),theGraph.length(),md5_sig); |
|
1275 MD5SigToString(md5_sig,sigStr.data(),33); |
|
1276 if (checkAndUpdateMd5Signature(absBaseName,sigStr) || |
|
1277 !QFileInfo(absMapName).exists()) |
|
1278 { |
|
1279 // image was new or has changed |
|
1280 QCString dotName=absBaseName+".dot"; |
|
1281 QFile f(dotName); |
|
1282 if (!f.open(IO_WriteOnly)) return; |
|
1283 QTextStream t(&f); |
|
1284 t.setEncoding(t.UnicodeUTF8); |
|
1285 t << theGraph; |
|
1286 f.close(); |
|
1287 resetReNumbering(); |
|
1288 |
|
1289 DotRunner dotRun(dotName); |
|
1290 dotRun.addJob(imgExt,absImgName); |
|
1291 dotRun.addJob(MAP_CMD,absMapName); |
|
1292 if (!dotRun.run()) |
|
1293 { |
|
1294 out << "</table>" << endl; |
|
1295 unsetDotFontPath(); |
|
1296 return; |
|
1297 } |
|
1298 checkDotResult(absImgName); |
|
1299 if (Config_getBool("DOT_CLEANUP")) d.remove(dotName); |
|
1300 } |
|
1301 Doxygen::indexList.addImageFile(imgName); |
|
1302 // write image and map in a table row |
|
1303 QCString mapLabel = escapeCharsInString(n->m_label,FALSE); |
|
1304 out << "<tr><td><img src=\"" << imgName << "\" border=\"0\" alt=\"\" usemap=\"#" |
|
1305 << mapLabel << "_map\"/>" << endl; |
|
1306 out << "<map name=\"" << mapLabel << "_map\" id=\"" << mapLabel << "\">" << endl; |
|
1307 convertMapFile(out,absMapName,""); |
|
1308 out << "</map></td></tr>" << endl; |
|
1309 //thisDir.remove(mapName); |
|
1310 } |
|
1311 out << "</table>" << endl; |
|
1312 |
|
1313 unsetDotFontPath(); |
|
1314 } |
|
1315 |
|
1316 void DotGfxHierarchyTable::addHierarchy(DotNode *n,ClassDef *cd,bool hideSuper) |
|
1317 { |
|
1318 //printf("addHierarchy `%s' baseClasses=%d\n",cd->name().data(),cd->baseClasses()->count()); |
|
1319 if (cd->subClasses()) |
|
1320 { |
|
1321 BaseClassListIterator bcli(*cd->subClasses()); |
|
1322 BaseClassDef *bcd; |
|
1323 for ( ; (bcd=bcli.current()) ; ++bcli ) |
|
1324 { |
|
1325 ClassDef *bClass=bcd->classDef; |
|
1326 //printf(" Trying sub class=`%s' usedNodes=%d\n",bClass->name().data(),m_usedNodes->count()); |
|
1327 if (bClass->isVisibleInHierarchy() && hasVisibleRoot(bClass->baseClasses())) |
|
1328 { |
|
1329 DotNode *bn; |
|
1330 //printf(" Node `%s' Found visible class=`%s'\n",n->m_label.data(), |
|
1331 // bClass->name().data()); |
|
1332 if ((bn=m_usedNodes->find(bClass->name()))) // node already present |
|
1333 { |
|
1334 if (n->m_children==0 || n->m_children->findRef(bn)==-1) // no arrow yet |
|
1335 { |
|
1336 n->addChild(bn,bcd->prot); |
|
1337 bn->addParent(n); |
|
1338 //printf(" Adding node %s to existing base node %s (c=%d,p=%d)\n", |
|
1339 // n->m_label.data(), |
|
1340 // bn->m_label.data(), |
|
1341 // bn->m_children ? bn->m_children->count() : 0, |
|
1342 // bn->m_parents ? bn->m_parents->count() : 0 |
|
1343 // ); |
|
1344 } |
|
1345 //else |
|
1346 //{ |
|
1347 // printf(" Class already has an arrow!\n"); |
|
1348 //} |
|
1349 } |
|
1350 else |
|
1351 { |
|
1352 QCString tmp_url=""; |
|
1353 if (bClass->isLinkable() && !bClass->isHidden()) |
|
1354 { |
|
1355 tmp_url=bClass->getReference()+"$"+bClass->getOutputFileBase(); |
|
1356 } |
|
1357 QCString tooltip = bClass->briefDescriptionAsTooltip(); |
|
1358 bn = new DotNode(m_curNodeNumber++, |
|
1359 bClass->displayName(), |
|
1360 tooltip, |
|
1361 tmp_url.data() |
|
1362 ); |
|
1363 n->addChild(bn,bcd->prot); |
|
1364 bn->addParent(n); |
|
1365 //printf(" Adding node %s to new base node %s (c=%d,p=%d)\n", |
|
1366 // n->m_label.data(), |
|
1367 // bn->m_label.data(), |
|
1368 // bn->m_children ? bn->m_children->count() : 0, |
|
1369 // bn->m_parents ? bn->m_parents->count() : 0 |
|
1370 // ); |
|
1371 //printf(" inserting %s (%p)\n",bClass->name().data(),bn); |
|
1372 m_usedNodes->insert(bClass->name(),bn); // add node to the used list |
|
1373 } |
|
1374 if (!bClass->visited && !hideSuper && bClass->subClasses()) |
|
1375 { |
|
1376 bool wasVisited=bClass->visited; |
|
1377 bClass->visited=TRUE; |
|
1378 addHierarchy(bn,bClass,wasVisited); |
|
1379 } |
|
1380 } |
|
1381 } |
|
1382 } |
|
1383 //printf("end addHierarchy\n"); |
|
1384 } |
|
1385 |
|
1386 void DotGfxHierarchyTable::addClassList(ClassSDict *cl) |
|
1387 { |
|
1388 ClassSDict::Iterator cli(*cl); |
|
1389 ClassDef *cd; |
|
1390 for (cli.toLast();(cd=cli.current());--cli) |
|
1391 { |
|
1392 //printf("Trying %s subClasses=%d\n",cd->name().data(),cd->subClasses()->count()); |
|
1393 if (!hasVisibleRoot(cd->baseClasses()) && |
|
1394 cd->isVisibleInHierarchy() |
|
1395 ) // root node in the forest |
|
1396 { |
|
1397 QCString tmp_url=""; |
|
1398 if (cd->isLinkable() && !cd->isHidden()) |
|
1399 { |
|
1400 tmp_url=cd->getReference()+"$"+cd->getOutputFileBase(); |
|
1401 } |
|
1402 //printf("Inserting root class %s\n",cd->name().data()); |
|
1403 QCString tooltip = cd->briefDescriptionAsTooltip(); |
|
1404 DotNode *n = new DotNode(m_curNodeNumber++, |
|
1405 cd->displayName(), |
|
1406 tooltip, |
|
1407 tmp_url.data()); |
|
1408 |
|
1409 //m_usedNodes->clear(); |
|
1410 m_usedNodes->insert(cd->name(),n); |
|
1411 m_rootNodes->insert(0,n); |
|
1412 if (!cd->visited && cd->subClasses()) |
|
1413 { |
|
1414 addHierarchy(n,cd,cd->visited); |
|
1415 cd->visited=TRUE; |
|
1416 } |
|
1417 } |
|
1418 } |
|
1419 } |
|
1420 |
|
1421 DotGfxHierarchyTable::DotGfxHierarchyTable() |
|
1422 { |
|
1423 m_curNodeNumber=0; |
|
1424 m_rootNodes = new QList<DotNode>; |
|
1425 m_usedNodes = new QDict<DotNode>(1009); |
|
1426 m_usedNodes->setAutoDelete(TRUE); |
|
1427 m_rootSubgraphs = new DotNodeList; |
|
1428 |
|
1429 // build a graph with each class as a node and the inheritance relations |
|
1430 // as edges |
|
1431 initClassHierarchy(Doxygen::classSDict); |
|
1432 initClassHierarchy(Doxygen::hiddenClasses); |
|
1433 addClassList(Doxygen::classSDict); |
|
1434 addClassList(Doxygen::hiddenClasses); |
|
1435 // m_usedNodes now contains all nodes in the graph |
|
1436 |
|
1437 // color the graph into a set of independent subgraphs |
|
1438 bool done=FALSE; |
|
1439 int curColor=0; |
|
1440 QListIterator<DotNode> dnli(*m_rootNodes); |
|
1441 while (!done) // there are still nodes to color |
|
1442 { |
|
1443 DotNode *n; |
|
1444 done=TRUE; // we are done unless there are still uncolored nodes |
|
1445 for (dnli.toLast();(n=dnli.current());--dnli) |
|
1446 { |
|
1447 if (n->m_subgraphId==-1) // not yet colored |
|
1448 { |
|
1449 //printf("Starting at node %s (%p): %d\n",n->m_label.data(),n,curColor); |
|
1450 done=FALSE; // still uncolored nodes |
|
1451 n->m_subgraphId=curColor; |
|
1452 n->markAsVisible(); |
|
1453 n->colorConnectedNodes(curColor); |
|
1454 curColor++; |
|
1455 const DotNode *dn=n->findDocNode(); |
|
1456 if (dn!=0) |
|
1457 m_rootSubgraphs->inSort(dn); |
|
1458 else |
|
1459 m_rootSubgraphs->inSort(n); |
|
1460 } |
|
1461 } |
|
1462 } |
|
1463 |
|
1464 //printf("Number of independent subgraphs: %d\n",curColor); |
|
1465 //QListIterator<DotNode> dnli2(*m_rootSubgraphs); |
|
1466 //DotNode *n; |
|
1467 //for (dnli2.toFirst();(n=dnli2.current());++dnli2) |
|
1468 //{ |
|
1469 // printf("Node %s color=%d (c=%d,p=%d)\n", |
|
1470 // n->m_label.data(),n->m_subgraphId, |
|
1471 // n->m_children?n->m_children->count():0, |
|
1472 // n->m_parents?n->m_parents->count():0); |
|
1473 //} |
|
1474 } |
|
1475 |
|
1476 DotGfxHierarchyTable::~DotGfxHierarchyTable() |
|
1477 { |
|
1478 //printf("DotGfxHierarchyTable::~DotGfxHierarchyTable\n"); |
|
1479 |
|
1480 //QDictIterator<DotNode> di(*m_usedNodes); |
|
1481 //DotNode *n; |
|
1482 //for (;(n=di.current());++di) |
|
1483 //{ |
|
1484 // printf("Node %p: %s\n",n,n->label().data()); |
|
1485 //} |
|
1486 |
|
1487 delete m_rootNodes; |
|
1488 delete m_usedNodes; |
|
1489 delete m_rootSubgraphs; |
|
1490 } |
|
1491 |
|
1492 //-------------------------------------------------------------------- |
|
1493 |
|
1494 int DotClassGraph::m_curNodeNumber = 0; |
|
1495 |
|
1496 void DotClassGraph::addClass(ClassDef *cd,DotNode *n,int prot, |
|
1497 const char *label,const char *usedName,const char *templSpec,bool base,int distance) |
|
1498 { |
|
1499 if (Config_getBool("HIDE_UNDOC_CLASSES") && !cd->isLinkable()) return; |
|
1500 |
|
1501 int edgeStyle = (label || prot==EdgeInfo::Orange) ? EdgeInfo::Dashed : EdgeInfo::Solid; |
|
1502 QCString className; |
|
1503 if (usedName) // name is a typedef |
|
1504 { |
|
1505 className=usedName; |
|
1506 } |
|
1507 else if (templSpec) // name has a template part |
|
1508 { |
|
1509 className=insertTemplateSpecifierInScope(cd->name(),templSpec); |
|
1510 } |
|
1511 else // just a normal name |
|
1512 { |
|
1513 className=cd->displayName(); |
|
1514 } |
|
1515 //printf("DotClassGraph::addClass(class=`%s',parent=%s,prot=%d,label=%s,dist=%d,usedName=%s,templSpec=%s,base=%d)\n", |
|
1516 // className.data(),n->m_label.data(),prot,label,distance,usedName,templSpec,base); |
|
1517 DotNode *bn = m_usedNodes->find(className); |
|
1518 if (bn) // class already inserted |
|
1519 { |
|
1520 if (base) |
|
1521 { |
|
1522 n->addChild(bn,prot,edgeStyle,label); |
|
1523 bn->addParent(n); |
|
1524 } |
|
1525 else |
|
1526 { |
|
1527 bn->addChild(n,prot,edgeStyle,label); |
|
1528 n->addParent(bn); |
|
1529 } |
|
1530 bn->setDistance(distance); |
|
1531 //printf(" add exiting node %s of %s\n",bn->m_label.data(),n->m_label.data()); |
|
1532 } |
|
1533 else // new class |
|
1534 { |
|
1535 QCString displayName=className; |
|
1536 if (Config_getBool("HIDE_SCOPE_NAMES")) displayName=stripScope(displayName); |
|
1537 QCString tmp_url; |
|
1538 if (cd->isLinkable() && !cd->isHidden()) |
|
1539 { |
|
1540 tmp_url=cd->getReference()+"$"+cd->getOutputFileBase(); |
|
1541 } |
|
1542 QCString tooltip = cd->briefDescriptionAsTooltip(); |
|
1543 bn = new DotNode(m_curNodeNumber++, |
|
1544 displayName, |
|
1545 tooltip, |
|
1546 tmp_url.data(), |
|
1547 FALSE, // rootNode |
|
1548 cd |
|
1549 ); |
|
1550 if (base) |
|
1551 { |
|
1552 n->addChild(bn,prot,edgeStyle,label); |
|
1553 bn->addParent(n); |
|
1554 } |
|
1555 else |
|
1556 { |
|
1557 bn->addChild(n,prot,edgeStyle,label); |
|
1558 n->addParent(bn); |
|
1559 } |
|
1560 bn->setDistance(distance); |
|
1561 m_usedNodes->insert(className,bn); |
|
1562 //printf(" add new child node `%s' to %s hidden=%d url=%s\n", |
|
1563 // className.data(),n->m_label.data(),cd->isHidden(),tmp_url.data()); |
|
1564 |
|
1565 buildGraph(cd,bn,base,distance+1); |
|
1566 } |
|
1567 } |
|
1568 |
|
1569 void DotClassGraph::determineTruncatedNodes(QList<DotNode> &queue,bool includeParents) |
|
1570 { |
|
1571 while (queue.count()>0) |
|
1572 { |
|
1573 DotNode *n = queue.take(0); |
|
1574 if (n->isVisible() && n->isTruncated()==DotNode::Unknown) |
|
1575 { |
|
1576 bool truncated = FALSE; |
|
1577 if (n->m_children) |
|
1578 { |
|
1579 QListIterator<DotNode> li(*n->m_children); |
|
1580 DotNode *dn; |
|
1581 for (li.toFirst();(dn=li.current());++li) |
|
1582 { |
|
1583 if (!dn->isVisible()) |
|
1584 truncated = TRUE; |
|
1585 else |
|
1586 queue.append(dn); |
|
1587 } |
|
1588 } |
|
1589 if (n->m_parents && includeParents) |
|
1590 { |
|
1591 QListIterator<DotNode> li(*n->m_parents); |
|
1592 DotNode *dn; |
|
1593 for (li.toFirst();(dn=li.current());++li) |
|
1594 { |
|
1595 if (!dn->isVisible()) |
|
1596 truncated = TRUE; |
|
1597 else |
|
1598 queue.append(dn); |
|
1599 } |
|
1600 } |
|
1601 n->markAsTruncated(truncated); |
|
1602 } |
|
1603 } |
|
1604 } |
|
1605 |
|
1606 bool DotClassGraph::determineVisibleNodes(DotNode *rootNode, |
|
1607 int maxNodes,bool includeParents) |
|
1608 { |
|
1609 QList<DotNode> childQueue; |
|
1610 QList<DotNode> parentQueue; |
|
1611 QArray<int> childTreeWidth; |
|
1612 QArray<int> parentTreeWidth; |
|
1613 childQueue.append(rootNode); |
|
1614 if (includeParents) parentQueue.append(rootNode); |
|
1615 bool firstNode=TRUE; // flag to force reprocessing rootNode in the parent loop |
|
1616 // despite being marked visible in the child loop |
|
1617 while ((childQueue.count()>0 || parentQueue.count()>0) && maxNodes>0) |
|
1618 { |
|
1619 static int maxDistance = Config_getInt("MAX_DOT_GRAPH_DEPTH"); |
|
1620 if (childQueue.count()>0) |
|
1621 { |
|
1622 DotNode *n = childQueue.take(0); |
|
1623 int distance = n->distance(); |
|
1624 if (!n->isVisible() && distance<maxDistance) // not yet processed |
|
1625 { |
|
1626 if (distance>0) |
|
1627 { |
|
1628 int oldSize=(int)childTreeWidth.size(); |
|
1629 if (distance>oldSize) |
|
1630 { |
|
1631 childTreeWidth.resize(QMAX(childTreeWidth.size(),(uint)distance)); |
|
1632 int i; for (i=oldSize;i<distance;i++) childTreeWidth[i]=0; |
|
1633 } |
|
1634 childTreeWidth[distance-1]+=n->label().length(); |
|
1635 } |
|
1636 n->markAsVisible(); |
|
1637 maxNodes--; |
|
1638 // add direct children |
|
1639 if (n->m_children) |
|
1640 { |
|
1641 QListIterator<DotNode> li(*n->m_children); |
|
1642 DotNode *dn; |
|
1643 for (li.toFirst();(dn=li.current());++li) |
|
1644 { |
|
1645 childQueue.append(dn); |
|
1646 } |
|
1647 } |
|
1648 } |
|
1649 } |
|
1650 if (includeParents && parentQueue.count()>0) |
|
1651 { |
|
1652 DotNode *n = parentQueue.take(0); |
|
1653 if ((!n->isVisible() || firstNode) && n->distance()<maxDistance) // not yet processed |
|
1654 { |
|
1655 firstNode=FALSE; |
|
1656 int distance = n->distance(); |
|
1657 if (distance>0) |
|
1658 { |
|
1659 int oldSize = (int)parentTreeWidth.size(); |
|
1660 if (distance>oldSize) |
|
1661 { |
|
1662 parentTreeWidth.resize(QMAX(parentTreeWidth.size(),(uint)distance)); |
|
1663 int i; for (i=oldSize;i<distance;i++) parentTreeWidth[i]=0; |
|
1664 } |
|
1665 parentTreeWidth[distance-1]+=n->label().length(); |
|
1666 } |
|
1667 n->markAsVisible(); |
|
1668 maxNodes--; |
|
1669 // add direct parents |
|
1670 if (n->m_parents) |
|
1671 { |
|
1672 QListIterator<DotNode> li(*n->m_parents); |
|
1673 DotNode *dn; |
|
1674 for (li.toFirst();(dn=li.current());++li) |
|
1675 { |
|
1676 parentQueue.append(dn); |
|
1677 } |
|
1678 } |
|
1679 } |
|
1680 } |
|
1681 } |
|
1682 if (Config_getBool("UML_LOOK")) return FALSE; // UML graph are always top to bottom |
|
1683 int maxWidth=0; |
|
1684 int maxHeight=(int)QMAX(childTreeWidth.size(),parentTreeWidth.size()); |
|
1685 uint i; |
|
1686 for (i=0;i<childTreeWidth.size();i++) |
|
1687 { |
|
1688 if (childTreeWidth.at(i)>maxWidth) maxWidth=childTreeWidth.at(i); |
|
1689 } |
|
1690 for (i=0;i<parentTreeWidth.size();i++) |
|
1691 { |
|
1692 if (parentTreeWidth.at(i)>maxWidth) maxWidth=parentTreeWidth.at(i); |
|
1693 } |
|
1694 //printf("max tree width=%d, max tree height=%d\n",maxWidth,maxHeight); |
|
1695 return maxWidth>80 && maxHeight<12; // used metric to decide to render the tree |
|
1696 // from left to right instead of top to bottom, |
|
1697 // with the idea to render very wide trees in |
|
1698 // left to right order. |
|
1699 } |
|
1700 |
|
1701 void DotClassGraph::buildGraph(ClassDef *cd,DotNode *n,bool base,int distance) |
|
1702 { |
|
1703 //printf("DocClassGraph::buildGraph(%s,distance=%d,base=%d)\n", |
|
1704 // cd->name().data(),distance,base); |
|
1705 // ---- Add inheritance relations |
|
1706 |
|
1707 if (m_graphType == DotNode::Inheritance || m_graphType==DotNode::Collaboration) |
|
1708 { |
|
1709 BaseClassList *bcl = base ? cd->baseClasses() : cd->subClasses(); |
|
1710 if (bcl) |
|
1711 { |
|
1712 BaseClassListIterator bcli(*bcl); |
|
1713 BaseClassDef *bcd; |
|
1714 for ( ; (bcd=bcli.current()) ; ++bcli ) |
|
1715 { |
|
1716 //printf("-------- inheritance relation %s->%s templ=`%s'\n", |
|
1717 // cd->name().data(),bcd->classDef->name().data(),bcd->templSpecifiers.data()); |
|
1718 addClass(bcd->classDef,n,bcd->prot,0,bcd->usedName, |
|
1719 bcd->templSpecifiers,base,distance); |
|
1720 } |
|
1721 } |
|
1722 } |
|
1723 if (m_graphType == DotNode::Collaboration) |
|
1724 { |
|
1725 // ---- Add usage relations |
|
1726 |
|
1727 UsesClassDict *dict = |
|
1728 base ? cd->usedImplementationClasses() : |
|
1729 cd->usedByImplementationClasses() |
|
1730 ; |
|
1731 if (dict) |
|
1732 { |
|
1733 UsesClassDictIterator ucdi(*dict); |
|
1734 UsesClassDef *ucd; |
|
1735 for (;(ucd=ucdi.current());++ucdi) |
|
1736 { |
|
1737 QCString label; |
|
1738 QDictIterator<void> dvi(*ucd->accessors); |
|
1739 const char *s; |
|
1740 bool first=TRUE; |
|
1741 int count=0; |
|
1742 int maxLabels=10; |
|
1743 for (;(s=dvi.currentKey()) && count<maxLabels;++dvi,++count) |
|
1744 { |
|
1745 if (first) |
|
1746 { |
|
1747 label=s; |
|
1748 first=FALSE; |
|
1749 } |
|
1750 else |
|
1751 { |
|
1752 label+=QCString("\n")+s; |
|
1753 } |
|
1754 } |
|
1755 if (count==maxLabels) label+="\n..."; |
|
1756 //printf("addClass: %s templSpec=%s\n",ucd->classDef->name().data(),ucd->templSpecifiers.data()); |
|
1757 addClass(ucd->classDef,n,EdgeInfo::Purple,label,0, |
|
1758 ucd->templSpecifiers,base,distance); |
|
1759 } |
|
1760 } |
|
1761 } |
|
1762 |
|
1763 // ---- Add template instantiation relations |
|
1764 |
|
1765 static bool templateRelations = Config_getBool("TEMPLATE_RELATIONS"); |
|
1766 if (templateRelations) |
|
1767 { |
|
1768 if (base) // template relations for base classes |
|
1769 { |
|
1770 ClassDef *templMaster=cd->templateMaster(); |
|
1771 if (templMaster) |
|
1772 { |
|
1773 QDictIterator<ClassDef> cli(*templMaster->getTemplateInstances()); |
|
1774 ClassDef *templInstance; |
|
1775 for (;(templInstance=cli.current());++cli) |
|
1776 { |
|
1777 if (templInstance==cd) |
|
1778 { |
|
1779 addClass(templMaster,n,EdgeInfo::Orange,cli.currentKey(),0, |
|
1780 0,TRUE,distance); |
|
1781 } |
|
1782 } |
|
1783 } |
|
1784 } |
|
1785 else // template relations for super classes |
|
1786 { |
|
1787 QDict<ClassDef> *templInstances = cd->getTemplateInstances(); |
|
1788 if (templInstances) |
|
1789 { |
|
1790 QDictIterator<ClassDef> cli(*templInstances); |
|
1791 ClassDef *templInstance; |
|
1792 for (;(templInstance=cli.current());++cli) |
|
1793 { |
|
1794 addClass(templInstance,n,EdgeInfo::Orange,cli.currentKey(),0, |
|
1795 0,FALSE,distance); |
|
1796 } |
|
1797 } |
|
1798 } |
|
1799 } |
|
1800 } |
|
1801 |
|
1802 DotClassGraph::DotClassGraph(ClassDef *cd,DotNode::GraphType t) |
|
1803 { |
|
1804 //printf("--------------- DotClassGraph::DotClassGraph `%s'\n",cd->displayName().data()); |
|
1805 m_graphType = t; |
|
1806 QCString tmp_url=""; |
|
1807 if (cd->isLinkable() && !cd->isHidden()) |
|
1808 { |
|
1809 tmp_url=cd->getReference()+"$"+cd->getOutputFileBase(); |
|
1810 } |
|
1811 QCString className = cd->displayName(); |
|
1812 QCString tooltip = cd->briefDescriptionAsTooltip(); |
|
1813 m_startNode = new DotNode(m_curNodeNumber++, |
|
1814 className, |
|
1815 tooltip, |
|
1816 tmp_url.data(), |
|
1817 TRUE, // is a root node |
|
1818 cd |
|
1819 ); |
|
1820 m_startNode->setDistance(0); |
|
1821 m_usedNodes = new QDict<DotNode>(1009); |
|
1822 m_usedNodes->insert(className,m_startNode); |
|
1823 |
|
1824 //printf("Root node %s\n",cd->name().data()); |
|
1825 //if (m_recDepth>0) |
|
1826 //{ |
|
1827 buildGraph(cd,m_startNode,TRUE,1); |
|
1828 if (t==DotNode::Inheritance) buildGraph(cd,m_startNode,FALSE,1); |
|
1829 //} |
|
1830 |
|
1831 static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES"); |
|
1832 //int directChildNodes = 1; |
|
1833 //if (m_startNode->m_children!=0) |
|
1834 // directChildNodes+=m_startNode->m_children->count(); |
|
1835 //if (t==DotNode::Inheritance && m_startNode->m_parents!=0) |
|
1836 // directChildNodes+=m_startNode->m_parents->count(); |
|
1837 //if (directChildNodes>maxNodes) maxNodes=directChildNodes; |
|
1838 //openNodeQueue.append(m_startNode); |
|
1839 m_lrRank = determineVisibleNodes(m_startNode,maxNodes,t==DotNode::Inheritance); |
|
1840 QList<DotNode> openNodeQueue; |
|
1841 openNodeQueue.append(m_startNode); |
|
1842 determineTruncatedNodes(openNodeQueue,t==DotNode::Inheritance); |
|
1843 |
|
1844 m_diskName = cd->getFileBase().copy(); |
|
1845 } |
|
1846 |
|
1847 bool DotClassGraph::isTrivial() const |
|
1848 { |
|
1849 if (m_graphType==DotNode::Inheritance) |
|
1850 return m_startNode->m_children==0 && m_startNode->m_parents==0; |
|
1851 else |
|
1852 return m_startNode->m_children==0; |
|
1853 } |
|
1854 |
|
1855 bool DotClassGraph::isTooBig() const |
|
1856 { |
|
1857 static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES"); |
|
1858 int numNodes = 0; |
|
1859 numNodes+= m_startNode->m_children ? m_startNode->m_children->count() : 0; |
|
1860 if (m_graphType==DotNode::Inheritance) |
|
1861 { |
|
1862 numNodes+= m_startNode->m_parents ? m_startNode->m_parents->count() : 0; |
|
1863 } |
|
1864 return numNodes>=maxNodes; |
|
1865 } |
|
1866 |
|
1867 DotClassGraph::~DotClassGraph() |
|
1868 { |
|
1869 deleteNodes(m_startNode); |
|
1870 delete m_usedNodes; |
|
1871 } |
|
1872 |
|
1873 /*! Computes a 16 byte md5 checksum for a given dot graph. |
|
1874 * The md5 checksum is returned as a 32 character ASCII string. |
|
1875 */ |
|
1876 QCString computeMd5Signature(DotNode *root, |
|
1877 DotNode::GraphType gt, |
|
1878 GraphOutputFormat format, |
|
1879 bool lrRank, |
|
1880 bool renderParents, |
|
1881 bool backArrows, |
|
1882 QCString &graphStr |
|
1883 ) |
|
1884 { |
|
1885 bool reNumber=TRUE; |
|
1886 |
|
1887 //printf("computeMd5Signature\n"); |
|
1888 QString buf; |
|
1889 QTextStream md5stream(&buf,IO_WriteOnly); |
|
1890 md5stream.setEncoding(md5stream.UnicodeUTF8); |
|
1891 writeGraphHeader(md5stream); |
|
1892 if (lrRank) |
|
1893 { |
|
1894 md5stream << " rankdir=LR;" << endl; |
|
1895 } |
|
1896 root->clearWriteFlag(); |
|
1897 root->write(md5stream, |
|
1898 gt, |
|
1899 format, |
|
1900 gt!=DotNode::CallGraph && gt!=DotNode::Dependency, |
|
1901 TRUE, |
|
1902 backArrows, |
|
1903 reNumber); |
|
1904 if (renderParents && root->m_parents) |
|
1905 { |
|
1906 QListIterator<DotNode> dnli(*root->m_parents); |
|
1907 DotNode *pn; |
|
1908 for (dnli.toFirst();(pn=dnli.current());++dnli) |
|
1909 { |
|
1910 if (pn->isVisible()) |
|
1911 { |
|
1912 root->writeArrow(md5stream, // stream |
|
1913 gt, // graph type |
|
1914 format, // output format |
|
1915 pn, // child node |
|
1916 pn->m_edgeInfo->at(pn->m_children->findRef(root)), // edge info |
|
1917 FALSE, // topDown? |
|
1918 backArrows, // point back? |
|
1919 reNumber // renumber nodes |
|
1920 ); |
|
1921 } |
|
1922 pn->write(md5stream, // stream |
|
1923 gt, // graph type |
|
1924 format, // output format |
|
1925 TRUE, // topDown? |
|
1926 FALSE, // toChildren? |
|
1927 backArrows, // backward pointing arrows? |
|
1928 reNumber // renumber nodes? |
|
1929 ); |
|
1930 } |
|
1931 } |
|
1932 writeGraphFooter(md5stream); |
|
1933 uchar md5_sig[16]; |
|
1934 QCString sigStr(33); |
|
1935 MD5Buffer((const unsigned char *)buf.ascii(),buf.length(),md5_sig); |
|
1936 MD5SigToString(md5_sig,sigStr.data(),33); |
|
1937 if (reNumber) |
|
1938 { |
|
1939 resetReNumbering(); |
|
1940 } |
|
1941 graphStr=buf.ascii(); |
|
1942 //printf("md5: %s | file: %s\n",sigStr,baseName.data()); |
|
1943 return sigStr; |
|
1944 } |
|
1945 |
|
1946 static bool updateDotGraph(DotNode *root, |
|
1947 DotNode::GraphType gt, |
|
1948 const QCString &baseName, |
|
1949 GraphOutputFormat format, |
|
1950 bool lrRank, |
|
1951 bool renderParents, |
|
1952 bool backArrows |
|
1953 ) |
|
1954 { |
|
1955 QCString theGraph; |
|
1956 // TODO: write graph to theGraph, then compute md5 checksum |
|
1957 QCString md5 = computeMd5Signature( |
|
1958 root,gt,format,lrRank,renderParents,backArrows,theGraph); |
|
1959 if (checkAndUpdateMd5Signature(baseName,md5)) // graph needs to be regenerated |
|
1960 { |
|
1961 QFile f; |
|
1962 f.setName(baseName+".dot"); |
|
1963 if (f.open(IO_WriteOnly)) |
|
1964 { |
|
1965 QTextStream t(&f); |
|
1966 t.setEncoding(t.UnicodeUTF8); |
|
1967 t << theGraph; |
|
1968 } |
|
1969 return TRUE; |
|
1970 } |
|
1971 return FALSE; |
|
1972 } |
|
1973 |
|
1974 QCString DotClassGraph::diskName() const |
|
1975 { |
|
1976 QCString result=m_diskName.copy(); |
|
1977 switch (m_graphType) |
|
1978 { |
|
1979 case DotNode::Collaboration: |
|
1980 result+="_coll_graph"; |
|
1981 break; |
|
1982 //case Interface: |
|
1983 // result+="_intf_graph"; |
|
1984 // break; |
|
1985 case DotNode::Inheritance: |
|
1986 result+="_inherit_graph"; |
|
1987 break; |
|
1988 default: |
|
1989 ASSERT(0); |
|
1990 break; |
|
1991 } |
|
1992 return result; |
|
1993 } |
|
1994 |
|
1995 QCString DotClassGraph::writeGraph(QTextStream &out, |
|
1996 GraphOutputFormat format, |
|
1997 const char *path, |
|
1998 const char *relPath, |
|
1999 bool /*isTBRank*/, |
|
2000 bool generateImageMap) const |
|
2001 { |
|
2002 QDir d(path); |
|
2003 // store the original directory |
|
2004 if (!d.exists()) |
|
2005 { |
|
2006 err("Error: Output dir %s does not exist!\n",path); exit(1); |
|
2007 } |
|
2008 setDotFontPath(d.absPath()); |
|
2009 |
|
2010 QCString baseName; |
|
2011 QCString mapName; |
|
2012 switch (m_graphType) |
|
2013 { |
|
2014 case DotNode::Collaboration: |
|
2015 mapName="coll_map"; |
|
2016 break; |
|
2017 //case Interface: |
|
2018 // mapName="intf_map"; |
|
2019 // break; |
|
2020 case DotNode::Inheritance: |
|
2021 mapName="inherit_map"; |
|
2022 break; |
|
2023 default: |
|
2024 ASSERT(0); |
|
2025 break; |
|
2026 } |
|
2027 baseName = convertNameToFile(diskName()); |
|
2028 QCString absBaseName = QCString(d.absPath().data())+"/"+baseName; |
|
2029 |
|
2030 QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT"); |
|
2031 |
|
2032 if (updateDotGraph(m_startNode, |
|
2033 m_graphType, |
|
2034 absBaseName, |
|
2035 format, |
|
2036 m_lrRank, |
|
2037 m_graphType==DotNode::Inheritance, |
|
2038 TRUE |
|
2039 ) |
|
2040 ) |
|
2041 { |
|
2042 if (format==BITMAP) // run dot to create a bitmap image |
|
2043 { |
|
2044 QCString dotArgs(maxCmdLine); |
|
2045 QCString absImgName = absBaseName+"."+imgExt; |
|
2046 |
|
2047 DotRunner dotRun(absBaseName+".dot"); |
|
2048 dotRun.addJob(imgExt,absImgName); |
|
2049 if (generateImageMap) dotRun.addJob(MAP_CMD,absBaseName+".map"); |
|
2050 if (!dotRun.run()) |
|
2051 { |
|
2052 unsetDotFontPath(); |
|
2053 return baseName; |
|
2054 } |
|
2055 checkDotResult(absImgName); |
|
2056 } |
|
2057 else if (format==EPS) // run dot to create a .eps image |
|
2058 { |
|
2059 DotRunner dotRun(absBaseName+".dot"); |
|
2060 dotRun.addJob("ps",absBaseName+".eps"); |
|
2061 |
|
2062 if (Config_getBool("USE_PDFLATEX")) |
|
2063 { |
|
2064 QCString epstopdfArgs(maxCmdLine); |
|
2065 epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"", |
|
2066 absBaseName.data(),absBaseName.data()); |
|
2067 dotRun.addPostProcessing("epstopdf",epstopdfArgs); |
|
2068 } |
|
2069 |
|
2070 if (!dotRun.run()) |
|
2071 { |
|
2072 unsetDotFontPath(); |
|
2073 return baseName; |
|
2074 } |
|
2075 } |
|
2076 if (Config_getBool("DOT_CLEANUP")) d.remove(baseName+".dot"); |
|
2077 } |
|
2078 Doxygen::indexList.addImageFile(baseName+"."+imgExt); |
|
2079 |
|
2080 if (format==BITMAP && generateImageMap) // produce HTML to include the image |
|
2081 { |
|
2082 QCString mapLabel = escapeCharsInString(m_startNode->m_label,FALSE)+"_"+ |
|
2083 escapeCharsInString(mapName,FALSE); |
|
2084 out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." |
|
2085 << imgExt << "\" border=\"0\" usemap=\"#" |
|
2086 << mapLabel << "\" alt=\""; |
|
2087 switch (m_graphType) |
|
2088 { |
|
2089 case DotNode::Collaboration: |
|
2090 out << "Collaboration graph"; |
|
2091 break; |
|
2092 case DotNode::Inheritance: |
|
2093 out << "Inheritance graph"; |
|
2094 break; |
|
2095 default: |
|
2096 ASSERT(0); |
|
2097 break; |
|
2098 } |
|
2099 out << "\"/></div>" << endl; |
|
2100 QString tmpstr; |
|
2101 QTextOStream tmpout(&tmpstr); |
|
2102 tmpout.setEncoding(tmpout.UnicodeUTF8); |
|
2103 convertMapFile(tmpout,absBaseName+".map",relPath); |
|
2104 if (!tmpstr.isEmpty()) |
|
2105 { |
|
2106 out << "<map name=\"" << mapLabel << "\" id=\"" << mapLabel << "\">" << endl; |
|
2107 out << tmpstr; |
|
2108 out << "</map>" << endl; |
|
2109 } |
|
2110 } |
|
2111 else if (format==EPS) // produce tex to include the .eps image |
|
2112 { |
|
2113 int width=420,height=600; |
|
2114 if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height)) |
|
2115 { |
|
2116 err("Error: Could not extract bounding box from .eps!\n"); |
|
2117 unsetDotFontPath(); |
|
2118 return baseName; |
|
2119 } |
|
2120 //printf("Got EPS size %d,%d\n",width,height); |
|
2121 int maxWidth = 400; /* approx. page width in points, excl. margins */ |
|
2122 int maxHeight = 400; /* approx. page height in points, excl. margins */ |
|
2123 out << "\\nopagebreak\n" |
|
2124 "\\begin{figure}[H]\n" |
|
2125 "\\begin{center}\n" |
|
2126 "\\leavevmode\n"; |
|
2127 if (width>maxWidth) |
|
2128 { |
|
2129 out << "\\includegraphics[width=" << maxWidth << "pt]"; |
|
2130 } |
|
2131 else if (height>maxHeight) |
|
2132 { |
|
2133 out << "\\includegraphics[height=" << maxHeight << "pt]"; |
|
2134 } |
|
2135 else |
|
2136 { |
|
2137 out << "\\includegraphics[width=" << width << "pt]"; |
|
2138 } |
|
2139 out << "{" << baseName << "}\n" |
|
2140 "\\end{center}\n" |
|
2141 "\\end{figure}\n"; |
|
2142 } |
|
2143 unsetDotFontPath(); |
|
2144 |
|
2145 return baseName; |
|
2146 } |
|
2147 |
|
2148 //-------------------------------------------------------------------- |
|
2149 |
|
2150 void DotClassGraph::writeXML(QTextStream &t) |
|
2151 { |
|
2152 QDictIterator<DotNode> dni(*m_usedNodes); |
|
2153 DotNode *node; |
|
2154 for (;(node=dni.current());++dni) |
|
2155 { |
|
2156 node->writeXML(t,TRUE); |
|
2157 } |
|
2158 } |
|
2159 |
|
2160 void DotClassGraph::writeXML(XmlStream &t) |
|
2161 { |
|
2162 QDictIterator<DotNode> dni(*m_usedNodes); |
|
2163 DotNode *node; |
|
2164 for (;(node=dni.current());++dni) |
|
2165 { |
|
2166 node->writeXML(t,TRUE); |
|
2167 } |
|
2168 } |
|
2169 |
|
2170 void DotClassGraph::writeXMLDITA(XmlStream &t) |
|
2171 { |
|
2172 QDictIterator<DotNode> dni(*m_usedNodes); |
|
2173 DotNode *node; |
|
2174 for (;(node=dni.current());++dni) |
|
2175 { |
|
2176 node->writeXMLDITA(t,TRUE); |
|
2177 } |
|
2178 } |
|
2179 |
|
2180 void DotClassGraph::writeDEF(QTextStream &t) |
|
2181 { |
|
2182 QDictIterator<DotNode> dni(*m_usedNodes); |
|
2183 DotNode *node; |
|
2184 for (;(node=dni.current());++dni) |
|
2185 { |
|
2186 node->writeDEF(t); |
|
2187 } |
|
2188 } |
|
2189 |
|
2190 //-------------------------------------------------------------------- |
|
2191 |
|
2192 int DotInclDepGraph::m_curNodeNumber = 0; |
|
2193 |
|
2194 void DotInclDepGraph::buildGraph(DotNode *n,FileDef *fd,int distance) |
|
2195 { |
|
2196 QList<IncludeInfo> *includeFiles = |
|
2197 m_inverse ? fd->includedByFileList() : fd->includeFileList(); |
|
2198 if (includeFiles) |
|
2199 { |
|
2200 QListIterator<IncludeInfo> ili(*includeFiles); |
|
2201 IncludeInfo *ii; |
|
2202 for (;(ii=ili.current());++ili) |
|
2203 { |
|
2204 FileDef *bfd = ii->fileDef; |
|
2205 QCString in = ii->includeName; |
|
2206 //printf(">>>> in=`%s' bfd=%p\n",ii->includeName.data(),bfd); |
|
2207 bool doc=TRUE,src=FALSE; |
|
2208 if (bfd) |
|
2209 { |
|
2210 in = bfd->absFilePath(); |
|
2211 doc = bfd->isLinkable() && !bfd->isHidden(); |
|
2212 src = bfd->generateSourceFile(); |
|
2213 } |
|
2214 if (doc || src || !Config_getBool("HIDE_UNDOC_RELATIONS")) |
|
2215 { |
|
2216 QCString url=""; |
|
2217 if (bfd) url=bfd->getOutputFileBase().copy(); |
|
2218 if (!doc && src) |
|
2219 { |
|
2220 url=bfd->getSourceFileBase(); |
|
2221 } |
|
2222 DotNode *bn = m_usedNodes->find(in); |
|
2223 if (bn) // file is already a node in the graph |
|
2224 { |
|
2225 n->addChild(bn,0,0,0); |
|
2226 bn->addParent(n); |
|
2227 bn->setDistance(distance); |
|
2228 } |
|
2229 else |
|
2230 { |
|
2231 QCString tmp_url; |
|
2232 QCString tooltip; |
|
2233 if (bfd) |
|
2234 { |
|
2235 tmp_url=doc || src ? bfd->getReference()+"$"+url : QCString(); |
|
2236 tooltip = bfd->briefDescriptionAsTooltip(); |
|
2237 } |
|
2238 bn = new DotNode( |
|
2239 m_curNodeNumber++, // n |
|
2240 ii->includeName, // label |
|
2241 tooltip, // tip |
|
2242 tmp_url, // url |
|
2243 FALSE, // rootNode |
|
2244 0 // cd |
|
2245 ); |
|
2246 n->addChild(bn,0,0,0); |
|
2247 bn->addParent(n); |
|
2248 m_usedNodes->insert(in,bn); |
|
2249 bn->setDistance(distance); |
|
2250 |
|
2251 if (bfd) buildGraph(bn,bfd,distance+1); |
|
2252 } |
|
2253 } |
|
2254 } |
|
2255 } |
|
2256 } |
|
2257 |
|
2258 void DotInclDepGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes) |
|
2259 { |
|
2260 while (queue.count()>0 && maxNodes>0) |
|
2261 { |
|
2262 static int maxDistance = Config_getInt("MAX_DOT_GRAPH_DEPTH"); |
|
2263 DotNode *n = queue.take(0); |
|
2264 if (!n->isVisible() && n->distance()<maxDistance) // not yet processed |
|
2265 { |
|
2266 n->markAsVisible(); |
|
2267 maxNodes--; |
|
2268 // add direct children |
|
2269 if (n->m_children) |
|
2270 { |
|
2271 QListIterator<DotNode> li(*n->m_children); |
|
2272 DotNode *dn; |
|
2273 for (li.toFirst();(dn=li.current());++li) |
|
2274 { |
|
2275 queue.append(dn); |
|
2276 } |
|
2277 } |
|
2278 } |
|
2279 } |
|
2280 } |
|
2281 |
|
2282 void DotInclDepGraph::determineTruncatedNodes(QList<DotNode> &queue) |
|
2283 { |
|
2284 while (queue.count()>0) |
|
2285 { |
|
2286 DotNode *n = queue.take(0); |
|
2287 if (n->isVisible() && n->isTruncated()==DotNode::Unknown) |
|
2288 { |
|
2289 bool truncated = FALSE; |
|
2290 if (n->m_children) |
|
2291 { |
|
2292 QListIterator<DotNode> li(*n->m_children); |
|
2293 DotNode *dn; |
|
2294 for (li.toFirst();(dn=li.current());++li) |
|
2295 { |
|
2296 if (!dn->isVisible()) |
|
2297 truncated = TRUE; |
|
2298 else |
|
2299 queue.append(dn); |
|
2300 } |
|
2301 } |
|
2302 n->markAsTruncated(truncated); |
|
2303 } |
|
2304 } |
|
2305 } |
|
2306 |
|
2307 |
|
2308 DotInclDepGraph::DotInclDepGraph(FileDef *fd,bool inverse) |
|
2309 { |
|
2310 m_maxDistance = 0; |
|
2311 m_inverse = inverse; |
|
2312 ASSERT(fd!=0); |
|
2313 m_diskName = fd->getFileBase().copy(); |
|
2314 QCString tmp_url=fd->getReference()+"$"+fd->getFileBase(); |
|
2315 m_startNode = new DotNode(m_curNodeNumber++, |
|
2316 fd->docName(), |
|
2317 "", |
|
2318 tmp_url.data(), |
|
2319 TRUE // root node |
|
2320 ); |
|
2321 m_startNode->setDistance(0); |
|
2322 m_usedNodes = new QDict<DotNode>(1009); |
|
2323 m_usedNodes->insert(fd->absFilePath(),m_startNode); |
|
2324 buildGraph(m_startNode,fd,1); |
|
2325 |
|
2326 static int nodes = Config_getInt("DOT_GRAPH_MAX_NODES"); |
|
2327 int maxNodes = nodes; |
|
2328 //int directChildNodes = 1; |
|
2329 //if (m_startNode->m_children!=0) |
|
2330 // directChildNodes+=m_startNode->m_children->count(); |
|
2331 //if (directChildNodes>maxNodes) maxNodes=directChildNodes; |
|
2332 QList<DotNode> openNodeQueue; |
|
2333 openNodeQueue.append(m_startNode); |
|
2334 determineVisibleNodes(openNodeQueue,maxNodes); |
|
2335 openNodeQueue.clear(); |
|
2336 openNodeQueue.append(m_startNode); |
|
2337 determineTruncatedNodes(openNodeQueue); |
|
2338 } |
|
2339 |
|
2340 DotInclDepGraph::~DotInclDepGraph() |
|
2341 { |
|
2342 deleteNodes(m_startNode); |
|
2343 delete m_usedNodes; |
|
2344 } |
|
2345 |
|
2346 QCString DotInclDepGraph::diskName() const |
|
2347 { |
|
2348 QCString result=m_diskName.copy(); |
|
2349 if (m_inverse) result+="_dep"; |
|
2350 result+="_incl"; |
|
2351 return convertNameToFile(result); |
|
2352 } |
|
2353 |
|
2354 QCString DotInclDepGraph::writeGraph(QTextStream &out, |
|
2355 GraphOutputFormat format, |
|
2356 const char *path, |
|
2357 const char *relPath, |
|
2358 bool generateImageMap |
|
2359 ) const |
|
2360 { |
|
2361 QDir d(path); |
|
2362 // store the original directory |
|
2363 if (!d.exists()) |
|
2364 { |
|
2365 err("Error: Output dir %s does not exist!\n",path); exit(1); |
|
2366 } |
|
2367 setDotFontPath(d.absPath()); |
|
2368 |
|
2369 QCString baseName=m_diskName; |
|
2370 if (m_inverse) baseName+="_dep"; |
|
2371 baseName+="_incl"; |
|
2372 baseName=convertNameToFile(baseName); |
|
2373 QCString mapName=escapeCharsInString(m_startNode->m_label,FALSE); |
|
2374 if (m_inverse) mapName+="dep"; |
|
2375 QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT"); |
|
2376 |
|
2377 QCString absBaseName = QCString(d.absPath())+"/"+baseName; |
|
2378 QCString absMapName = QCString(d.absPath())+"/"+mapName; |
|
2379 |
|
2380 if (updateDotGraph(m_startNode, |
|
2381 DotNode::Dependency, |
|
2382 absBaseName, |
|
2383 format, |
|
2384 FALSE, // lrRank |
|
2385 FALSE, // renderParents |
|
2386 m_inverse // backArrows |
|
2387 ) |
|
2388 ) |
|
2389 { |
|
2390 if (format==BITMAP) |
|
2391 { |
|
2392 // run dot to create a bitmap image |
|
2393 QCString dotArgs(maxCmdLine); |
|
2394 QCString absImgName=absBaseName+"."+imgExt; |
|
2395 DotRunner dotRun(absBaseName+".dot"); |
|
2396 dotRun.addJob(imgExt,absImgName); |
|
2397 if (generateImageMap) dotRun.addJob(MAP_CMD,absBaseName+".map"); |
|
2398 if (!dotRun.run()) |
|
2399 { |
|
2400 unsetDotFontPath(); |
|
2401 return baseName; |
|
2402 } |
|
2403 checkDotResult(absImgName); |
|
2404 } |
|
2405 else if (format==EPS) |
|
2406 { |
|
2407 // run dot to create a .eps image |
|
2408 DotRunner dotRun(absBaseName+".dot"); |
|
2409 dotRun.addJob("ps",absBaseName+".eps"); |
|
2410 if (Config_getBool("USE_PDFLATEX")) |
|
2411 { |
|
2412 QCString epstopdfArgs(maxCmdLine); |
|
2413 epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"", |
|
2414 absBaseName.data(),absBaseName.data()); |
|
2415 dotRun.addPostProcessing("epstopdf",epstopdfArgs); |
|
2416 } |
|
2417 if (!dotRun.run()) |
|
2418 { |
|
2419 unsetDotFontPath(); |
|
2420 return baseName; |
|
2421 } |
|
2422 } |
|
2423 } |
|
2424 Doxygen::indexList.addImageFile(baseName+"."+imgExt); |
|
2425 |
|
2426 if (format==BITMAP && generateImageMap) |
|
2427 { |
|
2428 out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." |
|
2429 << imgExt << "\" border=\"0\" usemap=\"#" |
|
2430 << mapName << "_map\" alt=\"\"/>"; |
|
2431 out << "</div>" << endl; |
|
2432 QString tmpstr; |
|
2433 QTextOStream tmpout(&tmpstr); |
|
2434 tmpout.setEncoding(tmpout.UnicodeUTF8); |
|
2435 convertMapFile(tmpout,absBaseName+".map",relPath); |
|
2436 if (!tmpstr.isEmpty()) |
|
2437 { |
|
2438 out << "<map name=\"" << mapName << "_map\" id=\"" << mapName << "\">" << endl; |
|
2439 out << tmpstr; |
|
2440 out << "</map>" << endl; |
|
2441 } |
|
2442 } |
|
2443 else if (format==EPS) |
|
2444 { |
|
2445 int width,height; |
|
2446 if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height)) |
|
2447 { |
|
2448 err("Error: Could not extract bounding box from .eps!\n"); |
|
2449 unsetDotFontPath(); |
|
2450 return baseName; |
|
2451 } |
|
2452 int maxWidth = 420; /* approx. page width in points */ |
|
2453 |
|
2454 out << "\\nopagebreak\n" |
|
2455 "\\begin{figure}[H]\n" |
|
2456 "\\begin{center}\n" |
|
2457 "\\leavevmode\n" |
|
2458 "\\includegraphics[width=" << QMIN(width/2,maxWidth) |
|
2459 << "pt]{" << baseName << "}\n" |
|
2460 "\\end{center}\n" |
|
2461 "\\end{figure}\n"; |
|
2462 } |
|
2463 |
|
2464 if (Config_getBool("DOT_CLEANUP")) d.remove(baseName+".dot"); |
|
2465 |
|
2466 unsetDotFontPath(); |
|
2467 return baseName; |
|
2468 } |
|
2469 |
|
2470 bool DotInclDepGraph::isTrivial() const |
|
2471 { |
|
2472 return m_startNode->m_children==0; |
|
2473 } |
|
2474 |
|
2475 bool DotInclDepGraph::isTooBig() const |
|
2476 { |
|
2477 static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES"); |
|
2478 int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0; |
|
2479 return numNodes>=maxNodes; |
|
2480 } |
|
2481 |
|
2482 void DotInclDepGraph::writeXML(QTextStream &t) |
|
2483 { |
|
2484 QDictIterator<DotNode> dni(*m_usedNodes); |
|
2485 DotNode *node; |
|
2486 for (;(node=dni.current());++dni) |
|
2487 { |
|
2488 node->writeXML(t,FALSE); |
|
2489 } |
|
2490 } |
|
2491 |
|
2492 void DotInclDepGraph::writeXML(XmlStream &xt) |
|
2493 { |
|
2494 QDictIterator<DotNode> dni(*m_usedNodes); |
|
2495 DotNode *node; |
|
2496 for (;(node=dni.current());++dni) |
|
2497 { |
|
2498 node->writeXML(xt,FALSE); |
|
2499 } |
|
2500 } |
|
2501 |
|
2502 //------------------------------------------------------------- |
|
2503 |
|
2504 int DotCallGraph::m_curNodeNumber = 0; |
|
2505 |
|
2506 void DotCallGraph::buildGraph(DotNode *n,MemberDef *md,int distance) |
|
2507 { |
|
2508 LockingPtr<MemberSDict> refs = m_inverse ? md->getReferencedByMembers() : md->getReferencesMembers(); |
|
2509 if (!refs.isNull()) |
|
2510 { |
|
2511 MemberSDict::Iterator mri(*refs); |
|
2512 MemberDef *rmd; |
|
2513 for (;(rmd=mri.current());++mri) |
|
2514 { |
|
2515 if (rmd->isFunction() || rmd->isSlot() || rmd->isSignal()) |
|
2516 { |
|
2517 QCString uniqueId; |
|
2518 uniqueId=rmd->getReference()+"$"+ |
|
2519 rmd->getOutputFileBase()+"#"+rmd->anchor(); |
|
2520 DotNode *bn = m_usedNodes->find(uniqueId); |
|
2521 if (bn) // file is already a node in the graph |
|
2522 { |
|
2523 n->addChild(bn,0,0,0); |
|
2524 bn->addParent(n); |
|
2525 bn->setDistance(distance); |
|
2526 } |
|
2527 else |
|
2528 { |
|
2529 QCString name; |
|
2530 if (Config_getBool("HIDE_SCOPE_NAMES")) |
|
2531 { |
|
2532 name = rmd->getOuterScope()==m_scope ? |
|
2533 rmd->name() : rmd->qualifiedName(); |
|
2534 } |
|
2535 else |
|
2536 { |
|
2537 name = rmd->qualifiedName(); |
|
2538 } |
|
2539 QCString tooltip = rmd->briefDescriptionAsTooltip(); |
|
2540 bn = new DotNode( |
|
2541 m_curNodeNumber++, |
|
2542 linkToText(name,FALSE), |
|
2543 tooltip, |
|
2544 uniqueId, |
|
2545 0 //distance |
|
2546 ); |
|
2547 n->addChild(bn,0,0,0); |
|
2548 bn->addParent(n); |
|
2549 bn->setDistance(distance); |
|
2550 m_usedNodes->insert(uniqueId,bn); |
|
2551 |
|
2552 buildGraph(bn,rmd,distance+1); |
|
2553 } |
|
2554 } |
|
2555 } |
|
2556 } |
|
2557 } |
|
2558 |
|
2559 void DotCallGraph::determineVisibleNodes(QList<DotNode> &queue, int &maxNodes) |
|
2560 { |
|
2561 while (queue.count()>0 && maxNodes>0) |
|
2562 { |
|
2563 static int maxDistance = Config_getInt("MAX_DOT_GRAPH_DEPTH"); |
|
2564 DotNode *n = queue.take(0); |
|
2565 if (!n->isVisible() && n->distance()<maxDistance) // not yet processed |
|
2566 { |
|
2567 n->markAsVisible(); |
|
2568 maxNodes--; |
|
2569 // add direct children |
|
2570 if (n->m_children) |
|
2571 { |
|
2572 QListIterator<DotNode> li(*n->m_children); |
|
2573 DotNode *dn; |
|
2574 for (li.toFirst();(dn=li.current());++li) |
|
2575 { |
|
2576 queue.append(dn); |
|
2577 } |
|
2578 } |
|
2579 } |
|
2580 } |
|
2581 } |
|
2582 |
|
2583 void DotCallGraph::determineTruncatedNodes(QList<DotNode> &queue) |
|
2584 { |
|
2585 while (queue.count()>0) |
|
2586 { |
|
2587 DotNode *n = queue.take(0); |
|
2588 if (n->isVisible() && n->isTruncated()==DotNode::Unknown) |
|
2589 { |
|
2590 bool truncated = FALSE; |
|
2591 if (n->m_children) |
|
2592 { |
|
2593 QListIterator<DotNode> li(*n->m_children); |
|
2594 DotNode *dn; |
|
2595 for (li.toFirst();(dn=li.current());++li) |
|
2596 { |
|
2597 if (!dn->isVisible()) |
|
2598 truncated = TRUE; |
|
2599 else |
|
2600 queue.append(dn); |
|
2601 } |
|
2602 } |
|
2603 n->markAsTruncated(truncated); |
|
2604 } |
|
2605 } |
|
2606 } |
|
2607 |
|
2608 |
|
2609 |
|
2610 DotCallGraph::DotCallGraph(MemberDef *md,bool inverse) |
|
2611 { |
|
2612 m_maxDistance = 0; |
|
2613 m_inverse = inverse; |
|
2614 m_diskName = md->getOutputFileBase()+"_"+md->anchor(); |
|
2615 m_scope = md->getOuterScope(); |
|
2616 QCString uniqueId; |
|
2617 uniqueId = md->getReference()+"$"+ |
|
2618 md->getOutputFileBase()+"#"+md->anchor(); |
|
2619 QCString name; |
|
2620 if (Config_getBool("HIDE_SCOPE_NAMES")) |
|
2621 { |
|
2622 name = md->name(); |
|
2623 } |
|
2624 else |
|
2625 { |
|
2626 name = md->qualifiedName(); |
|
2627 } |
|
2628 m_startNode = new DotNode(m_curNodeNumber++, |
|
2629 linkToText(name,FALSE), |
|
2630 "", |
|
2631 uniqueId.data(), |
|
2632 TRUE // root node |
|
2633 ); |
|
2634 m_startNode->setDistance(0); |
|
2635 m_usedNodes = new QDict<DotNode>(1009); |
|
2636 m_usedNodes->insert(uniqueId,m_startNode); |
|
2637 buildGraph(m_startNode,md,1); |
|
2638 |
|
2639 static int nodes = Config_getInt("DOT_GRAPH_MAX_NODES"); |
|
2640 int maxNodes = nodes; |
|
2641 //int directChildNodes = 1; |
|
2642 //if (m_startNode->m_children!=0) |
|
2643 // directChildNodes+=m_startNode->m_children->count(); |
|
2644 //if (directChildNodes>maxNodes) maxNodes=directChildNodes; |
|
2645 QList<DotNode> openNodeQueue; |
|
2646 openNodeQueue.append(m_startNode); |
|
2647 determineVisibleNodes(openNodeQueue,maxNodes); |
|
2648 openNodeQueue.clear(); |
|
2649 openNodeQueue.append(m_startNode); |
|
2650 determineTruncatedNodes(openNodeQueue); |
|
2651 } |
|
2652 |
|
2653 DotCallGraph::~DotCallGraph() |
|
2654 { |
|
2655 deleteNodes(m_startNode); |
|
2656 delete m_usedNodes; |
|
2657 } |
|
2658 |
|
2659 QCString DotCallGraph::writeGraph(QTextStream &out, GraphOutputFormat format, |
|
2660 const char *path,const char *relPath,bool generateImageMap) const |
|
2661 { |
|
2662 QDir d(path); |
|
2663 // store the original directory |
|
2664 if (!d.exists()) |
|
2665 { |
|
2666 err("Error: Output dir %s does not exist!\n",path); exit(1); |
|
2667 } |
|
2668 setDotFontPath(d.absPath()); |
|
2669 |
|
2670 QCString baseName = m_diskName + (m_inverse ? "_icgraph" : "_cgraph"); |
|
2671 QCString mapName=baseName; |
|
2672 QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT"); |
|
2673 |
|
2674 QCString absBaseName = QCString(d.absPath())+"/"+baseName; |
|
2675 |
|
2676 if (updateDotGraph(m_startNode, |
|
2677 DotNode::CallGraph, |
|
2678 absBaseName, |
|
2679 format, |
|
2680 TRUE, // lrRank |
|
2681 FALSE, // renderParents |
|
2682 m_inverse // backArrows |
|
2683 ) |
|
2684 ) |
|
2685 { |
|
2686 if (format==BITMAP) |
|
2687 { |
|
2688 // run dot to create a bitmap image |
|
2689 QCString dotArgs(maxCmdLine); |
|
2690 QCString absImgName=absBaseName+"."+imgExt; |
|
2691 DotRunner dotRun(absBaseName+".dot"); |
|
2692 dotRun.addJob(imgExt,absImgName); |
|
2693 if (generateImageMap) dotRun.addJob(MAP_CMD,absBaseName+".map"); |
|
2694 if (!dotRun.run()) |
|
2695 { |
|
2696 unsetDotFontPath(); |
|
2697 return baseName; |
|
2698 } |
|
2699 checkDotResult(absImgName); |
|
2700 } |
|
2701 else if (format==EPS) |
|
2702 { |
|
2703 // run dot to create a .eps image |
|
2704 DotRunner dotRun(absBaseName+".dot"); |
|
2705 dotRun.addJob("ps",absBaseName+".eps"); |
|
2706 if (Config_getBool("USE_PDFLATEX")) |
|
2707 { |
|
2708 QCString epstopdfArgs(maxCmdLine); |
|
2709 epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"", |
|
2710 absBaseName.data(),absBaseName.data()); |
|
2711 dotRun.addPostProcessing("epstopdf",epstopdfArgs); |
|
2712 } |
|
2713 if (!dotRun.run()) |
|
2714 { |
|
2715 unsetDotFontPath(); |
|
2716 return baseName; |
|
2717 } |
|
2718 } |
|
2719 } |
|
2720 Doxygen::indexList.addImageFile(baseName+"."+imgExt); |
|
2721 |
|
2722 if (format==BITMAP && generateImageMap) |
|
2723 { |
|
2724 out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." |
|
2725 << imgExt << "\" border=\"0\" usemap=\"#" |
|
2726 << mapName << "_map\" alt=\""; |
|
2727 out << "\">"; |
|
2728 out << "</div>" << endl; |
|
2729 QString tmpstr; |
|
2730 QTextOStream tmpout(&tmpstr); |
|
2731 tmpout.setEncoding(tmpout.UnicodeUTF8); |
|
2732 convertMapFile(tmpout,absBaseName+".map",relPath); |
|
2733 if (!tmpstr.isEmpty()) |
|
2734 { |
|
2735 out << "<map name=\"" << mapName << "_map\" id=\"" << mapName << "\">" << endl; |
|
2736 out << tmpstr; |
|
2737 out << "</map>" << endl; |
|
2738 } |
|
2739 } |
|
2740 else if (format==EPS) |
|
2741 { |
|
2742 int width,height; |
|
2743 if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height)) |
|
2744 { |
|
2745 err("Error: Could not extract bounding box from .eps!\n"); |
|
2746 unsetDotFontPath(); |
|
2747 return baseName; |
|
2748 } |
|
2749 int maxWidth = 420; /* approx. page width in points */ |
|
2750 |
|
2751 out << "\\nopagebreak\n" |
|
2752 "\\begin{figure}[H]\n" |
|
2753 "\\begin{center}\n" |
|
2754 "\\leavevmode\n" |
|
2755 "\\includegraphics[width=" << QMIN(width/2,maxWidth) |
|
2756 << "pt]{" << baseName << "}\n" |
|
2757 "\\end{center}\n" |
|
2758 "\\end{figure}\n"; |
|
2759 } |
|
2760 |
|
2761 if (Config_getBool("DOT_CLEANUP")) d.remove(baseName+".dot"); |
|
2762 |
|
2763 unsetDotFontPath(); |
|
2764 return baseName; |
|
2765 } |
|
2766 |
|
2767 bool DotCallGraph::isTrivial() const |
|
2768 { |
|
2769 return m_startNode->m_children==0; |
|
2770 } |
|
2771 |
|
2772 bool DotCallGraph::isTooBig() const |
|
2773 { |
|
2774 static int maxNodes = Config_getInt("DOT_GRAPH_MAX_NODES"); |
|
2775 int numNodes = m_startNode->m_children ? m_startNode->m_children->count() : 0; |
|
2776 return numNodes>=maxNodes; |
|
2777 } |
|
2778 |
|
2779 //------------------------------------------------------------- |
|
2780 |
|
2781 DotDirDeps::DotDirDeps(DirDef *dir) : m_dir(dir) |
|
2782 { |
|
2783 } |
|
2784 |
|
2785 DotDirDeps::~DotDirDeps() |
|
2786 { |
|
2787 } |
|
2788 |
|
2789 QCString DotDirDeps::writeGraph(QTextStream &out, |
|
2790 GraphOutputFormat format, |
|
2791 const char *path, |
|
2792 const char *relPath, |
|
2793 bool generateImageMap) const |
|
2794 { |
|
2795 QDir d(path); |
|
2796 // store the original directory |
|
2797 if (!d.exists()) |
|
2798 { |
|
2799 err("Error: Output dir %s does not exist!\n",path); exit(1); |
|
2800 } |
|
2801 setDotFontPath(d.absPath()); |
|
2802 |
|
2803 QCString baseName=m_dir->getOutputFileBase()+"_dep"; |
|
2804 QCString mapName=escapeCharsInString(baseName,FALSE); |
|
2805 QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT"); |
|
2806 |
|
2807 QCString absBaseName = QCString(d.absPath())+"/"+baseName; |
|
2808 |
|
2809 // TODO: create check, update md5 checksum |
|
2810 { |
|
2811 QFile f(absBaseName+".dot"); |
|
2812 if (!f.open(IO_WriteOnly)) |
|
2813 { |
|
2814 err("Cannot create file %s.dot for writing!\n",baseName.data()); |
|
2815 } |
|
2816 QTextStream t(&f); |
|
2817 t.setEncoding(t.UnicodeUTF8); |
|
2818 m_dir->writeDepGraph(t); |
|
2819 f.close(); |
|
2820 |
|
2821 if (format==BITMAP) |
|
2822 { |
|
2823 // run dot to create a bitmap image |
|
2824 QCString dotArgs(maxCmdLine); |
|
2825 QCString absImgName=absBaseName+"."+imgExt; |
|
2826 DotRunner dotRun(absBaseName+".dot"); |
|
2827 dotRun.addJob(imgExt,absImgName); |
|
2828 if (generateImageMap) dotRun.addJob(MAP_CMD,absBaseName+".map"); |
|
2829 if (!dotRun.run()) |
|
2830 { |
|
2831 unsetDotFontPath(); |
|
2832 return baseName; |
|
2833 } |
|
2834 checkDotResult(absImgName); |
|
2835 } |
|
2836 else if (format==EPS) |
|
2837 { |
|
2838 // run dot to create a .eps image |
|
2839 DotRunner dotRun(absBaseName+".dot"); |
|
2840 dotRun.addJob("ps",absBaseName+".eps"); |
|
2841 if (Config_getBool("USE_PDFLATEX")) |
|
2842 { |
|
2843 QCString epstopdfArgs(maxCmdLine); |
|
2844 epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"", |
|
2845 absBaseName.data(),absBaseName.data()); |
|
2846 dotRun.addPostProcessing("epstopdf",epstopdfArgs); |
|
2847 } |
|
2848 if (!dotRun.run()) |
|
2849 { |
|
2850 unsetDotFontPath(); |
|
2851 return baseName; |
|
2852 } |
|
2853 } |
|
2854 } |
|
2855 Doxygen::indexList.addImageFile(baseName+"."+imgExt); |
|
2856 |
|
2857 if (format==BITMAP && generateImageMap) |
|
2858 { |
|
2859 out << "<div class=\"center\"><img src=\"" << relPath << baseName << "." |
|
2860 << imgExt << "\" border=\"0\" usemap=\"#" |
|
2861 << mapName << "_map\" alt=\""; |
|
2862 out << convertToXML(m_dir->displayName()); |
|
2863 out << "\"/>"; |
|
2864 out << "</div>" << endl; |
|
2865 QString tmpstr; |
|
2866 QTextOStream tmpout(&tmpstr); |
|
2867 tmpout.setEncoding(tmpout.UnicodeUTF8); |
|
2868 convertMapFile(tmpout,absBaseName+".map",relPath,TRUE); |
|
2869 if (!tmpstr.isEmpty()) |
|
2870 { |
|
2871 out << "<map name=\"" << mapName << "_map\" id=\"" << mapName << "\">" << endl; |
|
2872 out << tmpstr; |
|
2873 out << "</map>" << endl; |
|
2874 } |
|
2875 else |
|
2876 { |
|
2877 //printf("Map is empty!\n"); |
|
2878 } |
|
2879 //thisDir.remove(baseName+".map"); |
|
2880 } |
|
2881 else if (format==EPS) |
|
2882 { |
|
2883 int width,height; |
|
2884 if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height)) |
|
2885 { |
|
2886 err("Error: Could not extract bounding box from .eps!\n"); |
|
2887 unsetDotFontPath(); |
|
2888 return baseName; |
|
2889 } |
|
2890 int maxWidth = 420; /* approx. page width in points */ |
|
2891 |
|
2892 out << "\\nopagebreak\n" |
|
2893 "\\begin{figure}[H]\n" |
|
2894 "\\begin{center}\n" |
|
2895 "\\leavevmode\n" |
|
2896 "\\includegraphics[width=" << QMIN(width/2,maxWidth) |
|
2897 << "pt]{" << baseName << "}\n" |
|
2898 "\\end{center}\n" |
|
2899 "\\end{figure}\n"; |
|
2900 } |
|
2901 |
|
2902 if (Config_getBool("DOT_CLEANUP")) d.remove(baseName+".dot"); |
|
2903 |
|
2904 unsetDotFontPath(); |
|
2905 return baseName; |
|
2906 } |
|
2907 |
|
2908 bool DotDirDeps::isTrivial() const |
|
2909 { |
|
2910 return m_dir->depGraphIsTrivial(); |
|
2911 } |
|
2912 |
|
2913 //------------------------------------------------------------- |
|
2914 |
|
2915 void generateGraphLegend(const char *path) |
|
2916 { |
|
2917 QFile dotFile((QCString)path+"/graph_legend.dot"); |
|
2918 if (!dotFile.open(IO_WriteOnly)) |
|
2919 { |
|
2920 err("Could not open file %s for writing\n", |
|
2921 convertToQCString(dotFile.name()).data()); |
|
2922 return; |
|
2923 } |
|
2924 QTextStream dotText(&dotFile); |
|
2925 writeGraphHeader(dotText); |
|
2926 dotText << " Node9 [shape=\"box\",label=\"Inherited\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",fillcolor=\"grey75\",style=\"filled\" fontcolor=\"black\"];\n"; |
|
2927 dotText << " Node10 -> Node9 [dir=back,color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n"; |
|
2928 dotText << " Node10 [shape=\"box\",label=\"PublicBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPublicBase" << Doxygen::htmlFileExtension << "\"];\n"; |
|
2929 dotText << " Node11 -> Node10 [dir=back,color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n"; |
|
2930 dotText << " Node11 [shape=\"box\",label=\"Truncated\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"red\",URL=\"$classTruncated" << Doxygen::htmlFileExtension << "\"];\n"; |
|
2931 dotText << " Node13 -> Node9 [dir=back,color=\"darkgreen\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n"; |
|
2932 dotText << " Node13 [shape=\"box\",label=\"ProtectedBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classProtectedBase" << Doxygen::htmlFileExtension << "\"];\n"; |
|
2933 dotText << " Node14 -> Node9 [dir=back,color=\"firebrick4\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n"; |
|
2934 dotText << " Node14 [shape=\"box\",label=\"PrivateBase\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classPrivateBase" << Doxygen::htmlFileExtension << "\"];\n"; |
|
2935 dotText << " Node15 -> Node9 [dir=back,color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n"; |
|
2936 dotText << " Node15 [shape=\"box\",label=\"Undocumented\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"grey75\"];\n"; |
|
2937 dotText << " Node16 -> Node9 [dir=back,color=\"midnightblue\",fontsize=\"" << FONTSIZE << "\",style=\"solid\",fontname=\"" << FONTNAME << "\"];\n"; |
|
2938 dotText << " Node16 [shape=\"box\",label=\"Templ< int >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n"; |
|
2939 dotText << " Node17 -> Node16 [dir=back,color=\"orange\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"< int >\",fontname=\"" << FONTNAME << "\"];\n"; |
|
2940 dotText << " Node17 [shape=\"box\",label=\"Templ< T >\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classTempl" << Doxygen::htmlFileExtension << "\"];\n"; |
|
2941 dotText << " Node18 -> Node9 [dir=back,color=\"darkorchid3\",fontsize=\"" << FONTSIZE << "\",style=\"dashed\",label=\"m_usedClass\",fontname=\"" << FONTNAME << "\"];\n"; |
|
2942 dotText << " Node18 [shape=\"box\",label=\"Used\",fontsize=\"" << FONTSIZE << "\",height=0.2,width=0.4,fontname=\"" << FONTNAME << "\",color=\"black\",URL=\"$classUsed" << Doxygen::htmlFileExtension << "\"];\n"; |
|
2943 writeGraphFooter(dotText); |
|
2944 dotFile.close(); |
|
2945 |
|
2946 QDir d(path); |
|
2947 // store the original directory |
|
2948 if (!d.exists()) |
|
2949 { |
|
2950 err("Error: Output dir %s does not exist!\n",path); exit(1); |
|
2951 } |
|
2952 setDotFontPath(d.absPath()); |
|
2953 |
|
2954 // run dot to generate the a bitmap image from the graph |
|
2955 QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT"); |
|
2956 QCString imgName = "graph_legend."+imgExt; |
|
2957 QCString absImgName = QCString(d.absPath())+"/"+ imgName; |
|
2958 |
|
2959 DotRunner dotRun(d.absPath()+"/graph_legend.dot"); |
|
2960 dotRun.addJob(imgExt,absImgName); |
|
2961 if (!dotRun.run()) |
|
2962 { |
|
2963 unsetDotFontPath(); |
|
2964 return; |
|
2965 } |
|
2966 checkDotResult(absImgName); |
|
2967 Doxygen::indexList.addImageFile(imgName); |
|
2968 unsetDotFontPath(); |
|
2969 } |
|
2970 |
|
2971 void writeDotGraphFromFile(const char *inFile,const char *outDir, |
|
2972 const char *outFile,GraphOutputFormat format) |
|
2973 { |
|
2974 QDir d(outDir); |
|
2975 if (!d.exists()) |
|
2976 { |
|
2977 err("Error: Output dir %s does not exist!\n",outDir); exit(1); |
|
2978 } |
|
2979 setDotFontPath(0); |
|
2980 |
|
2981 QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT"); |
|
2982 QCString imgName = (QCString)outFile+"."+imgExt; |
|
2983 QCString absImgName = QCString(d.absPath())+"/"+imgName; |
|
2984 QCString absOutFile = QCString(d.absPath())+"/"+outFile; |
|
2985 |
|
2986 DotRunner dotRun(inFile); |
|
2987 if (format==BITMAP) |
|
2988 dotRun.addJob(imgExt,absImgName); |
|
2989 else // format==EPS |
|
2990 dotRun.addJob("ps",absOutFile+".eps"); |
|
2991 |
|
2992 if ( (format==EPS) && (Config_getBool("USE_PDFLATEX")) ) |
|
2993 { |
|
2994 QCString epstopdfArgs(maxCmdLine); |
|
2995 epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"", |
|
2996 absOutFile.data(),absOutFile.data()); |
|
2997 dotRun.addPostProcessing("epstopdf",epstopdfArgs); |
|
2998 } |
|
2999 |
|
3000 if (!dotRun.run()) |
|
3001 { |
|
3002 unsetDotFontPath(); |
|
3003 return; |
|
3004 } |
|
3005 |
|
3006 if (format==BITMAP) checkDotResult(absImgName); |
|
3007 Doxygen::indexList.addImageFile(imgName); |
|
3008 |
|
3009 unsetDotFontPath(); |
|
3010 } |
|
3011 |
|
3012 |
|
3013 /*! Marco Dalla Gasperina [marcodg@attbi.com] added this to allow |
|
3014 * dotfiles to generate image maps. |
|
3015 * \param inFile just the basename part of the filename |
|
3016 * \param outDir output directory |
|
3017 * \param relPath relative path the to root of the output dir |
|
3018 * \param context the scope in which this graph is found (for resolving links) |
|
3019 * \returns a string which is the HTML image map (without the \<map\>\</map\>) |
|
3020 */ |
|
3021 QString getDotImageMapFromFile(const QString& inFile, const QString& outDir, |
|
3022 const QCString &relPath,const QString &context) |
|
3023 { |
|
3024 QString outFile = inFile + ".map"; |
|
3025 |
|
3026 QDir d(outDir); |
|
3027 if (!d.exists()) |
|
3028 { |
|
3029 err("Error: Output dir %s does not exist!\n",outDir.data()); exit(1); |
|
3030 } |
|
3031 setDotFontPath(d.absPath()); |
|
3032 |
|
3033 QCString absOutFile = QCString(d.absPath())+"/"+outFile.data(); |
|
3034 |
|
3035 DotRunner dotRun(inFile); |
|
3036 dotRun.addJob(MAP_CMD,absOutFile); |
|
3037 if (!dotRun.run()) |
|
3038 { |
|
3039 unsetDotFontPath(); |
|
3040 return ""; |
|
3041 } |
|
3042 |
|
3043 QString result; |
|
3044 QTextOStream tmpout(&result); |
|
3045 tmpout.setEncoding(tmpout.UnicodeUTF8); |
|
3046 convertMapFile(tmpout, absOutFile, relPath ,TRUE, context); |
|
3047 d.remove(outFile); |
|
3048 |
|
3049 unsetDotFontPath(); |
|
3050 return result; |
|
3051 } |
|
3052 // end MDG mods |
|
3053 |
|
3054 //------------------------------------------------------------- |
|
3055 |
|
3056 DotGroupCollaboration::DotGroupCollaboration(GroupDef* gd) |
|
3057 { |
|
3058 m_curNodeId = 0; |
|
3059 QCString tmp_url = gd->getReference()+"$"+gd->getOutputFileBase(); |
|
3060 m_usedNodes = new QDict<DotNode>(1009); |
|
3061 m_rootNode = new DotNode(m_curNodeId++, gd->groupTitle(), "", tmp_url, TRUE ); |
|
3062 m_rootNode->markAsVisible(); |
|
3063 m_usedNodes->insert(gd->name(), m_rootNode ); |
|
3064 m_edges.setAutoDelete(TRUE); |
|
3065 |
|
3066 m_diskName = gd->getOutputFileBase(); |
|
3067 |
|
3068 buildGraph( gd ); |
|
3069 } |
|
3070 |
|
3071 DotGroupCollaboration::~DotGroupCollaboration() |
|
3072 { |
|
3073 delete m_usedNodes; |
|
3074 } |
|
3075 |
|
3076 void DotGroupCollaboration::buildGraph(GroupDef* gd) |
|
3077 { |
|
3078 QCString tmp_url; |
|
3079 //=========================== |
|
3080 // hierarchy. |
|
3081 |
|
3082 // Write parents |
|
3083 LockingPtr<GroupList> groups = gd->partOfGroups(); |
|
3084 if ( groups!=0 ) |
|
3085 { |
|
3086 GroupListIterator gli(*groups); |
|
3087 GroupDef *d; |
|
3088 for (gli.toFirst();(d=gli.current());++gli) |
|
3089 { |
|
3090 DotNode* nnode = m_usedNodes->find(d->name()); |
|
3091 if ( !nnode ) |
|
3092 { // add node |
|
3093 tmp_url = d->getReference()+"$"+d->getOutputFileBase(); |
|
3094 QCString tooltip = d->briefDescriptionAsTooltip(); |
|
3095 nnode = new DotNode(m_curNodeId++, d->groupTitle(), tooltip, tmp_url ); |
|
3096 nnode->markAsVisible(); |
|
3097 m_usedNodes->insert(d->name(), nnode ); |
|
3098 } |
|
3099 tmp_url = ""; |
|
3100 addEdge( nnode, m_rootNode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url ); |
|
3101 } |
|
3102 } |
|
3103 |
|
3104 // Add subgroups |
|
3105 if ( gd->getSubGroups() && gd->getSubGroups()->count() ) |
|
3106 { |
|
3107 QListIterator<GroupDef> defli(*gd->getSubGroups()); |
|
3108 GroupDef *def; |
|
3109 for (;(def=defli.current());++defli) |
|
3110 { |
|
3111 DotNode* nnode = m_usedNodes->find(def->name()); |
|
3112 if ( !nnode ) |
|
3113 { // add node |
|
3114 tmp_url = def->getReference()+"$"+def->getOutputFileBase(); |
|
3115 QCString tooltip = def->briefDescriptionAsTooltip(); |
|
3116 nnode = new DotNode(m_curNodeId++, def->groupTitle(), tooltip, tmp_url ); |
|
3117 nnode->markAsVisible(); |
|
3118 m_usedNodes->insert(def->name(), nnode ); |
|
3119 } |
|
3120 tmp_url = ""; |
|
3121 addEdge( m_rootNode, nnode, DotGroupCollaboration::thierarchy, tmp_url, tmp_url ); |
|
3122 } |
|
3123 } |
|
3124 |
|
3125 //======================= |
|
3126 // Write collaboration |
|
3127 |
|
3128 // Add members |
|
3129 addMemberList( gd->getMemberList(MemberList::allMembersList) ); |
|
3130 |
|
3131 // Add classes |
|
3132 if ( gd->getClasses() && gd->getClasses()->count() ) |
|
3133 { |
|
3134 ClassSDict::Iterator defli(*gd->getClasses()); |
|
3135 ClassDef *def; |
|
3136 for (;(def=defli.current());++defli) |
|
3137 { |
|
3138 tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension; |
|
3139 addCollaborationMember( def, tmp_url, DotGroupCollaboration::tclass ); |
|
3140 } |
|
3141 } |
|
3142 |
|
3143 // Add namespaces |
|
3144 if ( gd->getNamespaces() && gd->getNamespaces()->count() ) |
|
3145 { |
|
3146 NamespaceSDict::Iterator defli(*gd->getNamespaces()); |
|
3147 NamespaceDef *def; |
|
3148 for (;(def=defli.current());++defli) |
|
3149 { |
|
3150 tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension; |
|
3151 addCollaborationMember( def, tmp_url, DotGroupCollaboration::tnamespace ); |
|
3152 } |
|
3153 } |
|
3154 |
|
3155 // Add files |
|
3156 if ( gd->getFiles() && gd->getFiles()->count() ) |
|
3157 { |
|
3158 QListIterator<FileDef> defli(*gd->getFiles()); |
|
3159 FileDef *def; |
|
3160 for (;(def=defli.current());++defli) |
|
3161 { |
|
3162 tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension; |
|
3163 addCollaborationMember( def, tmp_url, DotGroupCollaboration::tfile ); |
|
3164 } |
|
3165 } |
|
3166 |
|
3167 // Add pages |
|
3168 if ( gd->getPages() && gd->getPages()->count() ) |
|
3169 { |
|
3170 PageSDict::Iterator defli(*gd->getPages()); |
|
3171 PageDef *def; |
|
3172 for (;(def=defli.current());++defli) |
|
3173 { |
|
3174 tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension; |
|
3175 addCollaborationMember( def, tmp_url, DotGroupCollaboration::tpages ); |
|
3176 } |
|
3177 } |
|
3178 |
|
3179 // Add directories |
|
3180 if ( gd->getDirs() && gd->getDirs()->count() ) |
|
3181 { |
|
3182 QListIterator<DirDef> defli(*gd->getDirs()); |
|
3183 DirDef *def; |
|
3184 for (;(def=defli.current());++defli) |
|
3185 { |
|
3186 tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension; |
|
3187 addCollaborationMember( def, tmp_url, DotGroupCollaboration::tdir ); |
|
3188 } |
|
3189 } |
|
3190 } |
|
3191 |
|
3192 void DotGroupCollaboration::addMemberList( MemberList* ml ) |
|
3193 { |
|
3194 if ( !( ml && ml->count()) ) return; |
|
3195 MemberListIterator defli(*ml); |
|
3196 MemberDef *def; |
|
3197 for (;(def=defli.current());++defli) |
|
3198 { |
|
3199 QCString tmp_url = def->getReference()+"$"+def->getOutputFileBase()+Doxygen::htmlFileExtension |
|
3200 +"#"+def->anchor(); |
|
3201 addCollaborationMember( def, tmp_url, DotGroupCollaboration::tmember ); |
|
3202 } |
|
3203 } |
|
3204 |
|
3205 DotGroupCollaboration::Edge* DotGroupCollaboration::addEdge( |
|
3206 DotNode* _pNStart, DotNode* _pNEnd, EdgeType _eType, |
|
3207 const QCString& _label, const QCString& _url ) |
|
3208 { |
|
3209 // search a existing link. |
|
3210 QListIterator<Edge> lli(m_edges); |
|
3211 Edge* newEdge = 0; |
|
3212 for ( lli.toFirst(); (newEdge=lli.current()); ++lli) |
|
3213 { |
|
3214 if ( newEdge->pNStart==_pNStart && |
|
3215 newEdge->pNEnd==_pNEnd && |
|
3216 newEdge->eType==_eType |
|
3217 ) |
|
3218 { // edge already found |
|
3219 break; |
|
3220 } |
|
3221 } |
|
3222 if ( newEdge==0 ) // new link |
|
3223 { |
|
3224 newEdge = new Edge(_pNStart,_pNEnd,_eType); |
|
3225 m_edges.append( newEdge ); |
|
3226 } |
|
3227 |
|
3228 if (!_label.isEmpty()) |
|
3229 { |
|
3230 newEdge->links.append(new Link(_label,_url)); |
|
3231 } |
|
3232 |
|
3233 return newEdge; |
|
3234 } |
|
3235 |
|
3236 void DotGroupCollaboration::addCollaborationMember( |
|
3237 Definition* def, QCString& url, EdgeType eType ) |
|
3238 { |
|
3239 // Create group nodes |
|
3240 if ( !def->partOfGroups() ) |
|
3241 return; |
|
3242 GroupListIterator gli(*def->partOfGroups()); |
|
3243 GroupDef *d; |
|
3244 QCString tmp_str; |
|
3245 for (;(d=gli.current());++gli) |
|
3246 { |
|
3247 DotNode* nnode = m_usedNodes->find(d->name()); |
|
3248 if ( nnode != m_rootNode ) |
|
3249 { |
|
3250 if ( nnode==0 ) |
|
3251 { // add node |
|
3252 tmp_str = d->getReference()+"$"+d->getOutputFileBase(); |
|
3253 QCString tooltip = d->briefDescriptionAsTooltip(); |
|
3254 nnode = new DotNode(m_curNodeId++, d->groupTitle(), tooltip, tmp_str ); |
|
3255 nnode->markAsVisible(); |
|
3256 m_usedNodes->insert(d->name(), nnode ); |
|
3257 } |
|
3258 tmp_str = def->qualifiedName(); |
|
3259 addEdge( m_rootNode, nnode, eType, tmp_str, url ); |
|
3260 } |
|
3261 } |
|
3262 } |
|
3263 |
|
3264 |
|
3265 QCString DotGroupCollaboration::writeGraph( QTextStream &t, GraphOutputFormat format, |
|
3266 const char *path, const char *relPath, |
|
3267 bool writeImageMap) const |
|
3268 { |
|
3269 QDir d(path); |
|
3270 // store the original directory |
|
3271 if (!d.exists()) |
|
3272 { |
|
3273 err("Error: Output dir %s does not exist!\n",path); exit(1); |
|
3274 } |
|
3275 setDotFontPath(d.absPath()); |
|
3276 |
|
3277 QCString baseName = m_diskName; |
|
3278 QCString absBaseName = QCString(d.absPath())+"/"+baseName; |
|
3279 |
|
3280 QFile dotfile(absBaseName+".dot"); |
|
3281 if (dotfile.open(IO_WriteOnly)) |
|
3282 { |
|
3283 QTextStream tdot(&dotfile); |
|
3284 tdot.setEncoding(tdot.UnicodeUTF8); |
|
3285 writeGraphHeader(tdot); |
|
3286 |
|
3287 // clean write flags |
|
3288 QDictIterator<DotNode> dni(*m_usedNodes); |
|
3289 DotNode *pn; |
|
3290 for (dni.toFirst();(pn=dni.current());++dni) |
|
3291 pn->clearWriteFlag(); |
|
3292 |
|
3293 // write other nodes. |
|
3294 for (dni.toFirst();(pn=dni.current());++dni) |
|
3295 { |
|
3296 pn->write(tdot,DotNode::Inheritance,format,TRUE,FALSE,FALSE,FALSE); |
|
3297 } |
|
3298 |
|
3299 // write edges |
|
3300 QListIterator<Edge> eli(m_edges); |
|
3301 Edge* edge; |
|
3302 for (eli.toFirst();(edge=eli.current());++eli) |
|
3303 { |
|
3304 edge->write( tdot ); |
|
3305 } |
|
3306 |
|
3307 writeGraphFooter(tdot); |
|
3308 dotfile.close(); |
|
3309 } |
|
3310 |
|
3311 QCString imgExt = Config_getEnum("DOT_IMAGE_FORMAT"); |
|
3312 if (format==BITMAP) // run dot to create a bitmap image |
|
3313 { |
|
3314 QCString dotArgs(maxCmdLine); |
|
3315 QCString imgName = baseName+"."+imgExt; |
|
3316 QCString mapName=baseName+".map"; |
|
3317 |
|
3318 QCString absImgName = QCString(d.absPath())+"/"+imgName; |
|
3319 QCString absMapName = QCString(d.absPath())+"/"+mapName; |
|
3320 |
|
3321 DotRunner dotRun(absBaseName+".dot"); |
|
3322 dotRun.addJob(imgExt,absImgName); |
|
3323 if (writeImageMap) dotRun.addJob(MAP_CMD,absMapName); |
|
3324 if (!dotRun.run()) |
|
3325 { |
|
3326 unsetDotFontPath(); |
|
3327 return baseName; |
|
3328 } |
|
3329 |
|
3330 if (writeImageMap) |
|
3331 { |
|
3332 QCString mapLabel = escapeCharsInString(baseName,FALSE); |
|
3333 t << "<center><table><tr><td><img src=\"" << relPath << imgName |
|
3334 << "\" border=\"0\" alt=\"\" usemap=\"#" |
|
3335 << mapLabel << "_map\"/>" << endl; |
|
3336 t << "<map name=\"" << mapLabel << "_map\" id=\"" << mapLabel << "\">" << endl; |
|
3337 convertMapFile(t,absMapName,relPath); |
|
3338 t << "</map></td></tr></table></center>" << endl; |
|
3339 } |
|
3340 } |
|
3341 else if (format==EPS) |
|
3342 { |
|
3343 DotRunner dotRun(absBaseName+".dot"); |
|
3344 dotRun.addJob("ps",absBaseName+".eps"); |
|
3345 if (Config_getBool("USE_PDFLATEX")) |
|
3346 { |
|
3347 QCString epstopdfArgs(maxCmdLine); |
|
3348 epstopdfArgs.sprintf("\"%s.eps\" --outfile=\"%s.pdf\"", |
|
3349 absBaseName.data(),absBaseName.data()); |
|
3350 dotRun.addPostProcessing("epstopdf",epstopdfArgs); |
|
3351 } |
|
3352 |
|
3353 if (!dotRun.run()) |
|
3354 { |
|
3355 unsetDotFontPath(); |
|
3356 return baseName; |
|
3357 } |
|
3358 |
|
3359 int width,height; |
|
3360 if (!readBoundingBoxEPS(absBaseName+".eps",&width,&height)) |
|
3361 { |
|
3362 err("Error: Could not extract bounding box from .eps!\n"); |
|
3363 unsetDotFontPath(); |
|
3364 return baseName; |
|
3365 } |
|
3366 int maxWidth = 420; /* approx. page width in points */ |
|
3367 t << "\\nopagebreak\n" |
|
3368 "\\begin{figure}[H]\n" |
|
3369 "\\begin{center}\n" |
|
3370 "\\leavevmode\n" |
|
3371 "\\includegraphics[width=" << QMIN(width/2,maxWidth) |
|
3372 << "pt]{" << baseName << "}\n" |
|
3373 "\\end{center}\n" |
|
3374 "\\end{figure}\n"; |
|
3375 } |
|
3376 if (Config_getBool("DOT_CLEANUP")) |
|
3377 { |
|
3378 d.remove(baseName+".dot"); |
|
3379 } |
|
3380 |
|
3381 unsetDotFontPath(); |
|
3382 return baseName; |
|
3383 } |
|
3384 |
|
3385 void DotGroupCollaboration::Edge::write( QTextStream &t ) const |
|
3386 { |
|
3387 const char* linkTypeColor[] = { |
|
3388 "darkorchid3" |
|
3389 ,"orange" |
|
3390 ,"blueviolet" |
|
3391 ,"darkgreen" |
|
3392 ,"firebrick4" |
|
3393 ,"grey75" |
|
3394 ,"midnightblue" |
|
3395 }; |
|
3396 QCString arrowStyle = "dir=\"none\", style=\"dashed\""; |
|
3397 t << " Node" << pNStart->number(); |
|
3398 t << "->"; |
|
3399 t << "Node" << pNEnd->number(); |
|
3400 |
|
3401 t << " [shape=plaintext"; |
|
3402 if (links.count()>0) // there are links |
|
3403 { |
|
3404 t << ", "; |
|
3405 // HTML-like edge labels crash on my Mac with Graphviz 2.0! and |
|
3406 // are not supported by older version of dot. |
|
3407 // |
|
3408 //t << label=<<TABLE BORDER=\"0\" CELLBORDER=\"0\">"; |
|
3409 //QListIterator<Link> lli(links); |
|
3410 //Link *link; |
|
3411 //for( lli.toFirst(); (link=lli.current()); ++lli) |
|
3412 //{ |
|
3413 // t << "<TR><TD"; |
|
3414 // if ( !link->url.isEmpty() ) |
|
3415 // t << " HREF=\"" << link->url << "\""; |
|
3416 // t << ">" << link->label << "</TD></TR>"; |
|
3417 //} |
|
3418 //t << "</TABLE>>"; |
|
3419 |
|
3420 t << "label=\""; |
|
3421 QListIterator<Link> lli(links); |
|
3422 Link *link; |
|
3423 bool first=TRUE; |
|
3424 int count=0; |
|
3425 const int maxLabels = 10; |
|
3426 for( lli.toFirst(); (link=lli.current()) && count<maxLabels; ++lli,++count) |
|
3427 { |
|
3428 if (first) first=FALSE; else t << "\\n"; |
|
3429 t << convertLabel(link->label); |
|
3430 } |
|
3431 if (count==maxLabels) t << "\\n..."; |
|
3432 t << "\""; |
|
3433 |
|
3434 } |
|
3435 switch( eType ) |
|
3436 { |
|
3437 case thierarchy : |
|
3438 arrowStyle = "dir=\"back\", style=\"solid\""; |
|
3439 default : |
|
3440 t << ", color=\"" << linkTypeColor[(int)eType] << "\""; |
|
3441 break; |
|
3442 } |
|
3443 t << ", " << arrowStyle; |
|
3444 t << "];" << endl; |
|
3445 } |
|
3446 |
|
3447 bool DotGroupCollaboration::isTrivial() const |
|
3448 { |
|
3449 return m_usedNodes->count() <= 1; |
|
3450 } |
|
3451 |
|
3452 void DotGroupCollaboration::writeGraphHeader(QTextStream &t) const |
|
3453 { |
|
3454 t << "digraph structs" << endl; |
|
3455 t << "{" << endl; |
|
3456 if (Config_getBool("DOT_TRANSPARENT")) |
|
3457 { |
|
3458 t << " bgcolor=\"transparent\";" << endl; |
|
3459 } |
|
3460 t << " edge [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\"," |
|
3461 "labelfontname=\"" << FONTNAME << "\",labelfontsize=\"" << FONTSIZE << "\"];\n"; |
|
3462 t << " node [fontname=\"" << FONTNAME << "\",fontsize=\"" << FONTSIZE << "\",shape=record];\n"; |
|
3463 t << " rankdir=LR;\n"; |
|
3464 } |
|
3465 |
|
3466 void writeDotDirDepGraph(QTextStream &t,DirDef *dd) |
|
3467 { |
|
3468 t << "digraph G {\n"; |
|
3469 if (Config_getBool("DOT_TRANSPARENT")) |
|
3470 { |
|
3471 t << " bgcolor=transparent;\n"; |
|
3472 } |
|
3473 t << " compound=true\n"; |
|
3474 t << " node [ fontsize=\"" << FONTSIZE << "\", fontname=\"" << FONTNAME << "\"];\n"; |
|
3475 t << " edge [ labelfontsize=\"" << FONTSIZE << "\", labelfontname=\"" << FONTNAME << "\"];\n"; |
|
3476 |
|
3477 QDict<DirDef> dirsInGraph(257); |
|
3478 |
|
3479 dirsInGraph.insert(dd->getOutputFileBase(),dd); |
|
3480 if (dd->parent()) |
|
3481 { |
|
3482 t << " subgraph cluster" << dd->parent()->getOutputFileBase() << " {\n"; |
|
3483 t << " graph [ bgcolor=\"#ddddee\", pencolor=\"black\", label=\"" |
|
3484 << dd->parent()->shortName() |
|
3485 << "\" fontname=\"" << FONTNAME << "\", fontsize=\"" << FONTSIZE << "\", URL=\""; |
|
3486 t << dd->parent()->getOutputFileBase() << Doxygen::htmlFileExtension; |
|
3487 t << "\"]\n"; |
|
3488 } |
|
3489 if (dd->isCluster()) |
|
3490 { |
|
3491 t << " subgraph cluster" << dd->getOutputFileBase() << " {\n"; |
|
3492 t << " graph [ bgcolor=\"#eeeeff\", pencolor=\"black\", label=\"\"" |
|
3493 << " URL=\"" << dd->getOutputFileBase() << Doxygen::htmlFileExtension |
|
3494 << "\"];\n"; |
|
3495 t << " " << dd->getOutputFileBase() << " [shape=plaintext label=\"" |
|
3496 << dd->shortName() << "\"];\n"; |
|
3497 |
|
3498 // add nodes for sub directories |
|
3499 QListIterator<DirDef> sdi(dd->subDirs()); |
|
3500 DirDef *sdir; |
|
3501 for (sdi.toFirst();(sdir=sdi.current());++sdi) |
|
3502 { |
|
3503 t << " " << sdir->getOutputFileBase() << " [shape=box label=\"" |
|
3504 << sdir->shortName() << "\""; |
|
3505 if (sdir->isCluster()) |
|
3506 { |
|
3507 t << " color=\"red\""; |
|
3508 } |
|
3509 else |
|
3510 { |
|
3511 t << " color=\"black\""; |
|
3512 } |
|
3513 t << " fillcolor=\"white\" style=\"filled\""; |
|
3514 t << " URL=\"" << sdir->getOutputFileBase() |
|
3515 << Doxygen::htmlFileExtension << "\""; |
|
3516 t << "];\n"; |
|
3517 dirsInGraph.insert(sdir->getOutputFileBase(),sdir); |
|
3518 } |
|
3519 t << " }\n"; |
|
3520 } |
|
3521 else |
|
3522 { |
|
3523 t << " " << dd->getOutputFileBase() << " [shape=box, label=\"" |
|
3524 << dd->shortName() << "\", style=\"filled\", fillcolor=\"#eeeeff\"," |
|
3525 << " pencolor=\"black\", URL=\"" << dd->getOutputFileBase() |
|
3526 << Doxygen::htmlFileExtension << "\"];\n"; |
|
3527 } |
|
3528 if (dd->parent()) |
|
3529 { |
|
3530 t << " }\n"; |
|
3531 } |
|
3532 |
|
3533 // add nodes for other used directories |
|
3534 QDictIterator<UsedDir> udi(*dd->usedDirs()); |
|
3535 UsedDir *udir; |
|
3536 //printf("*** For dir %s\n",shortName().data()); |
|
3537 for (udi.toFirst();(udir=udi.current());++udi) |
|
3538 // for each used dir (=directly used or a parent of a directly used dir) |
|
3539 { |
|
3540 const DirDef *usedDir=udir->dir(); |
|
3541 DirDef *dir=dd; |
|
3542 while (dir) |
|
3543 { |
|
3544 //printf("*** check relation %s->%s same_parent=%d !%s->isParentOf(%s)=%d\n", |
|
3545 // dir->shortName().data(),usedDir->shortName().data(), |
|
3546 // dir->parent()==usedDir->parent(), |
|
3547 // usedDir->shortName().data(), |
|
3548 // shortName().data(), |
|
3549 // !usedDir->isParentOf(this) |
|
3550 // ); |
|
3551 if (dir!=usedDir && dir->parent()==usedDir->parent() && |
|
3552 !usedDir->isParentOf(dd)) |
|
3553 // include if both have the same parent (or no parent) |
|
3554 { |
|
3555 t << " " << usedDir->getOutputFileBase() << " [shape=box label=\"" |
|
3556 << usedDir->shortName() << "\""; |
|
3557 if (usedDir->isCluster()) |
|
3558 { |
|
3559 if (!Config_getBool("DOT_TRANSPARENT")) |
|
3560 { |
|
3561 t << " fillcolor=\"white\" style=\"filled\""; |
|
3562 } |
|
3563 t << " color=\"red\""; |
|
3564 } |
|
3565 t << " URL=\"" << usedDir->getOutputFileBase() |
|
3566 << Doxygen::htmlFileExtension << "\"];\n"; |
|
3567 dirsInGraph.insert(usedDir->getOutputFileBase(),usedDir); |
|
3568 break; |
|
3569 } |
|
3570 dir=dir->parent(); |
|
3571 } |
|
3572 } |
|
3573 |
|
3574 // add relations between all selected directories |
|
3575 DirDef *dir; |
|
3576 QDictIterator<DirDef> di(dirsInGraph); |
|
3577 for (di.toFirst();(dir=di.current());++di) // foreach dir in the graph |
|
3578 { |
|
3579 QDictIterator<UsedDir> udi(*dir->usedDirs()); |
|
3580 UsedDir *udir; |
|
3581 for (udi.toFirst();(udir=udi.current());++udi) // foreach used dir |
|
3582 { |
|
3583 const DirDef *usedDir=udir->dir(); |
|
3584 if ((dir!=dd || !udir->inherited()) && // only show direct dependendies for this dir |
|
3585 (usedDir!=dd || !udir->inherited()) && // only show direct dependendies for this dir |
|
3586 !usedDir->isParentOf(dir) && // don't point to own parent |
|
3587 dirsInGraph.find(usedDir->getOutputFileBase())) // only point to nodes that are in the graph |
|
3588 { |
|
3589 QCString relationName; |
|
3590 relationName.sprintf("dir_%06d_%06d",dir->dirCount(),usedDir->dirCount()); |
|
3591 if (Doxygen::dirRelations.find(relationName)==0) |
|
3592 { |
|
3593 // new relation |
|
3594 Doxygen::dirRelations.append(relationName, |
|
3595 new DirRelation(relationName,dir,udir)); |
|
3596 } |
|
3597 int nrefs = udir->filePairs().count(); |
|
3598 t << " " << dir->getOutputFileBase() << "->" |
|
3599 << usedDir->getOutputFileBase(); |
|
3600 t << " [headlabel=\"" << nrefs << "\", labeldistance=1.5"; |
|
3601 t << " headhref=\"" << relationName << Doxygen::htmlFileExtension |
|
3602 << "\"];\n"; |
|
3603 } |
|
3604 } |
|
3605 } |
|
3606 |
|
3607 t << "}\n"; |
|
3608 } |