25 #include <QWheelEvent>
26 #include <QGraphicsSceneMouseEvent>
31 QY2Graph::QY2Graph(
const std::string& filename,
const std::string& layoutAlgorithm, QWidget* parent)
32 : QGraphicsView(parent)
36 renderGraph(filename, layoutAlgorithm);
40 QY2Graph::QY2Graph(graph_t* graph, QWidget* parent)
41 : QGraphicsView(parent)
57 setRenderHint(QPainter::Antialiasing);
58 setRenderHint(QPainter::TextAntialiasing);
59 setDragMode(QGraphicsView::ScrollHandDrag);
60 setTransformationAnchor(AnchorUnderMouse);
61 setResizeAnchor(AnchorUnderMouse);
63 scene =
new QGraphicsScene(
this);
64 scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
70 QY2Graph::keyPressEvent(QKeyEvent* event)
82 case Qt::Key_Asterisk:
91 QGraphicsView::keyPressEvent(event);
97 QY2Graph::wheelEvent(QWheelEvent* event)
102 scaleView(pow(2.0, event->angleDelta().y() / 240.0));
108 QY2Graph::scaleView(qreal scaleFactor)
111 qreal f = sqrt(transform().determinant());
114 if (scaleFactor * f > 8.0)
115 scaleFactor = 8.0 / f;
116 if (scaleFactor * f < 0.1)
117 scaleFactor = 0.1 / f;
119 scale(scaleFactor, scaleFactor);
124 QY2Graph::contextMenuEvent(QContextMenuEvent* event)
129 emit nodeContextMenuEvent(event, node->name);
131 emit backgroundContextMenuEvent(event);
136 QY2Graph::mouseDoubleClickEvent(QMouseEvent* event)
141 emit nodeDoubleClickEvent(event, node->name);
146 QY2Graph::gToQ(
const pointf& p,
bool upside_down)
const
148 return upside_down ? QPointF(p.x, graphRect.height() - p.y) : QPointF(p.x, -p.y);
153 QY2Graph::aggetToQString(
void* obj,
const char* name,
const QString& fallback)
const
155 const char* tmp = agget(obj,
const_cast<char*
>(name));
156 if (tmp == NULL || strlen(tmp) == 0)
158 return unescape(tmp);
163 QY2Graph::aggetToQColor(
void* obj,
const char* name,
const QColor& fallback)
const
165 const char* tmp = agget(obj,
const_cast<char*
>(name));
166 if (tmp == NULL || strlen(tmp) == 0)
173 QY2Graph::aggetToQPenStyle(
void* obj,
const char* name,
const Qt::PenStyle fallback)
const
175 const char* tmp = agget(obj,
const_cast<char*
>(name));
176 if (tmp == NULL || strlen(tmp) == 0)
178 if (strcmp(tmp,
"dashed") == 0)
180 if (strcmp(tmp,
"dotted") == 0)
187 QY2Graph::makeBezier(
const bezier& bezier)
const
190 path.moveTo(gToQ(bezier.list[0]));
191 for (
int i = 1; i < bezier.size - 1; i += 3)
192 path.cubicTo(gToQ(bezier.list[i]), gToQ(bezier.list[i+1]), gToQ(bezier.list[i+2]));
198 QY2Graph::drawArrow(
const QLineF& line,
const QColor& color, QPainter* painter)
const
200 QLineF n(line.normalVector());
201 QPointF o(n.dx() / 3.0, n.dy() / 3.0);
204 polygon.append(line.p1() + o);
205 polygon.append(line.p2());
206 polygon.append(line.p1() - o);
210 painter->setPen(pen);
213 painter->setBrush(brush);
215 painter->drawPolygon(polygon);
220 QY2Graph::renderGraph(
const std::string& filename,
const std::string& layoutAlgorithm)
222 FILE* fp = fopen(filename.c_str(),
"r");
225 GVC_t* gvc = gvContext();
229 graph_t* graph = agread(fp, NULL);
231 graph_t* graph = agread(fp);
235 if (gvLayout(gvc, graph,
const_cast<char*
>(layoutAlgorithm.c_str())) == 0)
239 gvFreeLayout(gvc, graph);
243 qCritical(
"gvLayout() failed");
250 qCritical(
"agread() failed");
257 qCritical(
"gvContext() failed");
264 qCritical(
"failed to open %s", filename.c_str());
270 QY2Graph::makeShapeHelper(node_t* node)
const
272 const polygon_t* poly = (polygon_t*) ND_shape_info(node);
274 if (poly->peripheries != 1)
276 qWarning(
"unsupported number of peripheries %d", poly->peripheries);
279 const int sides = poly->sides;
280 const pointf* vertices = poly->vertices;
283 for (
int side = 0; side < sides; side++)
284 polygon.append(gToQ(vertices[side],
false));
290 QY2Graph::makeShape(node_t* node)
const
294 const char* name = ND_shape(node)->name;
296 if ((strcmp(name,
"rectangle") == 0) ||
297 (strcmp(name,
"box") == 0) ||
298 (strcmp(name,
"hexagon") == 0) ||
299 (strcmp(name,
"polygon") == 0) ||
300 (strcmp(name,
"diamond") == 0))
302 QPolygonF polygon = makeShapeHelper(node);
303 polygon.append(polygon[0]);
304 path.addPolygon(polygon);
306 else if ((strcmp(name,
"ellipse") == 0) ||
307 (strcmp(name,
"circle") == 0))
309 QPolygonF polygon = makeShapeHelper(node);
310 path.addEllipse(QRectF(polygon[0], polygon[1]));
314 qWarning(
"unsupported shape %s", name);
322 QY2Graph::drawLabel(
const textlabel_t* textlabel, QPainter* painter)
const
324 painter->setPen(textlabel->fontcolor);
328 QFont font(textlabel->fontname, textlabel->fontsize);
329 font.setPixelSize(textlabel->fontsize);
331 if (!font.exactMatch())
333 QFontInfo fontinfo(font);
334 qWarning(
"replacing font \"%s\" by font \"%s\"", font.family().toUtf8().data(),
335 fontinfo.family().toUtf8().data());
338 painter->setFont(font);
340 QString text(unescape(textlabel->text));
341 QFontMetricsF fm(painter->fontMetrics());
342 QRectF rect(fm.boundingRect(sceneRect(), Qt::AlignHCenter, text));
343 rect.moveCenter(gToQ(textlabel->pos,
false));
344 painter->drawText(rect.adjusted(-2, -2, +2, +2), Qt::AlignCenter, text);
349 QY2Graph::clearGraph()
351 QList<QGraphicsItem*> items(scene->items());
352 while (!items.isEmpty())
353 delete items.takeFirst();
358 QY2Graph::renderGraph(graph_t* graph)
362 if (GD_charset(graph) != 0)
364 qWarning(
"unsupported charset");
368 graphRect = QRectF(GD_bb(graph).LL.x, GD_bb(graph).LL.y, GD_bb(graph).UR.x, GD_bb(graph).UR.y);
369 scene->setSceneRect(graphRect.adjusted(-5, -5, +5, +5));
371 scene->setBackgroundBrush(aggetToQColor(graph,
"bgcolor", Qt::white));
373 for (node_t* node = agfstnode(graph); node != NULL; node = agnxtnode(graph, node))
376 QPainter painter(&picture);
379 drawLabel(ND_label(node), &painter);
383 QY2Node* item =
new QY2Node(makeShape(node), picture, agnameof(node));
385 QY2Node* item =
new QY2Node(makeShape(node), picture, node->name);
388 item->setPos(gToQ(ND_coord(node)));
390 QPen pen(aggetToQColor(node,
"color", Qt::black));
394 QBrush brush(aggetToQColor(node,
"fillcolor", Qt::gray));
395 item->setBrush(brush);
397 QString tooltip = aggetToQString(node,
"tooltip",
"");
398 if (!tooltip.isEmpty())
399 item->setToolTip(tooltip);
401 scene->addItem(item);
403 for (edge_t* edge = agfstout(graph, node); edge != NULL; edge = agnxtout(graph, edge))
405 const splines* spl = ED_spl(edge);
409 for (
int i = 0; i < spl->size; ++i)
411 const bezier& bz = spl->list[i];
413 QColor color(aggetToQColor(edge,
"color", Qt::black));
415 QPainterPath path(makeBezier(bz));
420 painter.begin(&picture);
422 drawArrow(QLineF(gToQ(bz.list[0]), gToQ(bz.sp)), color, &painter);
424 drawArrow(QLineF(gToQ(bz.list[bz.size-1]), gToQ(bz.ep)), color, &painter);
430 pen.setStyle(aggetToQPenStyle(edge,
"style", Qt::SolidLine));
434 item->setZValue(-1.0);
436 scene->addItem(item);
444 QY2Graph::unescape(
const std::string& s)
const
448 bool backslashed =
false;
450 for (
const char c : s)
485 return QString::fromUtf8(r.c_str());
489 QY2Node::QY2Node(
const QPainterPath& path,
const QPicture& picture,
const QString& name)
490 : QGraphicsPathItem(path),
498 QY2Node::paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget)
501 QGraphicsPathItem::paint(painter, option, widget);
504 picture.play(painter);
508 QY2Edge::QY2Edge(
const QPainterPath& path,
const QPicture& picture)
509 : QGraphicsPathItem(path),
516 QY2Edge::boundingRect()
const
518 return QGraphicsPathItem::boundingRect().united(picture.boundingRect());
523 QY2Edge::paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget)
526 QGraphicsPathItem::paint(painter, option, widget);
529 picture.play(painter);