PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/debugger/src/graph/GraphWidget.cpp

#
C++ | 380 lines | 219 code | 62 blank | 99 comment | 45 complexity | ff4a4d252e9896b0227e4cdbf27eaecb MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. Copyright (C) 2009 - 2011 Evan Teran
  3. eteran@alum.rit.edu
  4. Copyright (C) 2009 Arvin Schnell
  5. aschnell@suse.de
  6. This program is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <cmath>
  18. #include <climits>
  19. #include <QObject>
  20. #include <QDebug>
  21. #include <QKeyEvent>
  22. #include <QWheelEvent>
  23. #include <QGraphicsSceneMouseEvent>
  24. #include "GraphWidget.h"
  25. #include "GraphNode.h"
  26. #include "GraphEdge.h"
  27. #include <graphviz/gvc.h>
  28. //------------------------------------------------------------------------------
  29. // Name: GraphWidget(const QString& filename, const QString& layout, QWidget* parent)
  30. // Desc:
  31. //------------------------------------------------------------------------------
  32. GraphWidget::GraphWidget(const QString& filename, const QString& layout, QWidget* parent) : GraphWidgetBase(parent) {
  33. render_graph(filename, layout);
  34. }
  35. //------------------------------------------------------------------------------
  36. // Name: GraphWidget(GVC_t *gvc, graph_t *graph, const QString& layout, QWidget* parent)
  37. // Desc:
  38. //------------------------------------------------------------------------------
  39. GraphWidget::GraphWidget(GVC_t *gvc, graph_t *graph, const QString& layout, QWidget* parent) : GraphWidgetBase(parent) {
  40. render_graph(gvc, graph, layout);
  41. }
  42. //------------------------------------------------------------------------------
  43. // Name: ~GraphWidget()
  44. // Desc:
  45. //------------------------------------------------------------------------------
  46. GraphWidget::~GraphWidget() {
  47. }
  48. //------------------------------------------------------------------------------
  49. // Name: keyPressEvent(QKeyEvent* event)
  50. // Desc:
  51. //------------------------------------------------------------------------------
  52. void GraphWidget::keyPressEvent(QKeyEvent* event) {
  53. switch(event->key()) {
  54. case Qt::Key_Plus:
  55. scale_view(1.2);
  56. break;
  57. case Qt::Key_Minus:
  58. scale_view(1.0 / 1.2);
  59. break;
  60. case Qt::Key_Asterisk:
  61. rotate(10.0);
  62. break;
  63. case Qt::Key_Slash:
  64. rotate(-10.0);
  65. break;
  66. default:
  67. QGraphicsView::keyPressEvent(event);
  68. }
  69. }
  70. //------------------------------------------------------------------------------
  71. // Name: wheelEvent(QWheelEvent* event)
  72. // Desc:
  73. //------------------------------------------------------------------------------
  74. void GraphWidget::wheelEvent(QWheelEvent* event) {
  75. scale_view(std::pow(2.0, +event->delta() / 240.0));
  76. }
  77. //------------------------------------------------------------------------------
  78. // Name: scale_view(qreal scaleFactor)
  79. // Desc:
  80. //------------------------------------------------------------------------------
  81. void GraphWidget::scale_view(qreal scaleFactor) {
  82. const qreal f = ::sqrt(matrix().det());
  83. scaleFactor = qBound(0.1 / f, scaleFactor, 8.0 / f);
  84. scale(scaleFactor, scaleFactor);
  85. }
  86. //------------------------------------------------------------------------------
  87. // Name: contextMenuEvent(QContextMenuEvent* event)
  88. // Desc:
  89. //------------------------------------------------------------------------------
  90. void GraphWidget::contextMenuEvent(QContextMenuEvent* event) {
  91. GraphNode *node = qgraphicsitem_cast<GraphNode*>(itemAt(event->pos()));
  92. if(node) {
  93. emit nodeContextMenuEvent(event, node->name);
  94. } else {
  95. emit backgroundContextMenuEvent(event);
  96. }
  97. }
  98. //------------------------------------------------------------------------------
  99. // Name: mouseDoubleClickEvent(QMouseEvent* event)
  100. // Desc:
  101. //------------------------------------------------------------------------------
  102. void GraphWidget::mouseDoubleClickEvent(QMouseEvent* event) {
  103. GraphNode *node = qgraphicsitem_cast<GraphNode*>(itemAt(event->pos()));
  104. if(node) {
  105. emit nodeDoubleClickEvent(event, node->name);
  106. }
  107. }
  108. //------------------------------------------------------------------------------
  109. // Name: gToQ(const pointf& p, bool upside_down) const
  110. // Desc:
  111. //------------------------------------------------------------------------------
  112. QPointF GraphWidget::gToQ(const pointf& p, bool upside_down) const {
  113. return upside_down ? QPointF(p.x, graph_rect_.height() - p.y) : QPointF(p.x, -p.y);
  114. }
  115. //------------------------------------------------------------------------------
  116. // Name: gToQ(const point& p, bool upside_down) const
  117. // Desc:
  118. //------------------------------------------------------------------------------
  119. QPointF GraphWidget::gToQ(const point& p, bool upside_down) const {
  120. return upside_down ? QPointF(p.x, graph_rect_.height() - p.y) : QPointF(p.x, -p.y);
  121. }
  122. //------------------------------------------------------------------------------
  123. // Name: aggetToQColor(void *obj, const char *name, const QColor& fallback) const
  124. // Desc:
  125. //------------------------------------------------------------------------------
  126. QColor GraphWidget::aggetToQColor(void *obj, const char *name, const QColor& fallback) const {
  127. const char *tmp = agget(obj, const_cast<char*>(name));
  128. if(tmp == 0 || strlen(tmp) == 0)
  129. return fallback;
  130. return QColor(tmp);
  131. }
  132. //------------------------------------------------------------------------------
  133. // Name: aggetToQPenStyle(void *obj, const char *name, const Qt::PenStyle fallback) const
  134. // Desc:
  135. //------------------------------------------------------------------------------
  136. Qt::PenStyle GraphWidget::aggetToQPenStyle(void *obj, const char *name, const Qt::PenStyle fallback) const {
  137. const char *tmp = agget(obj, const_cast<char*>(name));
  138. if(tmp == 0 || strlen(tmp) == 0)
  139. return fallback;
  140. if(strcmp(tmp, "dashed") == 0)
  141. return Qt::DashLine;
  142. if(strcmp(tmp, "dotted") == 0)
  143. return Qt::DotLine;
  144. if(strcmp(tmp, "solid") == 0)
  145. return Qt::SolidLine;
  146. return fallback;
  147. }
  148. //------------------------------------------------------------------------------
  149. // Name: render_graph(const QString& filename, const QString& layout)
  150. // Desc:
  151. //------------------------------------------------------------------------------
  152. void GraphWidget::render_graph(const QString& filename, const QString& layout) {
  153. if(FILE *const fp = fopen(qPrintable(filename), "r")) {
  154. if(GVC_t *gvc = gvContext()) {
  155. if(graph_t *graph = agread(fp)) {
  156. render_graph(gvc, graph, layout);
  157. agclose(graph);
  158. } else {
  159. qCritical("agread() failed");
  160. }
  161. gvFreeContext(gvc);
  162. } else {
  163. qCritical("gvContext() failed");
  164. }
  165. fclose(fp);
  166. } else {
  167. qCritical("failed to open %s", qPrintable(filename));
  168. }
  169. }
  170. //------------------------------------------------------------------------------
  171. // Name: render_sub_graph(GVC_t *gvc, graph_t *graph)
  172. // Desc:
  173. //------------------------------------------------------------------------------
  174. void GraphWidget::render_sub_graph(GVC_t *gvc, graph_t *graph) {
  175. if(graph) {
  176. for(int i = 1; i <= graph->u.n_cluster; ++i) {
  177. graph_t * sub_graph = graph->u.clust[i];
  178. render_sub_graph(gvc, sub_graph);
  179. }
  180. drawGraphLabel(graph);
  181. }
  182. const QRectF subGraphRect(
  183. gToQ(GD_bb(graph).LL),
  184. gToQ(GD_bb(graph).UR)
  185. );
  186. QPen graphPen(aggetToQColor(graph, "color", Qt::white));
  187. const QString style = QString::fromUtf8(agget(graph, const_cast<char *>("style")));
  188. const QBrush graphBrush(
  189. style.contains("filled") ? aggetToQColor(graph, "color", Qt::white) : QBrush()
  190. );
  191. QGraphicsRectItem *item = scene_->addRect(subGraphRect, graphPen, graphBrush);
  192. item->setZValue(INT_MIN);
  193. }
  194. //------------------------------------------------------------------------------
  195. // Name: render_edge(edge_t *edge)
  196. // Desc:
  197. //------------------------------------------------------------------------------
  198. void GraphWidget::render_edge(edge_t *edge) {
  199. const splines* spl = ED_spl(edge);
  200. if(spl == 0)
  201. return;
  202. for (int i = 0; i < spl->size; ++i) {
  203. const bezier& bz = spl->list[i];
  204. const QColor color(aggetToQColor(edge, "color", Qt::black));
  205. GraphEdge *const item = new GraphEdge(this, bz, color);
  206. QPen pen(color);
  207. pen.setStyle(aggetToQPenStyle(edge, "style", Qt::SolidLine));
  208. pen.setWidthF(1.0);
  209. item->setPen(pen);
  210. item->setZValue(-1.0);
  211. scene_->addItem(item);
  212. }
  213. }
  214. //------------------------------------------------------------------------------
  215. // Name: render_node(graph_t *graph, node_t *node)
  216. // Desc:
  217. //------------------------------------------------------------------------------
  218. void GraphWidget::render_node(graph_t *graph, node_t *node) {
  219. const QString node_style = QString::fromUtf8(agget(node, const_cast<char *>("style")));
  220. const QStringList node_styles = node_style.split(",");
  221. if(!node_styles.contains("invisible")) {
  222. GraphNode* item = new GraphNode(this, node);
  223. item->setZValue(1.0);
  224. #ifdef ND_coord_i
  225. item->setPos(gToQ(ND_coord_i(node)));
  226. #else
  227. item->setPos(gToQ(ND_coord(node)));
  228. #endif
  229. QPen pen(aggetToQColor(node, "color", Qt::black));
  230. pen.setWidthF(1.0);
  231. item->setPen(pen);
  232. if(node_styles.contains("filled")) {
  233. QString fill_color = QString::fromUtf8(agget(node, const_cast<char *>("fillcolor")));
  234. if(fill_color.isEmpty()) {
  235. fill_color = QString::fromUtf8(agget(node, const_cast<char *>("color")));
  236. }
  237. QColor color(fill_color);
  238. if(color.isValid()) {
  239. item->setBrush(QBrush(color));
  240. } else {
  241. item->setBrush(QBrush(Qt::gray));
  242. }
  243. }
  244. QString tooltip = QString::fromUtf8(agget(node, const_cast<char *>("tooltip")));
  245. if(!tooltip.isEmpty()) {
  246. tooltip.replace("\\n", "\n");
  247. item->setToolTip(tooltip);
  248. }
  249. scene_->addItem(item);
  250. for(edge_t *e = agfstout(graph, node); e != 0; e = agnxtout(graph, e)) {
  251. render_edge(e);
  252. }
  253. }
  254. }
  255. //------------------------------------------------------------------------------
  256. // Name: render_graph(GVC_t *gvc, graph_t *graph, const QString& layout)
  257. // Desc:
  258. //------------------------------------------------------------------------------
  259. void GraphWidget::render_graph(GVC_t *gvc, graph_t *graph, const QString& layout) {
  260. if(gvLayout(gvc, graph, const_cast<char *>(qPrintable(layout))) == 0) {
  261. clear_graph();
  262. if(GD_charset(graph) != 0) {
  263. qWarning("unsupported charset");
  264. }
  265. // don't use gToQ here since it adjusts the values
  266. graph_rect_ = QRectF(
  267. GD_bb(graph).LL.x,
  268. GD_bb(graph).LL.y,
  269. GD_bb(graph).UR.x,
  270. GD_bb(graph).UR.y);
  271. scene_->setSceneRect(graph_rect_.adjusted(-5, -5, +5, +5));
  272. scene_->setBackgroundBrush(aggetToQColor(graph, "bgcolor", Qt::white));
  273. render_sub_graph(gvc, graph);
  274. for(node_t *n = agfstnode(graph); n != 0; n = agnxtnode(graph, n)) {
  275. render_node(graph, n);
  276. }
  277. } else {
  278. qCritical("gvLayout() failed");
  279. }
  280. }
  281. //------------------------------------------------------------------------------
  282. // Name: drawGraphLabel(graph_t *graph)
  283. // Desc:
  284. //------------------------------------------------------------------------------
  285. void GraphWidget::drawGraphLabel(graph_t *graph) {
  286. const textlabel_t *const textlabel = GD_label(graph);
  287. if(textlabel) {
  288. #if 0
  289. // Since I always just take the points from graph and pass them to Qt
  290. // as pixel I also have to set the pixel size of the font.
  291. QFont font(textlabel->fontname, textlabel->fontsize);
  292. font.setPixelSize(textlabel->fontsize);
  293. if(!font.exactMatch()) {
  294. QFontInfo fontinfo(font);
  295. qWarning("replacing font \"%s\" by font \"%s\"", qPrintable(font.family()), qPrintable(fontinfo.family()));
  296. }
  297. QGraphicsTextItem *item = scene_->addText(QString::fromUtf8(textlabel->text), font);
  298. const QRectF subGraphRect(
  299. gToQ(GD_bb(graph).LL),
  300. gToQ(GD_bb(graph).UR)
  301. );
  302. const pointf p = textlabel->pos;
  303. QPointF pos = true ? QPointF(p.x, subGraphRect.height() - p.y) : QPointF(p.x, -p.y);
  304. item->setPos(pos);
  305. #endif
  306. }
  307. }
  308. //------------------------------------------------------------------------------
  309. // Name: clear_graph()
  310. // Desc:
  311. //------------------------------------------------------------------------------
  312. void GraphWidget::clear_graph() {
  313. qDeleteAll(scene_->items());
  314. }